diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 3ba9f5df2..8c15f6835 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -32,6 +32,8 @@ module Admin 'nsfw_on' elsif params[:nsfw_off] 'nsfw_off' + elsif params[:expire] + 'expire' elsif params[:delete] 'delete' end diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index ef279509d..ec362ec34 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -11,10 +11,11 @@ module Admin def index authorize :status, :index? - @statuses = @account.statuses.where(visibility: [:public, :unlisted]) + @statuses = Status.include_expired.where(account: @account).where(visibility: [:public, :unlisted]) if params[:media] - @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)) + @statuses.merge!(Status.include_expired.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)) + @statuses = @statuses.order(id: :desc) end @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) @@ -67,6 +68,8 @@ module Admin 'nsfw_on' elsif params[:nsfw_off] 'nsfw_off' + elsif params[:expire] + 'expire' elsif params[:delete] 'delete' end diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss index cd41e5786..c727b100d 100644 --- a/app/javascript/styles/mastodon-light/diff.scss +++ b/app/javascript/styles/mastodon-light/diff.scss @@ -611,6 +611,22 @@ html { .nothing-here { border-color: lighten($ui-base-color, 8%); } + + &__row--expired { + background: darken($warning-red, 29%); + + &:hover { + background: darken($warning-red, 27%); + } + + &:nth-child(even) { + background: darken($warning-red, 25%); + + &:hover { + background: darken($warning-red, 23%); + } + } + } } .activity-stream { diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 1016d3d61..62ac2ded2 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -217,6 +217,22 @@ a.table-action-link { } } + &--expired { + background: darken($warning-red, 54%); + + &:hover { + background: darken($warning-red, 52%); + } + + &:nth-child(even) { + background: darken($warning-red, 50%); + + &:hover { + background: darken($warning-red, 48%); + } + } + } + &__content { padding-top: 12px; padding-bottom: 16px; diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb index 1d1db1b7a..881f7c010 100644 --- a/app/models/admin/action_log.rb +++ b/app/models/admin/action_log.rb @@ -31,7 +31,7 @@ class Admin::ActionLog < ApplicationRecord def set_changes case action - when :destroy, :create + when :destroy, :create, :expire self.recorded_changes = target.attributes when :update, :promote, :demote self.recorded_changes = target.previous_changes diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index a1c156a8b..9edb3be26 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -31,6 +31,7 @@ class Admin::ActionLogFilter disable_user: { target_type: 'User', action: 'disable' }.freeze, enable_custom_emoji: { target_type: 'CustomEmoji', action: 'enable' }.freeze, enable_user: { target_type: 'User', action: 'enable' }.freeze, + expire_status: { target_type: 'Status', action: 'expire' }.freeze, memorialize_account: { target_type: 'Account', action: 'memorialize' }.freeze, promote_user: { target_type: 'User', action: 'promote' }.freeze, remove_avatar_user: { target_type: 'User', action: 'remove_avatar' }.freeze, diff --git a/app/models/form/status_batch.rb b/app/models/form/status_batch.rb index c4943a7ea..d91e22a30 100644 --- a/app/models/form/status_batch.rb +++ b/app/models/form/status_batch.rb @@ -10,6 +10,8 @@ class Form::StatusBatch case action when 'nsfw_on', 'nsfw_off' change_sensitive(action == 'nsfw_on') + when 'expire' + expire_statuses when 'delete' delete_statuses end @@ -32,8 +34,17 @@ class Form::StatusBatch false end - def delete_statuses + def expire_statuses Status.where(id: status_ids).reorder(nil).find_each do |status| + RemoveStatusService.new.call(status, mark_expired: true) + log_action :expire, status + end + + true + end + + def delete_statuses + Status.include_expired.where(id: status_ids).reorder(nil).find_each do |status| status.discard RemovalWorker.perform_async(status.id, immediate: true) Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 679c03b4b..22da10d15 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -15,10 +15,10 @@ class RemoveStatusService < BaseService @status = status @account = status.account @options = options - @status_expire = status.status_expire + @status_expire = status.status_expire || mark_expired? && StatusExpire.new(status_id: status.id, expires_at: Time.now) || nil @payload = Oj.dump(event: mark_expired? ? :expire : :delete, payload: status.id.to_s) - return if mark_expired? && @status_expire.nil? + return if mark_expired? && already_expired? @status.discard unless mark_expired? @@ -70,6 +70,10 @@ class RemoveStatusService < BaseService @options[:mark_expired] end + def already_expired? + @status.expired? + end + def remove_from_self FeedManager.instance.unpush_from_home(@account, @status, @options) end diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml index ada6dd2bc..fcbd3c202 100644 --- a/app/views/admin/reports/_status.html.haml +++ b/app/views/admin/reports/_status.html.haml @@ -1,4 +1,4 @@ -.batch-table__row +.batch-table__row{ class: ('batch-table__row--expired' if status.proper.expired?)} %label.batch-table__row__select.batch-checkbox = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id .batch-table__row__content @@ -38,3 +38,7 @@ · = fa_icon('eye-slash fw') = t('stream_entries.sensitive_content') + - if status.proper.expired? + · + = fa_icon('clock-o fw') + = t('stream_entries.expired') diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index b060c553f..420600eb9 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -112,6 +112,7 @@ .batch-table__toolbar__actions = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('clock-o'), t('admin.statuses.batch.expire')]), name: :expire, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } .batch-table__body = render partial: 'admin/reports/status', collection: @report.statuses, locals: { f: f } diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml index c39ba9071..813a5b3ff 100644 --- a/app/views/admin/statuses/index.html.haml +++ b/app/views/admin/statuses/index.html.haml @@ -30,6 +30,7 @@ .batch-table__toolbar__actions = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('clock-o'), t('admin.statuses.batch.expire')]), name: :expire, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } .batch-table__body = render partial: 'admin/reports/status', collection: @statuses, locals: { f: f } diff --git a/app/views/admin/statuses/show.html.haml b/app/views/admin/statuses/show.html.haml index e2470198d..ca7878f59 100644 --- a/app/views/admin/statuses/show.html.haml +++ b/app/views/admin/statuses/show.html.haml @@ -22,6 +22,7 @@ .batch-table__toolbar__actions = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('clock-o'), t('admin.statuses.batch.expire')]), name: :expire, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } .batch-table__body = render partial: 'admin/reports/status', collection: @statuses, locals: { f: f } diff --git a/config/locales/en.yml b/config/locales/en.yml index 7c279bf41..dece3f40e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -260,6 +260,7 @@ en: enable_custom_emoji: Enable Custom Emoji enable_sign_in_token_auth_user: Enable E-mail Token Authentication for User enable_user: Enable User + expire_status: Close Post memorialize_account: Memorialize Account promote_user: Promote User remove_avatar_user: Remove Avatar @@ -305,6 +306,7 @@ en: enable_custom_emoji_html: "%{name} enabled emoji %{target}" enable_sign_in_token_auth_user_html: "%{name} enabled e-mail token authentication for %{target}" enable_user_html: "%{name} enabled login for user %{target}" + expire_status_html: "%{name} closed post by %{target}" memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page" promote_user_html: "%{name} promoted user %{target}" remove_avatar_user_html: "%{name} removed %{target}'s avatar" @@ -694,6 +696,7 @@ en: back_to_account: Back to account page batch: delete: Delete + expire: Close nsfw_off: Mark as not sensitive nsfw_on: Mark as sensitive deleted: Deleted @@ -1442,6 +1445,7 @@ en: invalid_expire_at: Invalid expires are specified. invalid_expire_action: Invalid expires_action are specified. stream_entries: + expired: Expired pinned: Pinned post reblogged: boosted sensitive_content: Sensitive content diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 6e0a00182..a457e665a 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -246,6 +246,7 @@ ja: disable_user: ユーザーを無効化 enable_custom_emoji: カスタム絵文字を有効化 enable_user: ユーザーを有効化 + expire_status: 投稿を公開終了 memorialize_account: 追悼アカウント化 promote_user: ユーザーを昇格 remove_avatar_user: アイコンを削除 @@ -289,6 +290,7 @@ ja: disable_user_html: "%{name} さんが %{target} さんのログインを無効化しました" enable_custom_emoji_html: "%{name} さんがカスタム絵文字 %{target} を有効化しました" enable_user_html: "%{name} さんが %{target} さんのログインを有効化しました" + expire_status_html: "%{name} さんが %{target} さんの投稿を公開終了しました" memorialize_account_html: "%{name} さんが %{target} さんを追悼アカウントページに登録しました" promote_user_html: "%{name} さんが %{target} さんを昇格しました" remove_avatar_user_html: "%{name} さんが %{target} さんのアイコンを削除しました" @@ -672,6 +674,7 @@ ja: back_to_account: アカウントページに戻る batch: delete: 削除 + expire: 公開終了 nsfw_off: 閲覧注意をはずす nsfw_on: 閲覧注意にする deleted: 削除済み @@ -1345,6 +1348,7 @@ ja: invalid_expire_at: 不正な公開期限が指定されています invalid_expire_action: 不正な公開期限時のアクションが指定されています stream_entries: + expired: 公開終了 pinned: 固定された投稿 reblogged: さんがブースト sensitive_content: 閲覧注意