diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account.rb | 16 | ||||
-rw-r--r-- | app/models/bookmark.rb | 26 | ||||
-rw-r--r-- | app/models/concerns/account_associations.rb | 1 | ||||
-rw-r--r-- | app/models/concerns/account_interactions.rb | 4 | ||||
-rw-r--r-- | app/models/form/admin_settings.rb | 20 | ||||
-rw-r--r-- | app/models/list.rb | 13 | ||||
-rw-r--r-- | app/models/media_attachment.rb | 28 | ||||
-rw-r--r-- | app/models/mute.rb | 2 | ||||
-rw-r--r-- | app/models/status.rb | 30 | ||||
-rw-r--r-- | app/models/stream_entry.rb | 2 | ||||
-rw-r--r-- | app/models/user.rb | 4 |
11 files changed, 124 insertions, 22 deletions
diff --git a/app/models/account.rb b/app/models/account.rb index 51e01246e..a82251d2e 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -62,6 +62,10 @@ class Account < ApplicationRecord include AccountCounters include DomainNormalizable + MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i + MAX_NOTE_LENGTH = (ENV['MAX_BIO_CHARS'] || 500).to_i + MAX_FIELDS = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i + enum protocol: [:ostatus, :activitypub] validates :username, presence: true @@ -74,9 +78,9 @@ class Account < ApplicationRecord validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? } validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } - validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } - validates :note, note_length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? } - validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? } + validates :display_name, length: { maximum: MAX_DISPLAY_NAME_LENGTH }, if: -> { local? && will_save_change_to_display_name? } + validates :note, note_length: { maximum: MAX_NOTE_LENGTH }, if: -> { local? && will_save_change_to_note? } + validates :fields, length: { maximum: MAX_FIELDS }, if: -> { local? && will_save_change_to_fields? } scope :remote, -> { where.not(domain: nil) } scope :local, -> { where(domain: nil) } @@ -262,15 +266,13 @@ class Account < ApplicationRecord self[:fields] = fields end - DEFAULT_FIELDS_SIZE = 4 - def build_fields - return if fields.size >= DEFAULT_FIELDS_SIZE + return if fields.size >= MAX_FIELDS tmp = self[:fields] || [] tmp = [] if tmp.is_a?(Hash) - (DEFAULT_FIELDS_SIZE - tmp.size).times do + (MAX_FIELDS - tmp.size).times do tmp << { name: '', value: '' } end diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb new file mode 100644 index 000000000..916261a17 --- /dev/null +++ b/app/models/bookmark.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: bookmarks +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# status_id :bigint(8) not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class Bookmark < ApplicationRecord + include Paginable + + update_index('statuses#status', :status) if Chewy.enabled? + + belongs_to :account, inverse_of: :bookmarks + belongs_to :status, inverse_of: :bookmarks + + validates :status_id, uniqueness: { scope: :account_id } + + before_validation do + self.status = status.reblog if status&.reblog? + end +end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index 70855e054..ecccaf35e 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -14,6 +14,7 @@ module AccountAssociations has_many :stream_entries, inverse_of: :account, dependent: :destroy has_many :statuses, inverse_of: :account, dependent: :destroy has_many :favourites, inverse_of: :account, dependent: :destroy + has_many :bookmarks, inverse_of: :account, dependent: :destroy has_many :mentions, inverse_of: :account, dependent: :destroy has_many :notifications, inverse_of: :account, dependent: :destroy has_many :conversations, class_name: 'AccountConversation', dependent: :destroy, inverse_of: :account diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb index ad2909d91..f27d39483 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account_interactions.rb @@ -186,6 +186,10 @@ module AccountInteractions status.proper.favourites.where(account: self).exists? end + def bookmarked?(status) + status.proper.bookmarks.where(account: self).exists? + end + def reblogged?(status) status.proper.reblogs.where(account: self).exists? end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 86a86ec66..83d303c33 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -17,7 +17,8 @@ class Form::AdminSettings timeline_preview show_staff_badge bootstrap_timeline_accounts - theme + flavour + skin min_invite_role activity_api_enabled peers_api_enabled @@ -25,6 +26,8 @@ class Form::AdminSettings preview_sensitive_media custom_css profile_directory + hide_followers_count + flavour_and_skin thumbnail hero mascot @@ -39,6 +42,7 @@ class Form::AdminSettings show_known_fediverse_at_about_page preview_sensitive_media profile_directory + hide_followers_count ).freeze UPLOAD_KEYS = %i( @@ -47,6 +51,10 @@ class Form::AdminSettings mascot ).freeze + PSEUDO_KEYS = %i( + flavour_and_skin + ).freeze + attr_accessor(*KEYS) validates :site_short_description, :site_description, html: { wrap_with: :p } @@ -66,6 +74,7 @@ class Form::AdminSettings return false unless valid? KEYS.each do |key| + next if PSEUDO_KEYS.include?(key) value = instance_variable_get("@#{key}") if UPLOAD_KEYS.include?(key) && !value.nil? @@ -78,10 +87,19 @@ class Form::AdminSettings end end + def flavour_and_skin + "#{Setting.flavour}/#{Setting.skin}" + end + + def flavour_and_skin=(value) + @flavour, @skin = value.split('/', 2) + end + private def initialize_attributes KEYS.each do |key| + next if PSEUDO_KEYS.include?(key) instance_variable_set("@#{key}", Setting.public_send(key)) if instance_variable_get("@#{key}").nil? end end diff --git a/app/models/list.rb b/app/models/list.rb index c9c94fca1..8493046e5 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -3,11 +3,12 @@ # # Table name: lists # -# id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# title :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# title :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# replies_policy :integer default("list_replies"), not null # class List < ApplicationRecord @@ -15,6 +16,8 @@ class List < ApplicationRecord PER_ACCOUNT_LIMIT = 50 + enum replies_policy: [:list_replies, :all_replies, :no_replies], _prefix: :show + belongs_to :account, optional: true has_many :list_accounts, inverse_of: :list, dependent: :destroy diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index a57ba0b2e..81397a18e 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -23,14 +23,16 @@ class MediaAttachment < ApplicationRecord self.inheritance_column = nil - enum type: [:image, :gifv, :video, :unknown] + enum type: [:image, :gifv, :video, :audio, :unknown] IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].freeze VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v', '.mov'].freeze + AUDIO_FILE_EXTENSIONS = ['.mp3', '.m4a', '.wav', '.ogg'].freeze IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime'].freeze VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm', 'video/quicktime'].freeze + AUDIO_MIME_TYPES = ['audio/mpeg', 'audio/mp4', 'audio/vnd.wav', 'audio/wav', 'audio/x-wav', 'audio/x-wave', 'audio/ogg',].freeze IMAGE_STYLES = { original: { @@ -44,6 +46,22 @@ class MediaAttachment < ApplicationRecord }, }.freeze + AUDIO_STYLES = { + original: { + format: 'mp4', + convert_options: { + output: { + filter_complex: '"[0:a]compand,showwaves=s=640x360:mode=line,format=yuv420p[v]"', + map: '"[v]" -map 0:a', + threads: 2, + vcodec: 'libx264', + acodec: 'aac', + movflags: '+faststart', + }, + }, + }, + }.freeze + VIDEO_STYLES = { small: { convert_options: { @@ -86,7 +104,7 @@ class MediaAttachment < ApplicationRecord processors: ->(f) { file_processors f }, convert_options: { all: '-quality 90 -strip' } - validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :video_or_gifv? validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :video_or_gifv? remotable_attachment :file, VIDEO_LIMIT @@ -154,6 +172,8 @@ class MediaAttachment < ApplicationRecord } elsif IMAGE_MIME_TYPES.include? f.instance.file_content_type IMAGE_STYLES + elsif AUDIO_MIME_TYPES.include? f.instance.file_content_type + AUDIO_STYLES elsif VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type) { small: VIDEO_STYLES[:small], @@ -169,6 +189,8 @@ class MediaAttachment < ApplicationRecord [:gif_transcoder] elsif VIDEO_MIME_TYPES.include? f.file_content_type [:video_transcoder] + elsif AUDIO_MIME_TYPES.include? f.file_content_type + [:audio_transcoder] else [:lazy_thumbnail] end @@ -193,7 +215,7 @@ class MediaAttachment < ApplicationRecord end def set_type_and_extension - self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : :image + self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : AUDIO_MIME_TYPES.include?(file_content_type) ? :audio : :image end def set_meta diff --git a/app/models/mute.rb b/app/models/mute.rb index 0e00c2278..639120f7d 100644 --- a/app/models/mute.rb +++ b/app/models/mute.rb @@ -6,9 +6,9 @@ # id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null +# hide_notifications :boolean default(TRUE), not null # account_id :bigint(8) not null # target_account_id :bigint(8) not null -# hide_notifications :boolean default(TRUE), not null # class Mute < ApplicationRecord diff --git a/app/models/status.rb b/app/models/status.rb index 8d31fd382..e7fa0220b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -21,6 +21,8 @@ # account_id :bigint(8) not null # application_id :bigint(8) # in_reply_to_account_id :bigint(8) +# local_only :boolean +# full_status_text :text default(""), not null # poll_id :bigint(8) # @@ -51,6 +53,7 @@ class Status < ApplicationRecord belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true has_many :favourites, inverse_of: :status, dependent: :destroy + has_many :bookmarks, inverse_of: :status, dependent: :destroy has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread has_many :mentions, dependent: :destroy, inverse_of: :status @@ -100,6 +103,8 @@ class Status < ApplicationRecord end } + scope :not_local_only, -> { where(local_only: [false, nil]) } + cache_associated :application, :media_attachments, :conversation, @@ -257,6 +262,8 @@ class Status < ApplicationRecord around_create Mastodon::Snowflake::Callbacks + before_create :set_locality + before_validation :prepare_contents, if: :local? before_validation :set_reblog before_validation :set_visibility @@ -339,6 +346,10 @@ class Status < ApplicationRecord Favourite.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |f, h| h[f.status_id] = true } end + def bookmarks_map(status_ids, account_id) + Bookmark.select('status_id').where(status_id: status_ids).where(account_id: account_id).map { |f| [f.status_id, true] }.to_h + end + def reblogs_map(status_ids, account_id) select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).reorder(nil).each_with_object({}) { |s, h| h[s.reblog_of_id] = true } end @@ -375,7 +386,7 @@ class Status < ApplicationRecord visibility = [:public, :unlisted] if account.nil? - where(visibility: visibility) + where(visibility: visibility).not_local_only elsif target_account.blocking?(account) # get rid of blocked peeps none elsif account.id == target_account.id # author can see own stuff @@ -418,7 +429,7 @@ class Status < ApplicationRecord end def filter_timeline_default(query) - query.excluding_silenced_accounts + query.not_local_only.excluding_silenced_accounts end def account_silencing_filter(account) @@ -431,6 +442,15 @@ class Status < ApplicationRecord end end + def marked_local_only? + # match both with and without U+FE0F (the emoji variation selector) + /#{local_only_emoji}\ufe0f?\z/.match?(content) + end + + def local_only_emoji + '👁' + end + private def update_status_stat!(attrs) @@ -463,6 +483,12 @@ class Status < ApplicationRecord self.sensitive = false if sensitive.nil? end + def set_locality + if account.domain.nil? && !attribute_changed?(:local_only) + self.local_only = marked_local_only? + end + end + def set_conversation self.thread = thread.reblog if thread&.reblog? diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb index 1a9afc5c7..edd30487e 100644 --- a/app/models/stream_entry.rb +++ b/app/models/stream_entry.rb @@ -27,7 +27,7 @@ class StreamEntry < ApplicationRecord scope :recent, -> { reorder(id: :desc) } scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) } - delegate :target, :title, :content, :thread, + delegate :target, :title, :content, :thread, :local_only?, to: :status, allow_nil: true diff --git a/app/models/user.rb b/app/models/user.rb index 135baae12..b2fb820af 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -102,8 +102,8 @@ class User < ApplicationRecord has_many :session_activations, dependent: :destroy - delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, - :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network, + 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_network, :hide_followers_count, :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, to: :settings, prefix: :setting, allow_nil: false attr_reader :invite_code |