parent
8387b3928e
commit
4d42a38954
@ -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 |
@ -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'); |
||||
}); |
||||
}); |
@ -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 |
@ -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 |
@ -0,0 +1,107 @@ |
||||
require 'rails_helper' |
||||
|
||||
describe Admin::StatusesController do |
||||
render_views |
||||
|
||||
let(:user) { Fabricate(:user, admin: true) } |
||||
let(:account) { Fabricate(:account) } |
||||
let!(:status) { Fabricate(:status, account: account) } |
||||
let(:media_attached_status) { Fabricate(:status, account: account, sensitive: !sensitive) } |
||||
let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: media_attached_status) } |
||||
let(:sensitive) { true } |
||||
|
||||
before do |
||||
sign_in user, scope: :user |
||||
end |
||||
|
||||
describe 'GET #index' do |
||||
it 'returns http success with no media' do |
||||
get :index, params: { account_id: account.id } |
||||
|
||||
statuses = assigns(:statuses).to_a |
||||
expect(statuses.size).to eq 2 |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
|
||||
it 'returns http success with media' do |
||||
get :index, params: { account_id: account.id , media: true } |
||||
|
||||
statuses = assigns(:statuses).to_a |
||||
expect(statuses.size).to eq 1 |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
end |
||||
|
||||
describe 'POST #create' do |
||||
subject do |
||||
-> { post :create, params: { account_id: account.id, form_status_batch: { action: action, status_ids: status_ids } } } |
||||
end |
||||
|
||||
let(:action) { 'nsfw_on' } |
||||
let(:status_ids) { [media_attached_status.id] } |
||||
|
||||
context 'updates sensitive column to true' do |
||||
it 'updates sensitive column' do |
||||
is_expected.to change { |
||||
media_attached_status.reload.sensitive |
||||
}.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'updates sensitive column to false' do |
||||
let(:action) { 'nsfw_off' } |
||||
let(:sensitive) { false } |
||||
|
||||
it 'updates sensitive column' do |
||||
is_expected.to change { |
||||
media_attached_status.reload.sensitive |
||||
}.from(true).to(false) |
||||
end |
||||
end |
||||
|
||||
it 'redirects to account statuses page' do |
||||
subject.call |
||||
expect(response).to redirect_to(admin_account_statuses_path(account.id)) |
||||
end |
||||
end |
||||
|
||||
describe 'PATCH #update' do |
||||
subject do |
||||
-> { patch :update, params: { account_id: account.id, id: media_attached_status, status: { sensitive: sensitive } } } |
||||
end |
||||
|
||||
context 'updates sensitive column to true' do |
||||
it 'updates sensitive column' do |
||||
is_expected.to change { |
||||
media_attached_status.reload.sensitive |
||||
}.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'updates sensitive column to false' do |
||||
let(:sensitive) { false } |
||||
|
||||
it 'updates sensitive column' do |
||||
is_expected.to change { |
||||
media_attached_status.reload.sensitive |
||||
}.from(true).to(false) |
||||
end |
||||
end |
||||
|
||||
it 'redirects to account statuses page' do |
||||
subject.call |
||||
expect(response).to redirect_to(admin_account_statuses_path(account.id)) |
||||
end |
||||
end |
||||
|
||||
describe 'DELETE #destroy' do |
||||
it 'removes a status' do |
||||
allow(RemovalWorker).to receive(:perform_async) |
||||
|
||||
delete :destroy, params: { account_id: account.id, id: status } |
||||
expect(response).to have_http_status(:success) |
||||
expect(RemovalWorker). |
||||
to have_received(:perform_async).with(status.id) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,52 @@ |
||||
require 'rails_helper' |
||||
|
||||
describe Form::StatusBatch do |
||||
let(:form) { Form::StatusBatch.new(action: action, status_ids: status_ids) } |
||||
let(:status) { Fabricate(:status) } |
||||
|
||||
describe 'with nsfw action' do |
||||
let(:status_ids) { [status.id, nonsensitive_status.id, sensitive_status.id] } |
||||
let(:nonsensitive_status) { Fabricate(:status, sensitive: false) } |
||||
let(:sensitive_status) { Fabricate(:status, sensitive: true) } |
||||
let!(:shown_media_attachment) { Fabricate(:media_attachment, status: nonsensitive_status) } |
||||
let!(:hidden_media_attachment) { Fabricate(:media_attachment, status: sensitive_status) } |
||||
|
||||
context 'nsfw_on' do |
||||
let(:action) { 'nsfw_on' } |
||||
|
||||
it { expect(form.save).to be true } |
||||
it { expect { form.save }.to change { nonsensitive_status.reload.sensitive }.from(false).to(true) } |
||||
it { expect { form.save }.not_to change { sensitive_status.reload.sensitive } } |
||||
it { expect { form.save }.not_to change { status.reload.sensitive } } |
||||
end |
||||
|
||||
context 'nsfw_off' do |
||||
let(:action) { 'nsfw_off' } |
||||
|
||||
it { expect(form.save).to be true } |
||||
it { expect { form.save }.to change { sensitive_status.reload.sensitive }.from(true).to(false) } |
||||
it { expect { form.save }.not_to change { nonsensitive_status.reload.sensitive } } |
||||
it { expect { form.save }.not_to change { status.reload.sensitive } } |
||||
end |
||||
end |
||||
|
||||
describe 'with delete action' do |
||||
let(:status_ids) { [status.id] } |
||||
let(:action) { 'delete' } |
||||
let!(:another_status) { Fabricate(:status) } |
||||
|
||||
before do |
||||
allow(RemovalWorker).to receive(:perform_async) |
||||
end |
||||
|
||||
it 'call RemovalWorker' do |
||||
form.save |
||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id) |
||||
end |
||||
|
||||
it 'do not call RemovalWorker' do |
||||
form.save |
||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id) |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue