From bd53dd521064b12261b82105624cf5f8b9ca9d69 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Mar 2022 08:52:32 +0100 Subject: Change design of federation pages in admin UI (#17704) * Change design of federation pages in admin UI * Fix query performance in instance media attachments measure * Fix reblogs being included in instance languages dimension --- app/views/admin/domain_blocks/edit.html.haml | 4 +- app/views/admin/domain_blocks/new.html.haml | 4 +- app/views/admin/domain_blocks/show.html.haml | 25 --- .../instances/_exhausted_deliveries_days.haml | 2 - app/views/admin/instances/_instance.html.haml | 28 +--- app/views/admin/instances/index.html.haml | 19 +-- app/views/admin/instances/show.html.haml | 169 +++++++++++---------- 7 files changed, 101 insertions(+), 150 deletions(-) delete mode 100644 app/views/admin/domain_blocks/show.html.haml delete mode 100644 app/views/admin/instances/_exhausted_deliveries_days.haml (limited to 'app/views') diff --git a/app/views/admin/domain_blocks/edit.html.haml b/app/views/admin/domain_blocks/edit.html.haml index 6fe2edc82..39c6d108a 100644 --- a/app/views/admin/domain_blocks/edit.html.haml +++ b/app/views/admin/domain_blocks/edit.html.haml @@ -24,10 +24,10 @@ = f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint') .field-group - = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6 + = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), as: :string .field-group - = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6 + = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), as: :string .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 8b78f71f2..bcaa331b5 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -24,10 +24,10 @@ = f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint') .field-group - = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6 + = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), as: :string .field-group - = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6 + = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), as: :string .actions = f.button :button, t('.create'), type: :submit diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml deleted file mode 100644 index e64aaa629..000000000 --- a/app/views/admin/domain_blocks/show.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -- content_for :page_title do - = t('admin.domain_blocks.show.title', domain: @domain_block.domain) - -- if @domain_block.private_comment.present? - .speech-bubble - .speech-bubble__bubble - = simple_format(h(@domain_block.private_comment)) - .speech-bubble__owner= t 'admin.instances.private_comment' - -- if @domain_block.public_comment.present? - .speech-bubble - .speech-bubble__bubble - = simple_format(h(@domain_block.public_comment)) - .speech-bubble__owner= t 'admin.instances.public_comment' - -= simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :delete do |f| - - - unless (@domain_block.noop?) - %p= t(".retroactive.#{@domain_block.severity}") - %p.hint= t(:affected_accounts, - scope: [:admin, :domain_blocks, :show], - count: @domain_block.affected_accounts_count) - - .actions - = f.button :button, t('.undo'), type: :submit diff --git a/app/views/admin/instances/_exhausted_deliveries_days.haml b/app/views/admin/instances/_exhausted_deliveries_days.haml deleted file mode 100644 index e581f542d..000000000 --- a/app/views/admin/instances/_exhausted_deliveries_days.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li.negative-hint - = l(exhausted_deliveries_days) diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index dc81007ac..8a4396002 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -1,33 +1,15 @@ .directory__tag = link_to admin_instance_path(instance) do %h4 + = fa_icon 'warning fw' if instance.failing? = instance.domain + %small - if instance.domain_block - - first_item = true - - if !instance.domain_block.noop? - = t("admin.domain_blocks.severity.#{instance.domain_block.severity}") - - first_item = false - - unless instance.domain_block.suspend? - - if instance.domain_block.reject_media? - - unless first_item - • - = t('admin.domain_blocks.rejecting_media') - - first_item = false - - if instance.domain_block.reject_reports? - - unless first_item - • - = t('admin.domain_blocks.rejecting_reports') - - elsif whitelist_mode? + = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + - elsif instance.domain_allow = t('admin.accounts.whitelisted') - else = t('admin.accounts.no_limits_imposed') - - if instance.failure_days - = ' / ' - %span.negative-hint - = t('admin.instances.delivery.warning_message', count: instance.failure_days) - - if instance.unavailable_domain - = ' / ' - %span.negative-hint - = t('admin.instances.delivery.unavailable_message') + .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= friendly_number_to_human instance.accounts_count diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml index 797948d94..f8273718d 100644 --- a/app/views/admin/instances/index.html.haml +++ b/app/views/admin/instances/index.html.haml @@ -17,22 +17,11 @@ %li= filter_link_to t('admin.instances.moderation.limited'), limited: '1' .filter-subset - %strong= t('admin.instances.delivery.title') + %strong= t('admin.instances.availability.title') %ul - %li= filter_link_to t('admin.instances.delivery.all'), warning: nil, unavailable: nil - %li= filter_link_to t('admin.instances.delivery.warning'), warning: '1', unavailable: nil - %li= filter_link_to t('admin.instances.delivery.unavailable'), warning: nil, unavailable: '1' - - .back-link - = link_to admin_instances_path() do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_all') - = link_to admin_instances_path(limited: 1) do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_limited') - = link_to admin_instances_path(warning: 1) do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_warning') + %li= filter_link_to t('admin.instances.delivery.all'), availability: nil + %li= filter_link_to t('admin.instances.delivery.failing'), availability: 'failing' + %li= filter_link_to t('admin.instances.delivery.unavailable'), availability: 'unavailable' - unless whitelist_mode? = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index 4db8fd15c..bed94f3fe 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -1,88 +1,95 @@ - content_for :page_title do = @instance.domain -.filters - .back-link - = link_to admin_instances_path() do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_all') - = link_to admin_instances_path(limited: 1) do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_limited') - = link_to admin_instances_path(warning: 1) do - %i.fa.fa-chevron-left.fa-fw - = t('admin.instances.back_to_warning') - -.dashboard__counters - %div - = link_to admin_accounts_path(origin: 'remote', by_domain: @instance.domain) do - .dashboard__counters__num= number_with_delimiter @instance.accounts_count - .dashboard__counters__label= t 'admin.accounts.title' - %div - = link_to admin_reports_path(by_target_domain: @instance.domain) do - .dashboard__counters__num= number_with_delimiter @instance.reports_count - .dashboard__counters__label= t 'admin.instances.total_reported' - %div - %div - .dashboard__counters__num= number_to_human_size @instance.media_storage - .dashboard__counters__label= t 'admin.instances.total_storage' - %div - %div - .dashboard__counters__num= number_with_delimiter @instance.following_count - .dashboard__counters__label= t 'admin.instances.total_followed_by_them' - %div - %div - .dashboard__counters__num= number_with_delimiter @instance.followers_count - .dashboard__counters__label= t 'admin.instances.total_followed_by_us' - %div - %div - .dashboard__counters__num= number_with_delimiter @instance.blocks_count - .dashboard__counters__label= t 'admin.instances.total_blocked_by_us' - - %div - %div - .dashboard__counters__num - - if @instance.delivery_failure_tracker.available? - = fa_icon 'check' - - else - = fa_icon 'times' - .dashboard__counters__label= t 'admin.instances.delivery_available' - -- if @instance.private_comment.present? - .speech-bubble - .speech-bubble__bubble - = simple_format(h(@instance.private_comment)) - .speech-bubble__owner= t 'admin.instances.private_comment' - -- if @instance.public_comment.present? - .speech-bubble - .speech-bubble__bubble - = simple_format(h(@instance.public_comment)) - .speech-bubble__owner= t 'admin.instances.public_comment' - -- unless @exhausted_deliveries_days.empty? - %h4= t 'admin.instances.delivery_error_days' - %ul - = render partial: 'exhausted_deliveries_days', collection: @exhausted_deliveries_days - %p.hint - = t 'admin.instances.delivery_error_hint', count: DeliveryFailureTracker::FAILURE_DAYS_THRESHOLD +- content_for :header_tags do + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' + +- content_for :heading_actions do + = l(@time_period.first) + = ' - ' + = l(@time_period.last) + +%p + = fa_icon 'info fw' + = t('admin.instances.totals_time_period_hint_html') + +.dashboard + .dashboard__item + = react_admin_component :counter, measure: 'instance_accounts', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_accounts_measure'), href: admin_accounts_path(origin: 'remote', by_domain: @instance.domain) + .dashboard__item + = react_admin_component :counter, measure: 'instance_statuses', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_statuses_measure') + .dashboard__item + = react_admin_component :counter, measure: 'instance_media_attachments', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_media_attachments_measure') + .dashboard__item + = react_admin_component :counter, measure: 'instance_follows', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_follows_measure') + .dashboard__item + = react_admin_component :counter, measure: 'instance_followers', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_followers_measure') + .dashboard__item + = react_admin_component :counter, measure: 'instance_reports', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, label: t('admin.instances.dashboard.instance_reports_measure'), href: admin_reports_path(by_target_domain: @instance.domain) + .dashboard__item + = react_admin_component :dimension, dimension: 'instance_accounts', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, limit: 8, label: t('admin.instances.dashboard.instance_accounts_dimension') + .dashboard__item + = react_admin_component :dimension, dimension: 'instance_languages', start_at: @time_period.first, end_at: @time_period.last, params: { domain: @instance.domain }, limit: 8, label: t('admin.instances.dashboard.instance_languages_dimension') %hr.spacer/ -%div.action-buttons - %div - - if @instance.domain_allow - = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } - - elsif @instance.domain_block - = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button' - = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button' - - else - = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button' - - if @instance.delivery_failure_tracker.available? - - unless @exhausted_deliveries_days.empty? - = link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' - = link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' +%h3= t('admin.instances.content_policies.title') + +- if whitelist_mode? + %p= t('admin.instances.content_policies.limited_federation_mode_description_html') + + - if @instance.domain_allow + = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } + - else + = link_to t('admin.domain_allows.add_new'), admin_domain_allows_path(domain_allow: { domain: @instance.domain }), class: 'button', method: :post +- else + %p= t('admin.instances.content_policies.description_html') + + - if @instance.domain_block + .table-wrapper + %table.table.horizontal-table + %tbody + %tr + %th= t('admin.instances.content_policies.comment') + %td= @instance.domain_block.private_comment + %tr + %th= t('admin.instances.content_policies.reason') + %td= @instance.domain_block.public_comment + %tr + %th= t('admin.instances.content_policies.policy') + %td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + + = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button' + = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } + - else + = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button' + +%hr.spacer/ + +%h3= t('admin.instances.availability.title') + +%p + = t('admin.instances.availability.description_html', count: DeliveryFailureTracker::FAILURE_DAYS_THRESHOLD) + +.availability-indicator + %ul.availability-indicator__graphic + - @instance.availability_over_days(14).each do |(date, failing)| + %li.availability-indicator__graphic__item{ class: failing ? 'negative' : 'neutral', title: l(date) } + .availability-indicator__hint + - if @instance.unavailable? + %span.negative-hint + = t('admin.instances.availability.failure_threshold_reached', date: l(@instance.unavailable_domain.created_at.to_date)) + = link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } + - elsif @instance.exhausted_deliveries_days.empty? + %span.positive-hint + = t('admin.instances.availability.no_failures_recorded') + = link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } - else - = link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' - - if !@instance.delivery_failure_tracker.available? || @instance.accounts_count.zero? || @instance.domain_block&.suspend? - = link_to t('admin.instances.purge'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button' + %span.negative-hint + = t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days) + = link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } unless @instance.exhausted_deliveries_days.empty? + +- if @instance.unavailable? + %p= t('admin.instances.purge_description_html') + + = link_to t('admin.instances.purge'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button button--destructive' -- cgit From d17fb7013116767fc5c7d5eef63218bd8c45b023 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Mar 2022 09:06:17 +0100 Subject: Change how changes to media attachments are stored for edits (#17696) * Change how changes to media attachments are stored for edits Fix not being able to re-order media attachments * Fix not broadcasting updates when polls/media is changed through ActivityPub * Various fixes and improvements * Update app/models/report.rb Co-authored-by: Claire * Add tracking of media attachment description changes * Change poll in status edit to have a structure closer to the real one Co-authored-by: Claire --- app/chewy/statuses_index.rb | 2 +- app/helpers/statuses_helper.rb | 12 ++--- app/lib/feed_manager.rb | 2 +- app/lib/rss/serializer.rb | 2 +- app/models/report.rb | 16 +++++- app/models/status.rb | 63 +++++++++++++--------- app/models/status_edit.rb | 41 +++++++++++--- app/serializers/activitypub/note_serializer.rb | 6 ++- app/serializers/rest/status_edit_serializer.rb | 10 +++- app/serializers/rest/status_serializer.rb | 2 +- .../activitypub/process_status_update_service.rb | 19 +++---- app/services/fan_out_on_write_service.rb | 2 +- app/services/post_status_service.rb | 6 ++- app/services/remove_status_service.rb | 2 +- app/services/update_status_service.rb | 21 ++------ app/views/admin/reports/_status.html.haml | 12 ++--- app/views/admin/reports/index.html.haml | 4 +- app/views/admin/trends/statuses/_status.html.haml | 2 +- app/views/disputes/strikes/show.html.haml | 2 +- app/views/notification_mailer/_status.html.haml | 4 +- app/views/statuses/_detailed_status.html.haml | 6 +-- app/views/statuses/_og_image.html.haml | 2 +- app/views/statuses/_simple_status.html.haml | 6 +-- ...add_ordered_media_attachment_ids_to_statuses.rb | 5 ++ ...ordered_media_attachment_ids_to_status_edits.rb | 8 +++ ..._media_attachments_changed_from_status_edits.rb | 7 +++ db/schema.rb | 7 ++- spec/models/report_spec.rb | 13 +++-- .../process_status_update_service_spec.rb | 17 +++--- spec/services/update_status_service_spec.rb | 14 ++--- 30 files changed, 190 insertions(+), 125 deletions(-) create mode 100644 db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb create mode 100644 db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb create mode 100644 db/post_migrate/20220303203437_remove_media_attachments_changed_from_status_edits.rb (limited to 'app/views') diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 1903c2ea3..65cbb6fcd 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -57,7 +57,7 @@ class StatusesIndex < Chewy::Index field :id, type: 'long' field :account_id, type: 'long' - field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do + field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.ordered_media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do field :stemmed, type: 'text', analyzer: 'content' end diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 25f079e9d..d328f89b7 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -132,7 +132,7 @@ module StatusesHelper end def render_video_component(status, **options) - video = status.media_attachments.first + video = status.ordered_media_attachments.first meta = video.file.meta || {} @@ -150,12 +150,12 @@ module StatusesHelper }.merge(**options) react_component :video, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end def render_audio_component(status, **options) - audio = status.media_attachments.first + audio = status.ordered_media_attachments.first meta = audio.file.meta || {} @@ -170,7 +170,7 @@ module StatusesHelper }.merge(**options) react_component :audio, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end @@ -178,11 +178,11 @@ module StatusesHelper component_params = { sensitive: sensitized?(status, current_account), autoplay: prefers_autoplay?, - media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }, + media: status.ordered_media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }, }.merge(**options) react_component :media_gallery, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index fc35292f2..46a55c7a4 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -448,7 +448,7 @@ class FeedManager Formatter.instance.plaintext(status), status.spoiler_text, status.preloadable_poll ? status.preloadable_poll.options.join("\n\n") : nil, - status.media_attachments.map(&:description).join("\n\n"), + status.ordered_media_attachments.map(&:description).join("\n\n"), ].compact.join("\n\n") combined_regex.match?(combined_text) diff --git a/app/lib/rss/serializer.rb b/app/lib/rss/serializer.rb index fd56c568c..7e3ed1f17 100644 --- a/app/lib/rss/serializer.rb +++ b/app/lib/rss/serializer.rb @@ -11,7 +11,7 @@ class RSS::Serializer .pub_date(status.created_at) .description(status.spoiler_text.presence || Formatter.instance.format(status, inline_poll_options: true).to_str) - status.media_attachments.each do |media| + status.ordered_media_attachments.each do |media| item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) end end diff --git a/app/models/report.rb b/app/models/report.rb index 8ba2dd8fd..6d4166540 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -63,8 +63,20 @@ class Report < ApplicationRecord Status.with_discarded.where(id: status_ids) end - def media_attachments - MediaAttachment.where(status_id: status_ids) + def media_attachments_count + statuses_to_query = [] + count = 0 + + statuses.pluck(:id, :ordered_media_attachment_ids).each do |id, ordered_ids| + if ordered_ids.nil? + statuses_to_query << id + else + count += ordered_ids.size + end + end + + count += MediaAttachment.where(status_id: statuses_to_query).count unless statuses_to_query.empty? + count end def rules diff --git a/app/models/status.rb b/app/models/status.rb index af3e645dc..db10eedc2 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -3,28 +3,29 @@ # # Table name: statuses # -# id :bigint(8) not null, primary key -# uri :string -# text :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# in_reply_to_id :bigint(8) -# reblog_of_id :bigint(8) -# url :string -# sensitive :boolean default(FALSE), not null -# visibility :integer default("public"), not null -# spoiler_text :text default(""), not null -# reply :boolean default(FALSE), not null -# language :string -# conversation_id :bigint(8) -# local :boolean -# account_id :bigint(8) not null -# application_id :bigint(8) -# in_reply_to_account_id :bigint(8) -# poll_id :bigint(8) -# deleted_at :datetime -# edited_at :datetime -# trendable :boolean +# id :bigint(8) not null, primary key +# uri :string +# text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# in_reply_to_id :bigint(8) +# reblog_of_id :bigint(8) +# url :string +# sensitive :boolean default(FALSE), not null +# visibility :integer default("public"), not null +# spoiler_text :text default(""), not null +# reply :boolean default(FALSE), not null +# language :string +# conversation_id :bigint(8) +# local :boolean +# account_id :bigint(8) not null +# application_id :bigint(8) +# in_reply_to_account_id :bigint(8) +# poll_id :bigint(8) +# deleted_at :datetime +# edited_at :datetime +# trendable :boolean +# ordered_media_attachment_ids :bigint(8) is an Array # class Status < ApplicationRecord @@ -211,11 +212,14 @@ class Status < ApplicationRecord public_visibility? || unlisted_visibility? end - def snapshot!(media_attachments_changed: false, account_id: nil, at_time: nil) + def snapshot!(account_id: nil, at_time: nil) edits.create!( text: text, spoiler_text: spoiler_text, - media_attachments_changed: media_attachments_changed, + sensitive: sensitive, + ordered_media_attachment_ids: ordered_media_attachment_ids || media_attachments.pluck(:id), + media_descriptions: ordered_media_attachments.map(&:description), + poll_options: preloadable_poll&.options, account_id: account_id || self.account_id, created_at: at_time || edited_at ) @@ -228,7 +232,7 @@ class Status < ApplicationRecord alias sign? distributable? def with_media? - media_attachments.any? + ordered_media_attachments.any? end def with_preview_card? @@ -252,6 +256,15 @@ class Status < ApplicationRecord @emojis = CustomEmoji.from_text(fields.join(' '), account.domain) end + def ordered_media_attachments + if ordered_media_attachment_ids.nil? + media_attachments + else + map = media_attachments.index_by(&:id) + ordered_media_attachment_ids.map { |media_attachment_id| map[media_attachment_id] } + end + end + def replies_count status_stat&.replies_count || 0 end diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 6e88864e8..94a387c36 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -3,17 +3,29 @@ # # Table name: status_edits # -# id :bigint(8) not null, primary key -# status_id :bigint(8) not null -# account_id :bigint(8) -# text :text default(""), not null -# spoiler_text :text default(""), not null -# media_attachments_changed :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# status_id :bigint(8) not null +# account_id :bigint(8) +# text :text default(""), not null +# spoiler_text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# ordered_media_attachment_ids :bigint(8) is an Array +# media_descriptions :text is an Array +# poll_options :string is an Array +# sensitive :boolean # class StatusEdit < ApplicationRecord + self.ignored_columns = %w( + media_attachments_changed + ) + + class PreservedMediaAttachment < ActiveModelSerializers::Model + attributes :media_attachment, :description + delegate :id, :type, :url, :preview_url, :remote_url, :preview_remote_url, :text_url, :meta, :blurhash, to: :media_attachment + end + belongs_to :status belongs_to :account, optional: true @@ -25,4 +37,17 @@ class StatusEdit < ApplicationRecord return @emojis if defined?(@emojis) @emojis = CustomEmoji.from_text([spoiler_text, text].join(' '), status.account.domain) end + + def ordered_media_attachments + return @ordered_media_attachments if defined?(@ordered_media_attachments) + + @ordered_media_attachments = begin + if ordered_media_attachment_ids.nil? + [] + else + map = status.media_attachments.index_by(&:id) + ordered_media_attachment_ids.map.with_index { |media_attachment_id, index| PreservedMediaAttachment.new(media_attachment: map[media_attachment_id], description: media_descriptions[index]) } + end + end + end end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 12dabc65a..7be2e2647 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -13,7 +13,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer attribute :content_map, if: :language? attribute :updated, if: :edited? - has_many :media_attachments, key: :attachment + has_many :virtual_attachments, key: :attachment has_many :virtual_tags, key: :tag has_one :replies, serializer: ActivityPub::CollectionSerializer, if: :local? @@ -106,6 +106,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer object.account.sensitized? || object.sensitive end + def virtual_attachments + object.ordered_media_attachments + end + def virtual_tags object.active_mentions.to_a.sort_by(&:id) + object.tags + object.emojis end diff --git a/app/serializers/rest/status_edit_serializer.rb b/app/serializers/rest/status_edit_serializer.rb index a1f9e824e..05ccd5e94 100644 --- a/app/serializers/rest/status_edit_serializer.rb +++ b/app/serializers/rest/status_edit_serializer.rb @@ -3,12 +3,18 @@ class REST::StatusEditSerializer < ActiveModel::Serializer has_one :account, serializer: REST::AccountSerializer - attributes :content, :spoiler_text, - :media_attachments_changed, :created_at + attributes :content, :spoiler_text, :sensitive, :created_at + has_many :ordered_media_attachments, key: :media_attachments, serializer: REST::MediaAttachmentSerializer has_many :emojis, serializer: REST::CustomEmojiSerializer + attribute :poll, if: -> { object.poll_options.present? } + def content Formatter.instance.format(object) end + + def poll + { options: object.poll_options.map { |title| { title: title } } } + end end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index aef51e0f7..7c3dd673e 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -19,7 +19,7 @@ class REST::StatusSerializer < ActiveModel::Serializer belongs_to :application, if: :show_application? belongs_to :account, serializer: REST::AccountSerializer - has_many :media_attachments, serializer: REST::MediaAttachmentSerializer + has_many :ordered_media_attachments, key: :media_attachments, serializer: REST::MediaAttachmentSerializer has_many :ordered_mentions, key: :mentions has_many :tags has_many :emojis, serializer: REST::CustomEmojiSerializer diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 7438a7c53..11afa894f 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -76,13 +76,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService end end - removed_media_attachments = previous_media_attachments - next_media_attachments - added_media_attachments = next_media_attachments - previous_media_attachments + added_media_attachments = next_media_attachments - previous_media_attachments - MediaAttachment.where(id: removed_media_attachments.map(&:id)).update_all(status_id: nil) MediaAttachment.where(id: added_media_attachments.map(&:id)).update_all(status_id: @status.id) - @media_attachments_changed = true if removed_media_attachments.any? || added_media_attachments.any? + @status.ordered_media_attachment_ids = next_media_attachments.map(&:id) + @status.media_attachments.reload + + @media_attachments_changed = true if @status.ordered_media_attachment_ids_changed? end def update_poll! @@ -215,19 +216,13 @@ class ActivityPub::ProcessStatusUpdateService < BaseService return if @status.edits.any? - @status.snapshot!( - media_attachments_changed: false, - at_time: @status.created_at - ) + @status.snapshot!(at_time: @status.created_at) end def create_edit! return unless significant_changes? - @status.snapshot!( - media_attachments_changed: @media_attachments_changed || @poll_changed, - account_id: @account.id - ) + @status.snapshot!(account_id: @account.id) end def skip_download? diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 2bab91116..76404c6b8 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -110,7 +110,7 @@ class FanOutOnWriteService < BaseService Redis.current.publish('timeline:public', anonymous_payload) Redis.current.publish(@status.local? ? 'timeline:public:local' : 'timeline:public:remote', anonymous_payload) - if @status.media_attachments.any? + if @status.with_media? Redis.current.publish('timeline:public:media', anonymous_payload) Redis.current.publish(@status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', anonymous_payload) end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 7508c3b64..c132930a9 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -100,7 +100,10 @@ class PostStatusService < BaseService end def validate_media! - return if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable) + if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable) + @media = [] + return + end raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if @options[:media_ids].size > 4 || @options[:poll].present? @@ -157,6 +160,7 @@ class PostStatusService < BaseService { text: @text, media_attachments: @media || [], + ordered_media_attachment_ids: (@options[:media_ids] || []).map(&:to_i) & @media.map(&:id), thread: @in_reply_to, poll_attributes: poll_attributes, sensitive: @sensitive, diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 7fb9b6301..159aec1f2 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -40,7 +40,7 @@ class RemoveStatusService < BaseService remove_reblogs remove_from_hashtags remove_from_public - remove_from_media if @status.media_attachments.any? + remove_from_media if @status.with_media? remove_media end diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index 973e6ddee..1c63ab656 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -17,8 +17,6 @@ class UpdateStatusService < BaseService @status = status @options = options @account_id = account_id - @media_attachments_changed = false - @poll_changed = false Status.transaction do create_previous_edit! @@ -41,14 +39,12 @@ class UpdateStatusService < BaseService def update_media_attachments! previous_media_attachments = @status.media_attachments.to_a next_media_attachments = validate_media! - removed_media_attachments = previous_media_attachments - next_media_attachments added_media_attachments = next_media_attachments - previous_media_attachments - MediaAttachment.where(id: removed_media_attachments.map(&:id)).update_all(status_id: nil) MediaAttachment.where(id: added_media_attachments.map(&:id)).update_all(status_id: @status.id) + @status.ordered_media_attachment_ids = (@options[:media_ids] || []).map(&:to_i) & next_media_attachments.map(&:id) @status.media_attachments.reload - @media_attachments_changed = true if removed_media_attachments.any? || added_media_attachments.any? end def validate_media! @@ -73,19 +69,18 @@ class UpdateStatusService < BaseService # If for some reasons the options were changed, it invalidates all previous # votes, so we need to remove them - @poll_changed = true if @options[:poll][:options] != poll.options || ActiveModel::Type::Boolean.new.cast(@options[:poll][:multiple]) != poll.multiple + poll_changed = true if @options[:poll][:options] != poll.options || ActiveModel::Type::Boolean.new.cast(@options[:poll][:multiple]) != poll.multiple poll.options = @options[:poll][:options] poll.hide_totals = @options[:poll][:hide_totals] || false poll.multiple = @options[:poll][:multiple] || false poll.expires_in = @options[:poll][:expires_in] - poll.reset_votes! if @poll_changed + poll.reset_votes! if poll_changed poll.save! @status.poll_id = poll.id elsif previous_poll.present? previous_poll.destroy - @poll_changed = true @status.poll_id = nil end end @@ -136,16 +131,10 @@ class UpdateStatusService < BaseService return if @status.edits.any? - @status.snapshot!( - media_attachments_changed: false, - at_time: @status.created_at - ) + @status.snapshot!(at_time: @status.created_at) end def create_edit! - @status.snapshot!( - media_attachments_changed: @media_attachments_changed || @poll_changed, - account_id: @account_id - ) + @status.snapshot!(account_id: @account_id) end end diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml index 4e06d4bbf..1c033c4c3 100644 --- a/app/views/admin/reports/_status.html.haml +++ b/app/views/admin/reports/_status.html.haml @@ -11,15 +11,15 @@ %strong> Content warning: #{Formatter.instance.format_spoiler(status.proper)} = Formatter.instance.format(status.proper, custom_emojify: true) - - unless status.proper.media_attachments.empty? - - if status.proper.media_attachments.first.video? - - video = status.proper.media_attachments.first + - unless status.proper.ordered_media_attachments.empty? + - if status.proper.ordered_media_attachments.first.video? + - video = status.proper.ordered_media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), frameRate: video.file.meta.dig('original', 'frame_rate'), blurhash: video.blurhash, sensitive: status.proper.sensitive?, visible: false, width: 610, height: 343, inline: true, alt: video.description, media: [ActiveModelSerializers::SerializableResource.new(video, serializer: REST::MediaAttachmentSerializer)].as_json - - elsif status.proper.media_attachments.first.audio? - - audio = status.proper.media_attachments.first + - elsif status.proper.ordered_media_attachments.first.audio? + - audio = status.proper.ordered_media_attachments.first = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration) - else - = react_component :media_gallery, height: 343, sensitive: status.proper.sensitive?, visible: false, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } + = react_component :media_gallery, height: 343, sensitive: status.proper.sensitive?, visible: false, media: status.proper.ordered_media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } .detailed-status__meta - if status.application diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 619173373..248718a73 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -59,11 +59,11 @@ %span.report-card__summary__item__content__icon{ title: t('admin.accounts.statuses') } = fa_icon('comment') - = report.statuses.count + = report.status_ids.size %span.report-card__summary__item__content__icon{ title: t('admin.accounts.media_attachments') } = fa_icon('camera') - = report.media_attachments.count + = report.media_attachments_count - if report.forwarded? · diff --git a/app/views/admin/trends/statuses/_status.html.haml b/app/views/admin/trends/statuses/_status.html.haml index edb27b9ff..50a855349 100644 --- a/app/views/admin/trends/statuses/_status.html.haml +++ b/app/views/admin/trends/statuses/_status.html.haml @@ -9,7 +9,7 @@ = link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank', class: 'emojify', rel: 'noopener noreferrer' do = one_line_preview(status) - - status.media_attachments.each do |media_attachment| + - status.ordered_media_attachments.each do |media_attachment| %abbr{ title: media_attachment.description } = fa_icon 'link' = media_attachment.file_file_name diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml index 7248b2574..0fc32b918 100644 --- a/app/views/disputes/strikes/show.html.haml +++ b/app/views/disputes/strikes/show.html.haml @@ -53,7 +53,7 @@ = link_to short_account_status_url(@strike.target_account, status_id), class: 'emojify' do = one_line_preview(status) - - status.media_attachments.each do |media_attachment| + - status.ordered_media_attachments.each do |media_attachment| %abbr{ title: media_attachment.description } = fa_icon 'link' = media_attachment.file_file_name diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml index 31460a76e..f520208e1 100644 --- a/app/views/notification_mailer/_status.html.haml +++ b/app/views/notification_mailer/_status.html.haml @@ -33,9 +33,9 @@ %div.auto-dir = Formatter.instance.format(status) - - if status.media_attachments.size > 0 + - if status.ordered_media_attachments.size > 0 %p - - status.media_attachments.each do |a| + - status.ordered_media_attachments.each do |a| - if status.local? = link_to full_asset_url(a.file.url(:original)), full_asset_url(a.file.url(:original)) - else diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index 1922f53ce..6ccf3725a 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -25,10 +25,10 @@ - if status.preloadable_poll = render_poll_component(status) - - if !status.media_attachments.empty? - - if status.media_attachments.first.video? + - if !status.ordered_media_attachments.empty? + - if status.ordered_media_attachments.first.video? = render_video_component(status, width: 670, height: 380, detailed: true) - - elsif status.media_attachments.first.audio? + - elsif status.ordered_media_attachments.first.audio? = render_audio_component(status, width: 670, height: 380) - else = render_media_gallery_component(status, height: 380, standalone: true) diff --git a/app/views/statuses/_og_image.html.haml b/app/views/statuses/_og_image.html.haml index c8b6147ef..5a647531a 100644 --- a/app/views/statuses/_og_image.html.haml +++ b/app/views/statuses/_og_image.html.haml @@ -1,6 +1,6 @@ - if activity.is_a?(Status) && (activity.non_sensitive_with_media? || (activity.with_media? && Setting.preview_sensitive_media)) - player_card = false - - activity.media_attachments.each do |media| + - activity.ordered_media_attachments.each do |media| - if media.image? = opengraph 'og:image', full_asset_url(media.file.url(:original)) = opengraph 'og:image:type', media.file_content_type diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index 0139d2016..5dd265b59 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -37,10 +37,10 @@ - if status.preloadable_poll = render_poll_component(status) - - if !status.media_attachments.empty? - - if status.media_attachments.first.video? + - if !status.ordered_media_attachments.empty? + - if status.ordered_media_attachments.first.video? = render_video_component(status, width: 610, height: 343) - - elsif status.media_attachments.first.audio? + - elsif status.ordered_media_attachments.first.audio? = render_audio_component(status, width: 610, height: 343) - else = render_media_gallery_component(status, height: 343) diff --git a/db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb b/db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb new file mode 100644 index 000000000..5443f32a2 --- /dev/null +++ b/db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb @@ -0,0 +1,5 @@ +class AddOrderedMediaAttachmentIdsToStatuses < ActiveRecord::Migration[6.1] + def change + add_column :statuses, :ordered_media_attachment_ids, :bigint, array: true + end +end diff --git a/db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb b/db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb new file mode 100644 index 000000000..b1071f359 --- /dev/null +++ b/db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb @@ -0,0 +1,8 @@ +class AddOrderedMediaAttachmentIdsToStatusEdits < ActiveRecord::Migration[6.1] + def change + add_column :status_edits, :ordered_media_attachment_ids, :bigint, array: true + add_column :status_edits, :media_descriptions, :text, array: true + add_column :status_edits, :poll_options, :string, array: true + add_column :status_edits, :sensitive, :boolean + end +end diff --git a/db/post_migrate/20220303203437_remove_media_attachments_changed_from_status_edits.rb b/db/post_migrate/20220303203437_remove_media_attachments_changed_from_status_edits.rb new file mode 100644 index 000000000..09725c74e --- /dev/null +++ b/db/post_migrate/20220303203437_remove_media_attachments_changed_from_status_edits.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveMediaAttachmentsChangedFromStatusEdits < ActiveRecord::Migration[5.2] + def change + safety_assured { remove_column :status_edits, :media_attachments_changed, :boolean, default: false, null: false } + end +end diff --git a/db/schema.rb b/db/schema.rb index ef1620113..6251fa28c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -391,7 +391,6 @@ ActiveRecord::Schema.define(version: 2022_03_07_094650) do t.bigint "parent_id" t.inet "ips", array: true t.datetime "last_refresh_at" - t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end @@ -845,9 +844,12 @@ ActiveRecord::Schema.define(version: 2022_03_07_094650) do t.bigint "account_id" t.text "text", default: "", null: false t.text "spoiler_text", default: "", null: false - t.boolean "media_attachments_changed", default: false, null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.bigint "ordered_media_attachment_ids", array: true + t.text "media_descriptions", array: true + t.string "poll_options", array: true + t.boolean "sensitive" t.index ["account_id"], name: "index_status_edits_on_account_id" t.index ["status_id"], name: "index_status_edits_on_status_id" end @@ -892,6 +894,7 @@ ActiveRecord::Schema.define(version: 2022_03_07_094650) do t.datetime "deleted_at" t.datetime "edited_at" t.boolean "trendable" + t.bigint "ordered_media_attachment_ids", array: true t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20190820", order: { id: :desc }, where: "(deleted_at IS NULL)" t.index ["deleted_at"], name: "index_statuses_on_deleted_at", where: "(deleted_at IS NOT NULL)" t.index ["id", "account_id"], name: "index_statuses_local_20190824", order: { id: :desc }, where: "((local OR (uri IS NULL)) AND (deleted_at IS NULL) AND (visibility = 0) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index df32a7c9d..874be4132 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -11,14 +11,13 @@ describe Report do end end - describe 'media_attachments' do - it 'returns media attachments from statuses' do - status = Fabricate(:status) - media_attachment = Fabricate(:media_attachment, status: status) - _other_media_attachment = Fabricate(:media_attachment) - report = Fabricate(:report, status_ids: [status.id]) + describe 'media_attachments_count' do + it 'returns count of media attachments in statuses' do + status1 = Fabricate(:status, ordered_media_attachment_ids: [1, 2]) + status2 = Fabricate(:status, ordered_media_attachment_ids: [5]) + report = Fabricate(:report, status_ids: [status1.id, status2.id]) - expect(report.media_attachments).to eq [media_attachment] + expect(report.media_attachments_count).to eq 3 end end diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 6ee1dcb43..40b405217 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -124,7 +124,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'updates media attachments' do - media_attachment = status.media_attachments.reload.first + media_attachment = status.reload.ordered_media_attachments.first expect(media_attachment).to_not be_nil expect(media_attachment.remote_url).to eq 'https://example.com/foo.png' @@ -135,7 +135,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'records media change in edit' do - expect(status.edits.reload.last.media_attachments_changed).to be true + expect(status.edits.reload.last.ordered_media_attachment_ids).to_not be_empty end end @@ -173,11 +173,11 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'updates media attachments' do - expect(status.media_attachments.reload.map(&:remote_url)).to eq %w(https://example.com/foo.png) + expect(status.ordered_media_attachments.map(&:remote_url)).to eq %w(https://example.com/foo.png) end it 'records media change in edit' do - expect(status.edits.reload.last.media_attachments_changed).to be true + expect(status.edits.reload.last.ordered_media_attachment_ids).to_not be_empty end end @@ -193,7 +193,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'records media change in edit' do - expect(status.edits.reload.last.media_attachments_changed).to be true + expect(status.edits.reload.last.poll_options).to be_nil end end @@ -226,7 +226,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'records media change in edit' do - expect(status.edits.reload.last.media_attachments_changed).to be true + expect(status.edits.reload.last.poll_options).to eq %w(Foo Bar Baz) end end @@ -239,10 +239,5 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do subject.call(status, json) expect(status.reload.edited_at.to_s).to eq '2021-09-08 22:39:25 UTC' end - - it 'records that no media has been changed in edit' do - subject.call(status, json) - expect(status.edits.reload.last.media_attachments_changed).to be false - end end end diff --git a/spec/services/update_status_service_spec.rb b/spec/services/update_status_service_spec.rb index 4fd4837c6..78cc89cd4 100644 --- a/spec/services/update_status_service_spec.rb +++ b/spec/services/update_status_service_spec.rb @@ -21,7 +21,7 @@ RSpec.describe UpdateStatusService, type: :service do end it 'saves edit history' do - expect(status.edits.pluck(:text, :media_attachments_changed)).to eq [['Foo', false], ['Bar', false]] + expect(status.edits.pluck(:text)).to eq %w(Foo Bar) end end @@ -39,7 +39,7 @@ RSpec.describe UpdateStatusService, type: :service do end it 'saves edit history' do - expect(status.edits.pluck(:text, :spoiler_text, :media_attachments_changed)).to eq [['Foo', '', false], ['Foo', 'Bar', false]] + expect(status.edits.pluck(:text, :spoiler_text)).to eq [['Foo', ''], ['Foo', 'Bar']] end end @@ -54,11 +54,11 @@ RSpec.describe UpdateStatusService, type: :service do end it 'updates media attachments' do - expect(status.media_attachments.to_a).to eq [attached_media_attachment] + expect(status.ordered_media_attachments).to eq [attached_media_attachment] end - it 'detaches detached media attachments' do - expect(detached_media_attachment.reload.status_id).to be_nil + it 'does not detach detached media attachments' do + expect(detached_media_attachment.reload.status_id).to eq status.id end it 'attaches attached media attachments' do @@ -66,7 +66,7 @@ RSpec.describe UpdateStatusService, type: :service do end it 'saves edit history' do - expect(status.edits.pluck(:text, :media_attachments_changed)).to eq [['Foo', false], ['Foo', true]] + expect(status.edits.pluck(:ordered_media_attachment_ids)).to eq [[detached_media_attachment.id], [attached_media_attachment.id]] end end @@ -95,7 +95,7 @@ RSpec.describe UpdateStatusService, type: :service do end it 'saves edit history' do - expect(status.edits.pluck(:text, :media_attachments_changed)).to eq [['Foo', false], ['Foo', true]] + expect(status.edits.pluck(:poll_options)).to eq [%w(Foo Bar), %w(Bar Baz Foo)] end end -- cgit From 3bc0aeed50b876db6e19c29d1f76775da764411b Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 9 Mar 2022 12:11:11 +0100 Subject: Change text version of warning mail to mention appeals instead of mails (#17725) Also, the instruction to reply to e-mail would probably not work in many cases where the notifications e-mail address is not able to receive incoming emails or the mailbox is not actively monitored. --- app/views/user_mailer/warning.text.erb | 3 ++- config/locales/en.yml | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/user_mailer/warning.text.erb b/app/views/user_mailer/warning.text.erb index 31d7308ae..1f410f441 100644 --- a/app/views/user_mailer/warning.text.erb +++ b/app/views/user_mailer/warning.text.erb @@ -32,4 +32,5 @@ --- <% end %> -<%= t 'user_mailer.warning.get_in_touch', instance: @instance %> +<%= t 'user_mailer.warning.appeal_description', instance: @instance %> +<%= disputes_strike_url(@warning) %> diff --git a/config/locales/en.yml b/config/locales/en.yml index acedf84c2..8f6100129 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1651,7 +1651,6 @@ en: sensitive: From now on, all your uploaded media files will be marked as sensitive and hidden behind a click-through warning. silence: You can still use your account but only people who are already following you will see your posts on this server, and you may be excluded from various discovery features. However, others may still manually follow you. suspend: You can no longer use your account, and your profile and other data are no longer accessible. You can still login to request a backup of your data until the data is fully removed in about 30 days, but we will retain some basic data to prevent you from evading the suspension. - get_in_touch: If you believe this is an error, you can reply to this e-mail to get in touch with the staff of %{instance}. reason: 'Reason:' statuses: 'Posts cited:' subject: -- cgit