about summary refs log tree commit diff
path: root/app/models
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2023-03-31 21:30:27 +0200
committerClaire <claire.github-309c@sitedethib.com>2023-03-31 21:30:27 +0200
commit01d6f7529faef97c0209ef11bbca2e856961bbab (patch)
tree513ac21302befa1a08fc4968dcd4dca6b0e06360 /app/models
parentcbdb25ab0343603165fc89fd28b07c9ca0f2ae6d (diff)
parentc6c03b49b255c4fe2183b94288a712ad7a66e2c2 (diff)
Merge branch 'main' into glitch-soc/merge-upstream
Conflicts:
- `README.md`:
  Upstream added a link to the roadmap, but we have a completely different README.
  Kept ours.
- `app/models/media_attachment.rb`:
  Upstream upped media attachment limits.
  Updated the default according to upstream's.
- `db/migrate/20180831171112_create_bookmarks.rb`:
  Upstream changed the migration compatibility level.
  Did so too.
- `config/initializers/content_security_policy.rb`:
  Upstream refactored this file but we have a different version.
  Kept our version.
- `app/controllers/settings/preferences_controller.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  The file does not directly references individual settings anymore.
  Applied upstream changes.
- `app/lib/user_settings_decorator.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  The file got removed entirely.
  Removed it as well.
- `app/models/user.rb`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  References to individual settings have been removed from the file.
  Removed them as well.
- `app/views/settings/preferences/appearance/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `app/views/settings/preferences/notifications/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `app/views/settings/preferences/other/show.html.haml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  Applied upstream's changes and ported ours back.
- `config/settings.yml`:
  Upstream completely refactored user settings storage, and glitch-soc has a
  different set of settings.
  In particular, upstream removed user-specific and unused settings.
  Did the same in glitch-soc.
- `spec/controllers/application_controller_spec.rb`:
  Conflicts due to glitch-soc's theming system.
  Mostly kept our version, as upstream messed up the tests.
Diffstat (limited to 'app/models')
-rw-r--r--app/models/account.rb2
-rw-r--r--app/models/account_stat.rb2
-rw-r--r--app/models/admin/action_log.rb2
-rw-r--r--app/models/backup.rb2
-rw-r--r--app/models/concerns/attachmentable.rb2
-rw-r--r--app/models/concerns/has_user_settings.rb141
-rw-r--r--app/models/custom_filter.rb2
-rw-r--r--app/models/email_domain_block.rb2
-rw-r--r--app/models/media_attachment.rb10
-rw-r--r--app/models/preview_card.rb4
-rw-r--r--app/models/report.rb2
-rw-r--r--app/models/status_edit.rb2
-rw-r--r--app/models/user.rb67
-rw-r--r--app/models/user_settings.rb99
-rw-r--r--app/models/user_settings/dsl.rb37
-rw-r--r--app/models/user_settings/glue.rb23
-rw-r--r--app/models/user_settings/namespace.rb21
-rw-r--r--app/models/user_settings/setting.rb49
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