diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/admin/reported_statuses_controller.rb | 15 | ||||
-rw-r--r-- | app/controllers/admin/reports_controller.rb | 4 | ||||
-rw-r--r-- | app/controllers/admin/statuses_controller.rb | 69 | ||||
-rw-r--r-- | app/javascript/packs/admin.js | 40 | ||||
-rw-r--r-- | app/javascript/styles/admin.scss | 45 | ||||
-rw-r--r-- | app/models/form/status_batch.rb | 39 | ||||
-rw-r--r-- | app/views/admin/accounts/show.html.haml | 4 | ||||
-rw-r--r-- | app/views/admin/reports/show.html.haml | 34 | ||||
-rw-r--r-- | app/views/admin/statuses/index.html.haml | 47 |
9 files changed, 280 insertions, 17 deletions
diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 32434d30f..5a31adecf 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -5,7 +5,14 @@ module Admin include Authorization before_action :set_report - before_action :set_status + before_action :set_status, only: [:update, :destroy] + + def create + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_report_path(@report) + end def update @status.update(status_params) @@ -15,7 +22,7 @@ module Admin def destroy authorize @status, :destroy? RemovalWorker.perform_async(@status.id) - redirect_to admin_report_path(@report) + render json: @status end private @@ -24,6 +31,10 @@ module Admin params.require(:status).permit(:sensitive) end + def form_status_batch_params + params.require(:form_status_batch).permit(:action, status_ids: []) + end + def set_report @report = Report.find(params[:report_id]) end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 2d8c3c820..226467739 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -8,7 +8,9 @@ module Admin @reports = filtered_reports.page(params[:page]) end - def show; end + def show + @form = Form::StatusBatch.new + end def update process_report diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb new file mode 100644 index 000000000..50712f0dd --- /dev/null +++ b/app/controllers/admin/statuses_controller.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Admin + class StatusesController < BaseController + include Authorization + + helper_method :current_params + + before_action :set_account + before_action :set_status, only: [:update, :destroy] + + PAR_PAGE = 20 + + def index + @statuses = @account.statuses + if params[:media] + account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct + @statuses.merge!(Status.where(id: account_media_status_ids)) + end + @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PAR_PAGE) + + @form = Form::StatusBatch.new + end + + def create + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_account_statuses_path(@account.id, current_params) + end + + def update + @status.update(status_params) + redirect_to admin_account_statuses_path(@account.id, current_params) + end + + def destroy + authorize @status, :destroy? + RemovalWorker.perform_async(@status.id) + render json: @status + end + + private + + def status_params + params.require(:status).permit(:sensitive) + end + + def form_status_batch_params + params.require(:form_status_batch).permit(:action, status_ids: []) + end + + def set_status + @status = @account.statuses.find(params[:id]) + end + + def set_account + @account = Account.find(params[:account_id]) + end + + def current_params + page = (params[:page] || 1).to_i + { + media: params[:media], + page: page > 1 && page, + }.select { |_, value| value.present? } + end + end +end diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js new file mode 100644 index 000000000..993827db5 --- /dev/null +++ b/app/javascript/packs/admin.js @@ -0,0 +1,40 @@ +import { delegate } from 'rails-ujs'; + +function handleDeleteStatus(event) { + const [data] = event.detail; + const element = document.querySelector(`[data-id="${data.id}"]`); + if (element) { + element.parentNode.removeChild(element); + } +} + +[].forEach.call(document.querySelectorAll('.trash-button'), (content) => { + content.addEventListener('ajax:success', handleDeleteStatus); +}); + +const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]'; + +delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { + [].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => { + content.checked = target.checked; + }); +}); + +delegate(document, batchCheckboxClassName, 'change', () => { + const checkAllElement = document.querySelector('#batch_checkbox_all'); + if (checkAllElement) { + checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + } +}); + +delegate(document, '.media-spoiler-show-button', 'click', () => { + [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => { + content.classList.add('media-spoiler-wrapper__visible'); + }); +}); + +delegate(document, '.media-spoiler-hide-button', 'click', () => { + [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => { + content.classList.remove('media-spoiler-wrapper__visible'); + }); +}); diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss index 3bc713566..4c3bbdfc5 100644 --- a/app/javascript/styles/admin.scss +++ b/app/javascript/styles/admin.scss @@ -253,7 +253,8 @@ } } -.report-status { +.report-status, +.account-status { display: flex; margin-bottom: 10px; @@ -263,7 +264,8 @@ } } -.report-status__actions { +.report-status__actions, +.account-status__actions { flex: 0 0 auto; display: flex; flex-direction: column; @@ -275,3 +277,42 @@ margin-bottom: 10px; } } + +.batch-form-box { + display: flex; + margin-bottom: 10px; + + #form_status_batch_action { + margin-right: 5px; + font-size: 14px; + } + + .media-spoiler-toggle-buttons { + margin-left: auto; + + .button { + overflow: visible; + } + } +} + +.batch-checkbox, +.batch-checkbox-all { + display: flex; + align-items: center; + margin-right: 5px; +} + +.back-link { + margin-bottom: 10px; + font-size: 14px; + + a { + color: $classic-highlight-color; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} diff --git a/app/models/form/status_batch.rb b/app/models/form/status_batch.rb new file mode 100644 index 000000000..a97b4aa28 --- /dev/null +++ b/app/models/form/status_batch.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class Form::StatusBatch + include ActiveModel::Model + + attr_accessor :status_ids, :action + + ACTION_TYPE = %w(nsfw_on nsfw_off delete).freeze + + def save + case action + when 'nsfw_on', 'nsfw_off' + change_sensitive(action == 'nsfw_on') + when 'delete' + delete_statuses + end + end + + private + + def change_sensitive(sensitive) + media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id) + ApplicationRecord.transaction do + Status.where(id: media_attached_status_ids).find_each do |status| + status.update!(sensitive: sensitive) + end + end + true + rescue ActiveRecord::RecordInvalid + false + end + + def delete_statuses + Status.where(id: status_ids).find_each do |status| + RemovalWorker.perform_async(status.id) + end + true + end +end diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index d91ba9c78..5ad1fd6ee 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -53,11 +53,11 @@ %td= @account.followers_count %tr %th= t('admin.accounts.statuses') - %td= @account.statuses_count + %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id) %tr %th= t('admin.accounts.media_attachments') %td - = @account.media_attachments.count + = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true }) = surround '(', ')' do = number_to_human_size @account.media_attachments.sum('file_file_size') %tr diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 44486cb42..5747cc274 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -1,3 +1,6 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + - content_for :page_title do = t('admin.reports.report', id: @report.id) @@ -19,16 +22,27 @@ - unless @report.statuses.empty? %hr/ - - @report.statuses.each do |status| - .report-status - .activity-stream.activity-stream-headless - .entry= render 'stream_entries/simple_status', status: status - .report-status__actions - - unless status.media_attachments.empty? - = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do - = fa_icon status.sensitive? ? 'eye' : 'eye-slash' - = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') } do - = fa_icon 'trash' + = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f| + .batch-form-box + .batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} + = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' + .media-spoiler-toggle-buttons + .media-spoiler-show-button.button= t('admin.statuses.media.show') + .media-spoiler-hide-button.button= t('admin.statuses.media.hide') + - @report.statuses.each do |status| + .report-status{ data: { id: status.id } } + .batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .activity-stream.activity-stream-headless + .entry= render 'stream_entries/simple_status', status: status + .report-status__actions + - unless status.media_attachments.empty? + = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :put, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do + = fa_icon status.sensitive? ? 'eye' : 'eye-slash' + = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do + = fa_icon 'trash' %hr/ diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml new file mode 100644 index 000000000..fe2581527 --- /dev/null +++ b/app/views/admin/statuses/index.html.haml @@ -0,0 +1,47 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + +- content_for :page_title do + = t('admin.statuses.title') + +.back-link + = link_to admin_account_path(@account.id) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.statuses.back_to_account') + +.filters + .filter-subset + %strong= t('admin.statuses.media.title') + %ul + %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected' + %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected' + +- if @statuses.empty? + .accounts-grid + = render 'accounts/nothing_here' +- else + = form_for(@form, url: admin_account_statuses_path(@account.id)) do |f| + = hidden_field_tag :page, params[:page] + = hidden_field_tag :media, params[:media] + .batch-form-box + .batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} + = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' + .media-spoiler-toggle-buttons + .media-spoiler-show-button.button= t('admin.statuses.media.show') + .media-spoiler-hide-button.button= t('admin.statuses.media.hide') + - @statuses.each do |status| + .account-status{ data: { id: status.id } } + .batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .activity-stream.activity-stream-headless + .entry= render 'stream_entries/simple_status', status: status + .account-status__actions + - unless status.media_attachments.empty? + = link_to admin_account_status_path(@account.id, status, current_params.merge(status: { sensitive: !status.sensitive })), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do + = fa_icon status.sensitive? ? 'eye' : 'eye-slash' + = link_to admin_account_status_path(@account.id, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do + = fa_icon 'trash' + += paginate @statuses |