From fcc4c9b34a6ab771c9cef6673e817866773e12d0 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 18 Jan 2023 16:20:52 +0100 Subject: Change domain block CSV parsing to be more robust and handle more lists (#21470) * Change domain block CSV parsing to be more robust and handle more lists * Add some tests * Improve domain block import validation and reporting --- .../admin/export_domain_allows_controller.rb | 4 +--- .../admin/export_domain_blocks_controller.rb | 24 ++++++++++++++-------- .../concerns/admin_export_controller_concern.rb | 10 --------- 3 files changed, 16 insertions(+), 22 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/admin/export_domain_allows_controller.rb b/app/controllers/admin/export_domain_allows_controller.rb index 57fb12c62..adfc39da2 100644 --- a/app/controllers/admin/export_domain_allows_controller.rb +++ b/app/controllers/admin/export_domain_allows_controller.rb @@ -23,9 +23,7 @@ module Admin @import = Admin::Import.new(import_params) return render :new unless @import.validate - parse_import_data!(export_headers) - - @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).each do |row| + @import.csv_rows.each do |row| domain = row['#domain'].strip next if DomainAllow.allowed?(domain) diff --git a/app/controllers/admin/export_domain_blocks_controller.rb b/app/controllers/admin/export_domain_blocks_controller.rb index fb0cd05d2..816422d4f 100644 --- a/app/controllers/admin/export_domain_blocks_controller.rb +++ b/app/controllers/admin/export_domain_blocks_controller.rb @@ -23,24 +23,30 @@ module Admin @import = Admin::Import.new(import_params) return render :new unless @import.validate - parse_import_data!(export_headers) - @global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc)) @form = Form::DomainBlockBatch.new - @domain_blocks = @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).filter_map do |row| + @domain_blocks = @import.csv_rows.filter_map do |row| domain = row['#domain'].strip next if DomainBlock.rule_for(domain).present? domain_block = DomainBlock.new(domain: domain, - severity: row['#severity'].strip, - reject_media: row['#reject_media'].strip, - reject_reports: row['#reject_reports'].strip, + severity: row.fetch('#severity', :suspend), + reject_media: row.fetch('#reject_media', false), + reject_reports: row.fetch('#reject_reports', false), private_comment: @global_private_comment, - public_comment: row['#public_comment']&.strip, - obfuscate: row['#obfuscate'].strip) + public_comment: row['#public_comment'], + obfuscate: row.fetch('#obfuscate', false)) + + if domain_block.invalid? + flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: domain_block.errors.full_messages.join(', ')) + next + end - domain_block if domain_block.valid? + domain_block + rescue ArgumentError => e + flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: e.message) + next end @warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain) diff --git a/app/controllers/concerns/admin_export_controller_concern.rb b/app/controllers/concerns/admin_export_controller_concern.rb index b40c76557..4ac48a04b 100644 --- a/app/controllers/concerns/admin_export_controller_concern.rb +++ b/app/controllers/concerns/admin_export_controller_concern.rb @@ -26,14 +26,4 @@ module AdminExportControllerConcern def import_params params.require(:admin_import).permit(:data) end - - def import_data_path - params[:admin_import][:data].path - end - - def parse_import_data!(default_headers) - data = CSV.read(import_data_path, headers: true, encoding: 'UTF-8') - data = CSV.read(import_data_path, headers: default_headers, encoding: 'UTF-8') unless data.headers&.first&.strip&.include?(default_headers[0]) - @data = data.reject(&:blank?) - end end -- cgit From b034dc42be2250f9b754fb88c9163b62d41f78f5 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 18 Jan 2023 16:28:18 +0100 Subject: Fix /api/v1/admin/trends/tags using wrong serializer (#18943) * Fix /api/v1/admin/trends/tags using wrong serializer Fix regression from #18641 * Only use `REST::Admin::TagSerializer` when the user can `manage_taxonomies` * Fix admin trending hashtag component to not link if `id` is unknown --- app/controllers/api/v1/admin/trends/tags_controller.rb | 8 ++++++++ app/javascript/mastodon/components/admin/Trends.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'app/controllers') diff --git a/app/controllers/api/v1/admin/trends/tags_controller.rb b/app/controllers/api/v1/admin/trends/tags_controller.rb index f3c0c4b6b..e77df3021 100644 --- a/app/controllers/api/v1/admin/trends/tags_controller.rb +++ b/app/controllers/api/v1/admin/trends/tags_controller.rb @@ -3,6 +3,14 @@ class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController before_action -> { authorize_if_got_token! :'admin:read' } + def index + if current_user&.can?(:manage_taxonomies) + render json: @tags, each_serializer: REST::Admin::TagSerializer + else + super + end + end + private def enabled? diff --git a/app/javascript/mastodon/components/admin/Trends.js b/app/javascript/mastodon/components/admin/Trends.js index 9530c2a5b..d01b8437e 100644 --- a/app/javascript/mastodon/components/admin/Trends.js +++ b/app/javascript/mastodon/components/admin/Trends.js @@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent { day.uses)} -- cgit From 4b92e59f4fea4486ee6e5af7421e7945d5f7f998 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 18 Jan 2023 16:33:55 +0100 Subject: Add support for editing media description and focus point of already-posted statuses (#20878) * Add backend support for editing media attachments of existing posts * Allow editing media attachments of already-posted toots * Add tests --- app/controllers/api/v1/statuses_controller.rb | 7 ++++ app/javascript/mastodon/actions/compose.js | 46 +++++++++++++++++++--- .../mastodon/features/compose/components/upload.js | 4 +- .../features/ui/components/focal_point_modal.js | 2 +- app/javascript/mastodon/reducers/compose.js | 2 +- app/services/update_status_service.rb | 11 +++++- spec/services/update_status_service_spec.rb | 22 +++++++++++ 7 files changed, 83 insertions(+), 11 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 6290a1746..9a8c0c161 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -79,6 +79,7 @@ class Api::V1::StatusesController < Api::BaseController current_account.id, text: status_params[:status], media_ids: status_params[:media_ids], + media_attributes: status_params[:media_attributes], sensitive: status_params[:sensitive], language: status_params[:language], spoiler_text: status_params[:spoiler_text], @@ -128,6 +129,12 @@ class Api::V1::StatusesController < Api::BaseController :language, :scheduled_at, media_ids: [], + media_attributes: [ + :id, + :thumbnail, + :description, + :focus, + ], poll: [ :multiple, :hide_totals, diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 531a5eb2b..72e592935 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -160,6 +160,18 @@ export function submitCompose(routerHistory) { dispatch(submitComposeRequest()); + // If we're editing a post with media attachments, those have not + // necessarily been changed on the server. Do it now in the same + // API call. + let media_attributes; + if (statusId !== null) { + media_attributes = media.map(item => ({ + id: item.get('id'), + description: item.get('description'), + focus: item.get('focus'), + })); + } + api(getState).request({ url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`, method: statusId === null ? 'post' : 'put', @@ -167,6 +179,7 @@ export function submitCompose(routerHistory) { status, in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), media_ids: media.map(item => item.get('id')), + media_attributes, sensitive: getState().getIn(['compose', 'sensitive']), spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '', visibility: getState().getIn(['compose', 'privacy']), @@ -375,11 +388,31 @@ export function changeUploadCompose(id, params) { return (dispatch, getState) => { dispatch(changeUploadComposeRequest()); - api(getState).put(`/api/v1/media/${id}`, params).then(response => { - dispatch(changeUploadComposeSuccess(response.data)); - }).catch(error => { - dispatch(changeUploadComposeFail(id, error)); - }); + let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id); + + // Editing already-attached media is deferred to editing the post itself. + // For simplicity's sake, fake an API reply. + if (media && !media.get('unattached')) { + let { description, focus } = params; + const data = media.toJS(); + + if (description) { + data.description = description; + } + + if (focus) { + focus = focus.split(','); + data.meta = { focus: { x: parseFloat(focus[0]), y: parseFloat(focus[1]) } }; + } + + dispatch(changeUploadComposeSuccess(data, true)); + } else { + api(getState).put(`/api/v1/media/${id}`, params).then(response => { + dispatch(changeUploadComposeSuccess(response.data, false)); + }).catch(error => { + dispatch(changeUploadComposeFail(id, error)); + }); + } }; } @@ -390,10 +423,11 @@ export function changeUploadComposeRequest() { }; } -export function changeUploadComposeSuccess(media) { +export function changeUploadComposeSuccess(media, attached) { return { type: COMPOSE_UPLOAD_CHANGE_SUCCESS, media: media, + attached: attached, skipLoading: true, }; } diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index b08307ade..af06ce1bf 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -43,10 +43,10 @@ export default class Upload extends ImmutablePureComponent {
- {!!media.get('unattached') && ()} +
- {(media.get('description') || '').length === 0 && !!media.get('unattached') && ( + {(media.get('description') || '').length === 0 && (
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js index 479f4abd2..b9dbd9390 100644 --- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js +++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js @@ -320,7 +320,7 @@ class FocalPointModal extends ImmutablePureComponent { -