Add export for custom filters (#39085)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
Pia B. 2026-06-01 11:56:17 +02:00 committed by GitHub
parent 3af33aca47
commit f6c156197e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 148 additions and 7 deletions

View File

@ -19,15 +19,12 @@ module Settings::ExportControllerConcern
def send_export_file
respond_to do |format|
format.csv { send_data export_data, filename: export_filename }
format.csv { send_data export_data, filename: "#{controller_name}.csv" }
format.json { send_data export_data, filename: "#{controller_name}.json" }
end
end
def export_data
raise 'Override in controller'
end
def export_filename
"#{controller_name}.csv"
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Settings
module Exports
class CustomFiltersController < BaseController
include Settings::ExportControllerConcern
def index
send_export_file
end
private
def export_data
@export.to_custom_filters_json
end
end
end
end

View File

@ -55,6 +55,23 @@ class Export
end
end
def to_custom_filters_json
data_collection = { custom_filters: [] }
account.custom_filters.includes(:keywords, :statuses).order(:phrase).each do |filter|
keywords_attributes = filter.keywords.map { |k| { keyword: k.keyword, whole_word: k.whole_word } }
data_collection[:custom_filters] << {
title: filter.title,
expire_at: filter.expires_at,
context: filter.context,
action: filter.action,
keywords_attributes: keywords_attributes,
statuses: filter.statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s.status) },
}
end
JSON.generate(data_collection)
end
private
def to_csv(accounts)

View File

@ -6,6 +6,7 @@ class ExportSummary
delegate(
:blocking,
:bookmarks,
:custom_filters,
:domain_blocks,
:owned_lists,
:media_attachments,
@ -27,6 +28,10 @@ class ExportSummary
counts[:bookmarks].value
end
def total_custom_filters
counts[:custom_filters].value
end
def total_domain_blocks
counts[:domain_blocks].value
end
@ -61,6 +66,7 @@ class ExportSummary
{
blocks: account_blocking.async_count,
bookmarks: account_bookmarks.async_count,
custom_filters: account_custom_filters.async_count,
domain_blocks: account_domain_blocks.async_count,
owned_lists: account_owned_lists.async_count,
muting: account_muting.async_count,

View File

@ -40,6 +40,10 @@
%th= t('exports.bookmarks')
%td= number_with_delimiter @export_summary.total_bookmarks
%td= table_link_to 'download', t('exports.csv'), settings_exports_bookmarks_path(format: :csv)
%tr
%th= t('exports.custom_filters')
%td= number_with_delimiter @export_summary.total_custom_filters
%td= table_link_to 'download', t('exports.json'), settings_exports_custom_filters_path(format: :json)
%hr.spacer/

View File

@ -1569,7 +1569,9 @@ en:
blocks: You block
bookmarks: Bookmarks
csv: CSV
custom_filters: Filters
domain_blocks: Domain blocks
json: JSON
lists: Lists
mutes: You mute
storage: Media storage

View File

@ -30,6 +30,7 @@ namespace :settings do
resources :lists, only: :index
resources :domain_blocks, only: :index, controller: :blocked_domains
resources :bookmarks, only: :index
resources :custom_filters, only: :index, constraints: { format: :json }, controller: :custom_filters
end
resources :two_factor_authentication_methods, only: [:index] do

View File

@ -15,10 +15,13 @@ RSpec.describe Settings::ExportControllerConcern do
end
end
def sign_in_user
sign_in Fabricate(:user)
end
describe 'GET #index' do
it 'returns a csv of the exported data when signed in' do
user = Fabricate(:user)
sign_in user
sign_in_user
get :index, format: :csv
expect(response).to have_http_status(200)
@ -33,4 +36,26 @@ RSpec.describe Settings::ExportControllerConcern do
expect(response).to have_http_status(401)
end
end
context 'when with json format' do
it 'returns a json of the exported data when signed in' do
sign_in_user
get :index, format: :json
expect(response).to have_http_status(200)
expect(response.media_type).to eq 'application/json'
expect(response)
.to have_attachment('anonymous.json')
expect(response.body).to be_present
end
end
context 'when without format' do
it 'does not send exported data' do
sign_in_user
get :index
expect(response).to have_http_status(406)
end
end
end

View File

@ -52,6 +52,14 @@ RSpec.describe ExportSummary do
end
end
describe '#total_custom_filters' do
before { Fabricate.times(2, :custom_filter, account: account) }
it 'returns the total number of lists' do
expect(subject.total_custom_filters).to eq(2)
end
end
describe '#total_followers' do
before { target_accounts.each { |target_account| target_account.follow!(account) } }

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings / Exports / CustomFilters' do
describe 'GET /settings/exports/custom_filters' do
context 'with a signed in user who has custom_filters' do
let(:user) { Fabricate(:user) }
let(:filter) { Fabricate(:custom_filter, account: user.account, phrase: 'foo') }
let(:other_filter) { Fabricate(:custom_filter, account: user.account, phrase: 'bar') }
let!(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
let!(:filter_keyword) { Fabricate(:custom_filter_keyword, keyword: 'something', custom_filter: filter, whole_word: false) }
let!(:other_keyword) { Fabricate(:custom_filter_keyword, custom_filter: other_filter) }
let!(:other_filter_keyword) { Fabricate(:custom_filter_keyword, keyword: 'something', custom_filter: other_filter, whole_word: false) }
let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
let(:expected_response_body) do
{ 'custom_filters' => [
{
'title' => other_filter.phrase,
'expire_at' => nil,
'context' => other_filter.context,
'action' => other_filter.action,
'keywords_attributes' => [{
'keyword' => other_keyword.keyword,
'whole_word' => other_keyword.whole_word,
}, {
'keyword' => other_filter_keyword.keyword,
'whole_word' => other_filter_keyword.whole_word,
}],
'statuses' => [],
},
{
'title' => filter.phrase,
'expire_at' => nil,
'context' => filter.context,
'action' => filter.action,
'keywords_attributes' => [{
'keyword' => keyword.keyword,
'whole_word' => keyword.whole_word,
}, {
'keyword' => filter_keyword.keyword,
'whole_word' => filter_keyword.whole_word,
}],
'statuses' => [ActivityPub::TagManager.instance.uri_for(status_filter.status)],
},
] }
end
before do
sign_in user
end
it 'returns a JSON with the custom filters' do
get '/settings/exports/custom_filters.json'
expect(response).to have_http_status(200)
expect(response.content_type).to eq('application/json')
expect(response.parsed_body).to eq(expected_response_body)
end
end
end
end