diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account.rb | 2 | ||||
-rw-r--r-- | app/models/account_stat.rb | 2 | ||||
-rw-r--r-- | app/models/admin/action_log.rb | 2 | ||||
-rw-r--r-- | app/models/backup.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/attachmentable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/has_user_settings.rb | 141 | ||||
-rw-r--r-- | app/models/custom_filter.rb | 2 | ||||
-rw-r--r-- | app/models/email_domain_block.rb | 2 | ||||
-rw-r--r-- | app/models/media_attachment.rb | 10 | ||||
-rw-r--r-- | app/models/preview_card.rb | 4 | ||||
-rw-r--r-- | app/models/report.rb | 2 | ||||
-rw-r--r-- | app/models/status_edit.rb | 2 | ||||
-rw-r--r-- | app/models/user.rb | 67 | ||||
-rw-r--r-- | app/models/user_settings.rb | 99 | ||||
-rw-r--r-- | app/models/user_settings/dsl.rb | 37 | ||||
-rw-r--r-- | app/models/user_settings/glue.rb | 23 | ||||
-rw-r--r-- | app/models/user_settings/namespace.rb | 21 | ||||
-rw-r--r-- | app/models/user_settings/setting.rb | 49 |
18 files changed, 391 insertions, 78 deletions
diff --git a/app/models/account.rb b/app/models/account.rb index f2ed871e7..4fc7b9d08 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -53,7 +53,7 @@ # class Account < ApplicationRecord - self.ignored_columns = %w( + self.ignored_columns += %w( subscription_expires_at secret remote_url diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb index 834f8ba4c..0fea7732e 100644 --- a/app/models/account_stat.rb +++ b/app/models/account_stat.rb @@ -16,7 +16,7 @@ class AccountStat < ApplicationRecord self.locking_column = nil - self.ignored_columns = %w(lock_version) + self.ignored_columns += %w(lock_version) belongs_to :account, inverse_of: :account_stat diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb index 4fa8008f5..f2c121d75 100644 --- a/app/models/admin/action_log.rb +++ b/app/models/admin/action_log.rb @@ -17,7 +17,7 @@ # class Admin::ActionLog < ApplicationRecord - self.ignored_columns = %w( + self.ignored_columns += %w( recorded_changes ) diff --git a/app/models/backup.rb b/app/models/backup.rb index dca06eb58..5feb31d7d 100644 --- a/app/models/backup.rb +++ b/app/models/backup.rb @@ -18,6 +18,6 @@ class Backup < ApplicationRecord belongs_to :user, inverse_of: :backups - has_attached_file :dump, s3_permissions: 'private' + has_attached_file :dump, s3_permissions: ->(*) { ENV['S3_PERMISSION'] == '' ? nil : 'private' } validates_attachment_content_type :dump, content_type: /\Aapplication/ end diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb index 01fae4236..d44c22438 100644 --- a/app/models/concerns/attachmentable.rb +++ b/app/models/concerns/attachmentable.rb @@ -5,7 +5,7 @@ require 'mime/types/columnar' module Attachmentable extend ActiveSupport::Concern - MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB + MAX_MATRIX_LIMIT = 33_177_600 # 7680x4320px or approx. 847MB in RAM GIF_MATRIX_LIMIT = 921_600 # 1280x720px # For some file extensions, there exist different content diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/has_user_settings.rb new file mode 100644 index 000000000..b3fa1f683 --- /dev/null +++ b/app/models/concerns/has_user_settings.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module HasUserSettings + extend ActiveSupport::Concern + + included do + serialize :settings, UserSettingsSerializer + end + + def settings_attributes=(attributes) + settings.update(attributes) + end + + def prefers_noindex? + settings['noindex'] + end + + def preferred_posting_language + valid_locale_cascade(settings['default_language'], locale, I18n.locale) + end + + def setting_auto_play_gif + settings['web.auto_play'] + end + + def setting_default_sensitive + settings['default_sensitive'] + end + + def setting_unfollow_modal + settings['web.unfollow_modal'] + end + + def setting_boost_modal + settings['web.reblog_modal'] + end + + def setting_delete_modal + settings['web.delete_modal'] + end + + def setting_reduce_motion + settings['web.reduce_motion'] + end + + def setting_system_font_ui + settings['web.use_system_font'] + end + + def setting_noindex + settings['noindex'] + end + + def setting_theme + settings['theme'] + end + + def setting_display_media + settings['web.display_media'] + end + + def setting_expand_spoilers + settings['web.expand_content_warnings'] + end + + def setting_default_language + settings['default_language'] + end + + def setting_aggregate_reblogs + settings['aggregate_reblogs'] + end + + def setting_show_application + settings['show_application'] + end + + def setting_advanced_layout + settings['web.advanced_layout'] + end + + def setting_use_blurhash + settings['web.use_blurhash'] + end + + def setting_use_pending_items + settings['web.use_pending_items'] + end + + def setting_trends + settings['web.trends'] + end + + def setting_crop_images + settings['web.crop_images'] + end + + def setting_disable_swiping + settings['web.disable_swiping'] + end + + def setting_always_send_emails + settings['always_send_emails'] + end + + def setting_default_privacy + settings['default_privacy'] || (account.locked? ? 'private' : 'public') + end + + def allows_report_emails? + settings['notification_emails.report'] + end + + def allows_pending_account_emails? + settings['notification_emails.pending_account'] + end + + def allows_appeal_emails? + settings['notification_emails.appeal'] + end + + def allows_trends_review_emails? + settings['notification_emails.trends'] + end + + def aggregates_reblogs? + settings['aggregate_reblogs'] + end + + def shows_application? + settings['show_application'] + end + + def show_all_media? + settings['web.display_media'] == 'show_all' + end + + def hide_all_media? + settings['web.display_media'] == 'hide_all' + end +end diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb index d85e196e9..0f4fd78cb 100644 --- a/app/models/custom_filter.rb +++ b/app/models/custom_filter.rb @@ -15,7 +15,7 @@ # class CustomFilter < ApplicationRecord - self.ignored_columns = %w(whole_word irreversible) + self.ignored_columns += %w(whole_word irreversible) alias_attribute :title, :phrase alias_attribute :filter_action, :action diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb index 276e7d31a..3c9be51ca 100644 --- a/app/models/email_domain_block.rb +++ b/app/models/email_domain_block.rb @@ -12,7 +12,7 @@ # class EmailDomainBlock < ApplicationRecord - self.ignored_columns = %w( + self.ignored_columns += %w( ips last_refresh_at ) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 62c9828c6..0367b4af7 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -39,11 +39,11 @@ class MediaAttachment < ApplicationRecord MAX_DESCRIPTION_LENGTH = 1_500 - IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i - VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i + IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 16.megabytes).to_i + VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 99.megabytes).to_i - MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px - MAX_VIDEO_FRAME_RATE = 60 + MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px + MAX_VIDEO_FRAME_RATE = 120 IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heic .heif .avif).freeze VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze @@ -69,7 +69,7 @@ class MediaAttachment < ApplicationRecord IMAGE_STYLES = { original: { - pixels: 2_073_600, # 1920x1080px + pixels: 8_294_400, # 3840x2160px file_geometry_parser: FastGeometryParser, }.freeze, diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index 6bce16562..a738940be 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -36,7 +36,7 @@ class PreviewCard < ApplicationRecord include Attachmentable IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze - LIMIT = 1.megabytes + LIMIT = 2.megabytes BLURHASH_OPTIONS = { x_comp: 4, @@ -121,7 +121,7 @@ class PreviewCard < ApplicationRecord def image_styles(file) styles = { original: { - geometry: '400x400>', + pixels: 230_400, # 640x360px file_geometry_parser: FastGeometryParser, convert_options: '-coalesce', blurhash: BLURHASH_OPTIONS, diff --git a/app/models/report.rb b/app/models/report.rb index a9940459d..c3a0c4c8b 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -21,7 +21,7 @@ # class Report < ApplicationRecord - self.ignored_columns = %w(action_taken) + self.ignored_columns += %w(action_taken) include Paginable include RateLimitable diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 653a04252..fa35e38ac 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -21,7 +21,7 @@ class StatusEdit < ApplicationRecord include RateLimitable - self.ignored_columns = %w( + self.ignored_columns += %w( media_attachments_changed ) diff --git a/app/models/user.rb b/app/models/user.rb index 744d66c8f..3471bb2c1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -39,10 +39,11 @@ # webauthn_id :string # sign_up_ip :inet # role_id :bigint(8) +# settings :text # class User < ApplicationRecord - self.ignored_columns = %w( + self.ignored_columns += %w( remember_created_at remember_token current_sign_in_ip @@ -51,9 +52,9 @@ class User < ApplicationRecord filtered_languages ) - include Settings::Extend include Redisable include LanguagesHelper + include HasUserSettings # The home and list feeds will be stored in Redis for this amount # of time, and status fan-out to followers will include only people @@ -132,13 +133,6 @@ class User < ApplicationRecord has_many :session_activations, dependent: :destroy - delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal, - :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_followers_count, - :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, - :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images, - :disable_swiping, :always_send_emails, :default_content_type, :system_emoji_font, - to: :settings, prefix: :setting, allow_nil: false - delegate :can?, to: :role attr_reader :invite_code @@ -303,50 +297,6 @@ class User < ApplicationRecord save! end - def prefers_noindex? - setting_noindex - end - - def preferred_posting_language - valid_locale_cascade(settings.default_language, locale, I18n.locale) - end - - def setting_default_privacy - settings.default_privacy || (account.locked? ? 'private' : 'public') - end - - def allows_report_emails? - settings.notification_emails['report'] - end - - def allows_pending_account_emails? - settings.notification_emails['pending_account'] - end - - def allows_appeal_emails? - settings.notification_emails['appeal'] - end - - def allows_trending_tags_review_emails? - settings.notification_emails['trending_tag'] - end - - def allows_trending_links_review_emails? - settings.notification_emails['trending_link'] - end - - def allows_trending_statuses_review_emails? - settings.notification_emails['trending_status'] - end - - def aggregates_reblogs? - @aggregates_reblogs ||= settings.aggregate_reblogs - end - - def shows_application? - @shows_application ||= settings.show_application - end - def token_for_app(app) return nil if app.nil? || app.owner != self @@ -426,14 +376,6 @@ class User < ApplicationRecord send_reset_password_instructions end - def show_all_media? - setting_display_media == 'show_all' - end - - def hide_all_media? - setting_display_media == 'hide_all' - end - protected def send_devise_notification(notification, *args, **kwargs) @@ -503,7 +445,8 @@ class User < ApplicationRecord def sanitize_languages return if chosen_languages.nil? - chosen_languages.reject!(&:blank?) + chosen_languages.compact_blank! + self.chosen_languages = nil if chosen_languages.empty? end diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb new file mode 100644 index 000000000..2c025d6c5 --- /dev/null +++ b/app/models/user_settings.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +class UserSettings + class Error < StandardError; end + class KeyError < Error; end + + include UserSettings::DSL + include UserSettings::Glue + + setting :always_send_emails, default: false + setting :aggregate_reblogs, default: true + setting :theme, default: -> { ::Setting.theme } + setting :noindex, default: -> { ::Setting.noindex } + setting :show_application, default: true + setting :default_language, default: nil + setting :default_sensitive, default: false + setting :default_privacy, default: nil + + namespace :web do + setting :crop_images, default: true + setting :advanced_layout, default: false + setting :trends, default: true + setting :use_blurhash, default: true + setting :use_pending_items, default: false + setting :use_system_font, default: false + setting :disable_swiping, default: false + setting :delete_modal, default: true + setting :reblog_modal, default: false + setting :unfollow_modal, default: true + setting :reduce_motion, default: false + setting :expand_content_warnings, default: false + setting :display_media, default: 'default', in: %w(default show_all hide_all) + setting :auto_play, default: false + end + + namespace :notification_emails do + setting :follow, default: true + setting :reblog, default: false + setting :favourite, default: false + setting :mention, default: true + setting :follow_request, default: true + setting :report, default: true + setting :pending_account, default: true + setting :trends, default: true + setting :appeal, default: true + end + + namespace :interactions do + setting :must_be_follower, default: false + setting :must_be_following, default: false + setting :must_be_following_dm, default: false + end + + def initialize(original_hash) + @original_hash = original_hash || {} + end + + def [](key) + key = key.to_sym + + raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key) + + if @original_hash.key?(key) + @original_hash[key] + else + self.class.definition_for(key).default_value + end + end + + def []=(key, value) + key = key.to_sym + + raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key) + + typecast_value = self.class.definition_for(key).type_cast(value) + + if typecast_value.nil? + @original_hash.delete(key) + else + @original_hash[key] = typecast_value + end + end + + def update(params) + params.each do |k, v| + self[k] = v unless v.nil? + end + end + + keys.each do |key| + define_method(key) do + self[key] + end + end + + def as_json + @original_hash + end +end diff --git a/app/models/user_settings/dsl.rb b/app/models/user_settings/dsl.rb new file mode 100644 index 000000000..26238bbbe --- /dev/null +++ b/app/models/user_settings/dsl.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module UserSettings::DSL + module ClassMethods + def setting(key, options = {}) + @definitions ||= {} + + UserSettings::Setting.new(key, options).tap do |s| + @definitions[s.key] = s + end + end + + def namespace(key, &block) + @definitions ||= {} + + UserSettings::Namespace.new(key).configure(&block).tap do |n| + @definitions.merge!(n.definitions) + end + end + + def keys + @definitions.keys + end + + def definition_for(key) + @definitions[key.to_sym] + end + + def definition_for?(key) + @definitions.key?(key.to_sym) + end + end + + def self.included(base) + base.extend ClassMethods + end +end diff --git a/app/models/user_settings/glue.rb b/app/models/user_settings/glue.rb new file mode 100644 index 000000000..02066a411 --- /dev/null +++ b/app/models/user_settings/glue.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module UserSettings::Glue + def to_model + self + end + + def to_key + '' + end + + def persisted? + false + end + + def type_for_attribute(key) + self.class.definition_for(key)&.type + end + + def has_attribute?(key) # rubocop:disable Naming/PredicateName + self.class.definition_for?(key) + end +end diff --git a/app/models/user_settings/namespace.rb b/app/models/user_settings/namespace.rb new file mode 100644 index 000000000..b8f7e092e --- /dev/null +++ b/app/models/user_settings/namespace.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class UserSettings::Namespace + attr_reader :name, :definitions + + def initialize(name) + @name = name.to_sym + @definitions = {} + end + + def configure(&block) + instance_eval(&block) + self + end + + def setting(key, options = {}) + UserSettings::Setting.new(key, options.merge(namespace: name)).tap do |s| + @definitions[s.key] = s + end + end +end diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb new file mode 100644 index 000000000..5f5504254 --- /dev/null +++ b/app/models/user_settings/setting.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class UserSettings::Setting + attr_reader :name, :namespace, :in + + def initialize(name, options = {}) + @name = name.to_sym + @default_value = options[:default] + @namespace = options[:namespace] + @in = options[:in] + end + + def default_value + if @default_value.respond_to?(:call) + @default_value.call + else + @default_value + end + end + + def type + case default_value + when TrueClass, FalseClass + ActiveModel::Type::Boolean.new + else + ActiveModel::Type::String.new + end + end + + def type_cast(value) + if type.respond_to?(:cast) + type.cast(value) + else + value + end + end + + def to_a + [key, default_value] + end + + def key + if namespace + "#{namespace}.#{name}".to_sym + else + name + end + end +end |