Merge branch 'main' of https://github.com/glitch-soc/mastodon into main
commit
ecc8e19071
1055 changed files with 31110 additions and 15962 deletions
@ -0,0 +1,7 @@ |
|||||||
|
[production] |
||||||
|
defaults |
||||||
|
not IE 11 |
||||||
|
not dead |
||||||
|
|
||||||
|
[development] |
||||||
|
supports es6-module |
@ -0,0 +1,21 @@ |
|||||||
|
{ |
||||||
|
"problemMatcher": [ |
||||||
|
{ |
||||||
|
"owner": "stylelint", |
||||||
|
"pattern": [ |
||||||
|
{ |
||||||
|
"regexp": "^([^\\s].*)$", |
||||||
|
"file": 1 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"regexp": "^\\s+((\\d+):(\\d+))?\\s+(✖|×)\\s+(.*)\\s{2,}(.*)$", |
||||||
|
"line": 2, |
||||||
|
"column": 3, |
||||||
|
"message": 5, |
||||||
|
"code": 6, |
||||||
|
"loop": true |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
--- |
||||||
|
################################# |
||||||
|
################################# |
||||||
|
## Super Linter GitHub Actions ## |
||||||
|
################################# |
||||||
|
################################# |
||||||
|
name: Lint Code Base |
||||||
|
|
||||||
|
# |
||||||
|
# Documentation: |
||||||
|
# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions |
||||||
|
# |
||||||
|
|
||||||
|
############################# |
||||||
|
# Start the job on all push # |
||||||
|
############################# |
||||||
|
on: |
||||||
|
push: |
||||||
|
branches-ignore: [main] |
||||||
|
# Remove the line above to run when pushing to master |
||||||
|
pull_request: |
||||||
|
branches: [main] |
||||||
|
|
||||||
|
############### |
||||||
|
# Set the Job # |
||||||
|
############### |
||||||
|
permissions: |
||||||
|
checks: write |
||||||
|
contents: read |
||||||
|
pull-requests: write |
||||||
|
statuses: write |
||||||
|
|
||||||
|
jobs: |
||||||
|
build: |
||||||
|
# Name the Job |
||||||
|
name: Lint Code Base |
||||||
|
# Set the agent to run on |
||||||
|
runs-on: ubuntu-latest |
||||||
|
|
||||||
|
################## |
||||||
|
# Load all steps # |
||||||
|
################## |
||||||
|
steps: |
||||||
|
########################## |
||||||
|
# Checkout the code base # |
||||||
|
########################## |
||||||
|
- name: Checkout Code |
||||||
|
uses: actions/checkout@v3 |
||||||
|
with: |
||||||
|
# Full git history is needed to get a proper list of changed files within `super-linter` |
||||||
|
fetch-depth: 0 |
||||||
|
|
||||||
|
- name: Set-up Node.js |
||||||
|
uses: actions/setup-node@v3 |
||||||
|
with: |
||||||
|
node-version: 16.x |
||||||
|
cache: yarn |
||||||
|
- name: Intall dependencies |
||||||
|
run: yarn install --frozen-lockfile |
||||||
|
- name: Set-up RuboCop Problem Mathcher |
||||||
|
uses: r7kamura/rubocop-problem-matchers-action@v1 |
||||||
|
- name: Set-up Stylelint Problem Matcher |
||||||
|
uses: xt0rted/stylelint-problem-matcher@v1 |
||||||
|
# https://github.com/xt0rted/stylelint-problem-matcher/issues/360 |
||||||
|
- run: echo "::add-matcher::.github/stylelint-matcher.json" |
||||||
|
|
||||||
|
################################ |
||||||
|
# Run Linter against code base # |
||||||
|
################################ |
||||||
|
- name: Lint Code Base |
||||||
|
uses: github/super-linter@v4 |
||||||
|
env: |
||||||
|
CSS_FILE_NAME: stylelint.config.js |
||||||
|
DEFAULT_BRANCH: main |
||||||
|
NO_COLOR: 1 # https://github.com/xt0rted/stylelint-problem-matcher/issues/360 |
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
||||||
|
JAVASCRIPT_ES_CONFIG_FILE: .eslintrc.js |
||||||
|
LINTER_RULES_PATH: . |
||||||
|
RUBY_CONFIG_FILE: .rubocop.yml |
||||||
|
VALIDATE_ALL_CODEBASE: false |
||||||
|
VALIDATE_CSS: true |
||||||
|
VALIDATE_JAVASCRIPT_ES: true |
||||||
|
VALIDATE_RUBY: true |
@ -1,37 +0,0 @@ |
|||||||
# Linter Documentation: |
|
||||||
# https://github.com/sasstools/sass-lint/tree/v1.13.1/docs/options |
|
||||||
|
|
||||||
files: |
|
||||||
include: app/javascript/styles/**/*.scss |
|
||||||
ignore: |
|
||||||
- app/javascript/styles/mastodon/reset.scss |
|
||||||
|
|
||||||
rules: |
|
||||||
# Disallows |
|
||||||
no-color-literals: 0 |
|
||||||
no-css-comments: 0 |
|
||||||
no-duplicate-properties: 0 |
|
||||||
no-ids: 0 |
|
||||||
no-important: 0 |
|
||||||
no-mergeable-selectors: 0 |
|
||||||
no-misspelled-properties: 0 |
|
||||||
no-qualifying-elements: 0 |
|
||||||
no-transition-all: 0 |
|
||||||
no-vendor-prefixes: 0 |
|
||||||
|
|
||||||
# Nesting |
|
||||||
force-element-nesting: 0 |
|
||||||
force-attribute-nesting: 0 |
|
||||||
force-pseudo-nesting: 0 |
|
||||||
|
|
||||||
# Name Formats |
|
||||||
class-name-format: 0 |
|
||||||
leading-zero: 0 |
|
||||||
|
|
||||||
# Style Guide |
|
||||||
attribute-quotes: 0 |
|
||||||
hex-length: 0 |
|
||||||
indentation: 0 |
|
||||||
nesting-depth: 0 |
|
||||||
property-sort-order: 0 |
|
||||||
quotes: 0 |
|
@ -0,0 +1,60 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'csv' |
||||||
|
|
||||||
|
module Admin |
||||||
|
class ExportDomainAllowsController < BaseController |
||||||
|
include AdminExportControllerConcern |
||||||
|
|
||||||
|
before_action :set_dummy_import!, only: [:new] |
||||||
|
|
||||||
|
ROWS_PROCESSING_LIMIT = 20_000 |
||||||
|
|
||||||
|
def new |
||||||
|
authorize :domain_allow, :create? |
||||||
|
end |
||||||
|
|
||||||
|
def export |
||||||
|
authorize :instance, :index? |
||||||
|
send_export_file |
||||||
|
end |
||||||
|
|
||||||
|
def import |
||||||
|
authorize :domain_allow, :create? |
||||||
|
begin |
||||||
|
@import = Admin::Import.new(import_params) |
||||||
|
parse_import_data!(export_headers) |
||||||
|
|
||||||
|
@data.take(ROWS_PROCESSING_LIMIT).each do |row| |
||||||
|
domain = row['#domain'].strip |
||||||
|
next if DomainAllow.allowed?(domain) |
||||||
|
|
||||||
|
domain_allow = DomainAllow.new(domain: domain) |
||||||
|
log_action :create, domain_allow if domain_allow.save |
||||||
|
end |
||||||
|
flash[:notice] = I18n.t('admin.domain_allows.created_msg') |
||||||
|
rescue ActionController::ParameterMissing |
||||||
|
flash[:error] = I18n.t('admin.export_domain_allows.no_file') |
||||||
|
end |
||||||
|
redirect_to admin_instances_path |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def export_filename |
||||||
|
'domain_allows.csv' |
||||||
|
end |
||||||
|
|
||||||
|
def export_headers |
||||||
|
%w(#domain) |
||||||
|
end |
||||||
|
|
||||||
|
def export_data |
||||||
|
CSV.generate(headers: export_headers, write_headers: true) do |content| |
||||||
|
DomainAllow.allowed_domains.each do |instance| |
||||||
|
content << [instance.domain] |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,71 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'csv' |
||||||
|
|
||||||
|
module Admin |
||||||
|
class ExportDomainBlocksController < BaseController |
||||||
|
include AdminExportControllerConcern |
||||||
|
|
||||||
|
before_action :set_dummy_import!, only: [:new] |
||||||
|
|
||||||
|
ROWS_PROCESSING_LIMIT = 20_000 |
||||||
|
|
||||||
|
def new |
||||||
|
authorize :domain_block, :create? |
||||||
|
end |
||||||
|
|
||||||
|
def export |
||||||
|
authorize :instance, :index? |
||||||
|
send_export_file |
||||||
|
end |
||||||
|
|
||||||
|
def import |
||||||
|
authorize :domain_block, :create? |
||||||
|
|
||||||
|
@import = Admin::Import.new(import_params) |
||||||
|
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(ROWS_PROCESSING_LIMIT).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, |
||||||
|
private_comment: @global_private_comment, |
||||||
|
public_comment: row['#public_comment']&.strip, |
||||||
|
obfuscate: row['#obfuscate'].strip) |
||||||
|
|
||||||
|
domain_block if domain_block.valid? |
||||||
|
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) |
||||||
|
rescue ActionController::ParameterMissing |
||||||
|
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file') |
||||||
|
set_dummy_import! |
||||||
|
render :new |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def export_filename |
||||||
|
'domain_blocks.csv' |
||||||
|
end |
||||||
|
|
||||||
|
def export_headers |
||||||
|
%w(#domain #severity #reject_media #reject_reports #public_comment #obfuscate) |
||||||
|
end |
||||||
|
|
||||||
|
def export_data |
||||||
|
CSV.generate(headers: export_headers, write_headers: true) do |content| |
||||||
|
DomainBlock.with_user_facing_limitations.each do |instance| |
||||||
|
content << [instance.domain, instance.severity, instance.reject_media, instance.reject_reports, instance.public_comment, instance.obfuscate] |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -1,27 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
module Admin |
|
||||||
class SignInTokenAuthenticationsController < BaseController |
|
||||||
before_action :set_target_user |
|
||||||
|
|
||||||
def create |
|
||||||
authorize @user, :enable_sign_in_token_auth? |
|
||||||
@user.update(skip_sign_in_token: false) |
|
||||||
log_action :enable_sign_in_token_auth, @user |
|
||||||
redirect_to admin_account_path(@user.account_id) |
|
||||||
end |
|
||||||
|
|
||||||
def destroy |
|
||||||
authorize @user, :disable_sign_in_token_auth? |
|
||||||
@user.update(skip_sign_in_token: true) |
|
||||||
log_action :disable_sign_in_token_auth, @user |
|
||||||
redirect_to admin_account_path(@user.account_id) |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def set_target_user |
|
||||||
@user = User.find(params[:user_id]) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,19 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
module Admin |
||||||
|
class Webhooks::SecretsController < BaseController |
||||||
|
before_action :set_webhook |
||||||
|
|
||||||
|
def rotate |
||||||
|
authorize @webhook, :rotate_secret? |
||||||
|
@webhook.rotate_secret! |
||||||
|
redirect_to admin_webhook_path(@webhook) |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_webhook |
||||||
|
@webhook = Webhook.find(params[:webhook_id]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,77 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
module Admin |
||||||
|
class WebhooksController < BaseController |
||||||
|
before_action :set_webhook, except: [:index, :new, :create] |
||||||
|
|
||||||
|
def index |
||||||
|
authorize :webhook, :index? |
||||||
|
|
||||||
|
@webhooks = Webhook.page(params[:page]) |
||||||
|
end |
||||||
|
|
||||||
|
def new |
||||||
|
authorize :webhook, :create? |
||||||
|
|
||||||
|
@webhook = Webhook.new |
||||||
|
end |
||||||
|
|
||||||
|
def create |
||||||
|
authorize :webhook, :create? |
||||||
|
|
||||||
|
@webhook = Webhook.new(resource_params) |
||||||
|
|
||||||
|
if @webhook.save |
||||||
|
redirect_to admin_webhook_path(@webhook) |
||||||
|
else |
||||||
|
render :new |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
authorize @webhook, :show? |
||||||
|
end |
||||||
|
|
||||||
|
def edit |
||||||
|
authorize @webhook, :update? |
||||||
|
end |
||||||
|
|
||||||
|
def update |
||||||
|
authorize @webhook, :update? |
||||||
|
|
||||||
|
if @webhook.update(resource_params) |
||||||
|
redirect_to admin_webhook_path(@webhook) |
||||||
|
else |
||||||
|
render :show |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def enable |
||||||
|
authorize @webhook, :enable? |
||||||
|
@webhook.enable! |
||||||
|
redirect_to admin_webhook_path(@webhook) |
||||||
|
end |
||||||
|
|
||||||
|
def disable |
||||||
|
authorize @webhook, :disable? |
||||||
|
@webhook.disable! |
||||||
|
redirect_to admin_webhook_path(@webhook) |
||||||
|
end |
||||||
|
|
||||||
|
def destroy |
||||||
|
authorize @webhook, :destroy? |
||||||
|
@webhook.destroy! |
||||||
|
redirect_to admin_webhooks_path |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_webhook |
||||||
|
@webhook = Webhook.find(params[:id]) |
||||||
|
end |
||||||
|
|
||||||
|
def resource_params |
||||||
|
params.require(:webhook).permit(:url, events: []) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,95 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class Api::V1::Admin::DomainAllowsController < Api::BaseController |
||||||
|
include Authorization |
||||||
|
include AccountableConcern |
||||||
|
|
||||||
|
LIMIT = 100 |
||||||
|
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:domain_allows' }, only: [:index, :show] |
||||||
|
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:domain_allows' }, except: [:index, :show] |
||||||
|
before_action :require_staff! |
||||||
|
before_action :set_domain_allows, only: :index |
||||||
|
before_action :set_domain_allow, only: [:show, :destroy] |
||||||
|
|
||||||
|
after_action :insert_pagination_headers, only: :index |
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(limit).freeze |
||||||
|
|
||||||
|
def create |
||||||
|
authorize :domain_allow, :create? |
||||||
|
|
||||||
|
@domain_allow = DomainAllow.find_by(resource_params) |
||||||
|
|
||||||
|
if @domain_allow.nil? |
||||||
|
@domain_allow = DomainAllow.create!(resource_params) |
||||||
|
log_action :create, @domain_allow |
||||||
|
end |
||||||
|
|
||||||
|
render json: @domain_allow, serializer: REST::Admin::DomainAllowSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def index |
||||||
|
authorize :domain_allow, :index? |
||||||
|
render json: @domain_allows, each_serializer: REST::Admin::DomainAllowSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
authorize @domain_allow, :show? |
||||||
|
render json: @domain_allow, serializer: REST::Admin::DomainAllowSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def destroy |
||||||
|
authorize @domain_allow, :destroy? |
||||||
|
UnallowDomainService.new.call(@domain_allow) |
||||||
|
log_action :destroy, @domain_allow |
||||||
|
render json: @domain_allow, serializer: REST::Admin::DomainAllowSerializer |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_domain_allows |
||||||
|
@domain_allows = filtered_domain_allows.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |
||||||
|
end |
||||||
|
|
||||||
|
def set_domain_allow |
||||||
|
@domain_allow = DomainAllow.find(params[:id]) |
||||||
|
end |
||||||
|
|
||||||
|
def filtered_domain_allows |
||||||
|
# TODO: no filtering yet |
||||||
|
DomainAllow.all |
||||||
|
end |
||||||
|
|
||||||
|
def insert_pagination_headers |
||||||
|
set_pagination_headers(next_path, prev_path) |
||||||
|
end |
||||||
|
|
||||||
|
def next_path |
||||||
|
api_v1_admin_domain_allows_url(pagination_params(max_id: pagination_max_id)) if records_continue? |
||||||
|
end |
||||||
|
|
||||||
|
def prev_path |
||||||
|
api_v1_admin_domain_allows_url(pagination_params(min_id: pagination_since_id)) unless @domain_allows.empty? |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_max_id |
||||||
|
@domain_allows.last.id |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_since_id |
||||||
|
@domain_allows.first.id |
||||||
|
end |
||||||
|
|
||||||
|
def records_continue? |
||||||
|
@domain_allows.size == limit_param(LIMIT) |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_params(core_params) |
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params) |
||||||
|
end |
||||||
|
|
||||||
|
def resource_params |
||||||
|
params.permit(:domain) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,109 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class Api::V1::Admin::DomainBlocksController < Api::BaseController |
||||||
|
include Authorization |
||||||
|
include AccountableConcern |
||||||
|
|
||||||
|
LIMIT = 100 |
||||||
|
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:domain_blocks' }, only: [:index, :show] |
||||||
|
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:domain_blocks' }, except: [:index, :show] |
||||||
|
before_action :require_staff! |
||||||
|
before_action :set_domain_blocks, only: :index |
||||||
|
before_action :set_domain_block, only: [:show, :update, :destroy] |
||||||
|
|
||||||
|
after_action :insert_pagination_headers, only: :index |
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(limit).freeze |
||||||
|
|
||||||
|
def create |
||||||
|
authorize :domain_block, :create? |
||||||
|
|
||||||
|
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil |
||||||
|
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if existing_domain_block.present? |
||||||
|
|
||||||
|
@domain_block = DomainBlock.create!(resource_params) |
||||||
|
DomainBlockWorker.perform_async(@domain_block.id) |
||||||
|
log_action :create, @domain_block |
||||||
|
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def index |
||||||
|
authorize :domain_block, :index? |
||||||
|
render json: @domain_blocks, each_serializer: REST::Admin::DomainBlockSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
authorize @domain_block, :show? |
||||||
|
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def update |
||||||
|
authorize @domain_block, :update? |
||||||
|
|
||||||
|
@domain_block.update(domain_block_params) |
||||||
|
severity_changed = @domain_block.severity_changed? |
||||||
|
@domain_block.save! |
||||||
|
DomainBlockWorker.perform_async(@domain_block.id, severity_changed) |
||||||
|
log_action :update, @domain_block |
||||||
|
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def destroy |
||||||
|
authorize @domain_block, :destroy? |
||||||
|
UnblockDomainService.new.call(@domain_block) |
||||||
|
log_action :destroy, @domain_block |
||||||
|
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_domain_blocks |
||||||
|
@domain_blocks = filtered_domain_blocks.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |
||||||
|
end |
||||||
|
|
||||||
|
def set_domain_block |
||||||
|
@domain_block = DomainBlock.find(params[:id]) |
||||||
|
end |
||||||
|
|
||||||
|
def filtered_domain_blocks |
||||||
|
# TODO: no filtering yet |
||||||
|
DomainBlock.all |
||||||
|
end |
||||||
|
|
||||||
|
def domain_block_params |
||||||
|
params.permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) |
||||||
|
end |
||||||
|
|
||||||
|
def insert_pagination_headers |
||||||
|
set_pagination_headers(next_path, prev_path) |
||||||
|
end |
||||||
|
|
||||||
|
def next_path |
||||||
|
api_v1_admin_domain_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue? |
||||||
|
end |
||||||
|
|
||||||
|
def prev_path |
||||||
|
api_v1_admin_domain_blocks_url(pagination_params(min_id: pagination_since_id)) unless @domain_blocks.empty? |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_max_id |
||||||
|
@domain_blocks.last.id |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_since_id |
||||||
|
@domain_blocks.first.id |
||||||
|
end |
||||||
|
|
||||||
|
def records_continue? |
||||||
|
@domain_blocks.size == limit_param(LIMIT) |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_params(core_params) |
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params) |
||||||
|
end |
||||||
|
|
||||||
|
def resource_params |
||||||
|
params.permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,50 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class Api::V1::Filters::KeywordsController < Api::BaseController |
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show] |
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show] |
||||||
|
before_action :require_user! |
||||||
|
|
||||||
|
before_action :set_keywords, only: :index |
||||||
|
before_action :set_keyword, only: [:show, :update, :destroy] |
||||||
|
|
||||||
|
def index |
||||||
|
render json: @keywords, each_serializer: REST::FilterKeywordSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def create |
||||||
|
@keyword = current_account.custom_filters.find(params[:filter_id]).keywords.create!(resource_params) |
||||||
|
|
||||||
|
render json: @keyword, serializer: REST::FilterKeywordSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
render json: @keyword, serializer: REST::FilterKeywordSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def update |
||||||
|
@keyword.update!(resource_params) |
||||||
|
|
||||||
|
render json: @keyword, serializer: REST::FilterKeywordSerializer |
||||||
|
end |
||||||
|
|
||||||
|
def destroy |
||||||
|
@keyword.destroy! |
||||||
|
render_empty |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_keywords |
||||||
|
filter = current_account.custom_filters.includes(:keywords).find(params[:filter_id]) |
||||||
|
@keywords = filter.keywords |
||||||
|
end |
||||||
|
|
||||||
|
def set_keyword |
||||||
|
@keyword = CustomFilterKeyword.includes(:custom_filter).where(custom_filter: { account: current_account }).find(params[:id]) |
||||||
|
end |
||||||
|
|
||||||
|
def resource_params |
||||||
|
params.permit(:keyword, :whole_word) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,31 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController |
||||||
|
FILTER_PARAMS = %i( |
||||||
|
origin |
||||||
|
status |
||||||
|
permissions |
||||||
|
username |
||||||
|
by_domain |
||||||
|
display_name |
||||||
|
email |
||||||
|
ip |
||||||
|
invited_by |
||||||
|
).freeze |
||||||
|
|
||||||
|
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def filtered_accounts |
||||||
|
AccountFilter.new(filter_params).results |
||||||
|
end |
||||||
|
|
||||||
|
def filter_params |
||||||
|
params.permit(*FILTER_PARAMS) |
||||||
|
end |
||||||
|
|
||||||
|
def pagination_params(core_params) |
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,48 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class Api::V2::FiltersController < Api::BaseController |
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show] |
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show] |
||||||
|
before_action :require_user! |
||||||
|
before_action :set_filters, only: :index |
||||||
|
before_action :set_filter, only: [:show, :update, :destroy] |
||||||
|
|
||||||
|
def index |
||||||
|
render json: @filters, each_serializer: REST::FilterSerializer, rules_requested: true |
||||||
|
end |
||||||
|
|
||||||
|
def create |
||||||
|
@filter = current_account.custom_filters.create!(resource_params) |
||||||
|
|
||||||
|
render json: @filter, serializer: REST::FilterSerializer, rules_requested: true |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
render json: @filter, serializer: REST::FilterSerializer, rules_requested: true |
||||||
|
end |
||||||
|
|
||||||
|
def update |
||||||
|
@filter.update!(resource_params) |
||||||
|
|
||||||
|
render json: @filter, serializer: REST::FilterSerializer, rules_requested: true |
||||||
|
end |
||||||
|
|
||||||
|
def destroy |
||||||
|
@filter.destroy! |
||||||
|
render_empty |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_filters |
||||||
|
@filters = current_account.custom_filters.includes(:keywords) |
||||||
|
end |
||||||
|
|
||||||
|
def set_filter |
||||||
|
@filter = current_account.custom_filters.find(params[:id]) |
||||||
|
end |
||||||
|
|
||||||
|
def resource_params |
||||||
|
params.permit(:title, :expires_in, :filter_action, context: [], keywords_attributes: [:id, :keyword, :whole_word, :_destroy]) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,39 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
module AdminExportControllerConcern |
||||||
|
extend ActiveSupport::Concern |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def send_export_file |
||||||
|
respond_to do |format| |
||||||
|
format.csv { send_data export_data, filename: export_filename } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def export_data |
||||||
|
raise 'Override in controller' |
||||||
|
end |
||||||
|
|
||||||
|
def export_filename |
||||||
|
raise 'Override in controller' |
||||||
|
end |
||||||
|
|
||||||
|
def set_dummy_import! |
||||||
|
@import = Admin::Import.new |
||||||
|
end |
||||||
|
|
||||||
|
def import_params |
||||||
|
params.require(:admin_import).permit(:data) |
||||||
|
end |
||||||
|
|
||||||
|
def import_data |
||||||
|
Paperclip.io_adapters.for(@import.data).read |
||||||
|
end |
||||||
|
|
||||||
|
def parse_import_data!(default_headers) |
||||||
|
data = CSV.parse(import_data, headers: true) |
||||||
|
data = CSV.parse(import_data, headers: default_headers) unless data.headers&.first&.strip&.include?(default_headers[0]) |
||||||
|
@data = data.reject(&:blank?) |
||||||
|
end |
||||||
|
end |
@ -1,57 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
module SignInTokenAuthenticationConcern |
|
||||||
extend ActiveSupport::Concern |
|
||||||
|
|
||||||
included do |
|
||||||
prepend_before_action :authenticate_with_sign_in_token, if: :sign_in_token_required?, only: [:create] |
|
||||||
end |
|
||||||
|
|
||||||
def sign_in_token_required? |
|
||||||
find_user&.suspicious_sign_in?(request.remote_ip) |
|
||||||
end |
|
||||||
|
|
||||||
def valid_sign_in_token_attempt?(user) |
|
||||||
Devise.secure_compare(user.sign_in_token, user_params[:sign_in_token_attempt]) |
|
||||||
end |
|
||||||
|
|
||||||
def authenticate_with_sign_in_token |
|
||||||
if user_params[:email].present? |
|
||||||
user = self.resource = find_user_from_params |
|
||||||
prompt_for_sign_in_token(user) if user&.external_or_valid_password?(user_params[:password]) |
|
||||||
elsif session[:attempt_user_id] |
|
||||||
user = self.resource = User.find_by(id: session[:attempt_user_id]) |
|
||||||
return if user.nil? |
|
||||||
|
|
||||||
if session[:attempt_user_updated_at] != user.updated_at.to_s |
|
||||||
restart_session |
|
||||||
elsif user_params.key?(:sign_in_token_attempt) |
|
||||||
authenticate_with_sign_in_token_attempt(user) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def authenticate_with_sign_in_token_attempt(user) |
|
||||||
if valid_sign_in_token_attempt?(user) |
|
||||||
on_authentication_success(user, :sign_in_token) |
|
||||||
else |
|
||||||
on_authentication_failure(user, :sign_in_token, :invalid_sign_in_token) |
|
||||||
flash.now[:alert] = I18n.t('users.invalid_sign_in_token') |
|
||||||
prompt_for_sign_in_token(user) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def prompt_for_sign_in_token(user) |
|
||||||
if user.sign_in_token_expired? |
|
||||||
user.generate_sign_in_token && user.save |
|
||||||
UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later! |
|
||||||
end |
|
||||||
|
|
||||||
set_attempt_session(user) |
|
||||||
use_pack 'auth' |
|
||||||
|
|
||||||
@body_classes = 'lighter' |
|
||||||
|
|
||||||
set_locale { render :sign_in_token } |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,37 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
module BrandingHelper |
||||||
|
def logo_as_symbol(version = :icon) |
||||||
|
case version |
||||||
|
when :icon |
||||||
|
_logo_as_symbol_icon |
||||||
|
when :wordmark |
||||||
|
_logo_as_symbol_wordmark |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def _logo_as_symbol_wordmark |
||||||
|
content_tag(:svg, tag(:use, href: '#logo-symbol-wordmark'), viewBox: '0 0 261 66', class: 'logo logo--wordmark') |
||||||
|
end |
||||||
|
|
||||||
|
def _logo_as_symbol_icon |
||||||
|
content_tag(:svg, tag(:use, href: '#logo-symbol-icon'), viewBox: '0 0 79 79', class: 'logo logo--icon') |
||||||
|
end |
||||||
|
|
||||||
|
def render_logo |
||||||
|
image_pack_tag('logo.svg', alt: 'Mastodon', class: 'logo logo--icon') |
||||||
|
end |
||||||
|
|
||||||
|
def render_symbol(version = :icon) |
||||||
|
path = begin |
||||||
|
case version |
||||||
|
when :icon |
||||||
|
'logo-symbol-icon.svg' |
||||||
|
when :wordmark |
||||||
|
'logo-symbol-wordmark.svg' |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
render(file: Rails.root.join('app', 'javascript', 'images', path)).html_safe # rubocop:disable Rails/OutputSafety |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,54 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
module FormattingHelper |
||||||
|
def html_aware_format(text, local, options = {}) |
||||||
|
HtmlAwareFormatter.new(text, local, options).to_s |
||||||
|
end |
||||||
|
|
||||||
|
def linkify(text, options = {}) |
||||||
|
TextFormatter.new(text, options).to_s |
||||||
|
end |
||||||
|
|
||||||
|
def extract_status_plain_text(status) |
||||||
|
PlainTextFormatter.new(status.text, status.local?).to_s |
||||||
|
end |
||||||
|
module_function :extract_status_plain_text |
||||||
|
|
||||||
|
def status_content_format(status) |
||||||
|
html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []), content_type: status.content_type) |
||||||
|
end |
||||||
|
|
||||||
|
def rss_status_content_format(status) |
||||||
|
html = status_content_format(status) |
||||||
|
|
||||||
|
before_html = begin |
||||||
|
if status.spoiler_text? |
||||||
|
"<p><strong>#{I18n.t('rss.content_warning', locale: available_locale_or_nil(status.language) || I18n.default_locale)}</strong> #{h(status.spoiler_text)}</p><hr />" |
||||||
|
else |
||||||
|
'' |
||||||
|
end |
||||||
|
end.html_safe # rubocop:disable Rails/OutputSafety |
||||||
|
|
||||||
|
after_html = begin |
||||||
|
if status.preloadable_poll |
||||||
|
"<p>#{status.preloadable_poll.options.map { |o| "<input type=#{status.preloadable_poll.multiple? ? 'checkbox' : 'radio'} disabled /> #{h(o)}" }.join('<br />')}</p>" |
||||||
|
else |
||||||
|
'' |
||||||
|
end |
||||||
|
end.html_safe # rubocop:disable Rails/OutputSafety |
||||||
|
|
||||||
|
prerender_custom_emojis( |
||||||
|
safe_join([before_html, html, after_html]), |
||||||
|
status.emojis, |
||||||
|
style: 'width: 1.1em; height: 1.1em; object-fit: contain; vertical-align: middle; margin: -.2ex .15em .2ex' |
||||||
|
).to_str |
||||||
|
end |
||||||
|
|
||||||
|
def account_bio_format(account) |
||||||
|
html_aware_format(account.note, account.local?) |
||||||
|
end |
||||||
|
|
||||||
|
def account_field_value_format(field, with_rel_me: true) |
||||||
|
html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false) |
||||||
|
end |
||||||
|
end |
@ -1,2 +0,0 @@ |
|||||||
module Settings::KeywordMutesHelper |
|
||||||
end |
|
@ -1 +1,3 @@ |
|||||||
import 'styles/mailer.scss'; |
require('../styles/mailer.scss'); |
||||||
|
|
||||||
|
require.context('../icons'); |
||||||
|
@ -0,0 +1,12 @@ |
|||||||
|
import { saveSettings } from './settings'; |
||||||
|
|
||||||
|
export const LANGUAGE_USE = 'LANGUAGE_USE'; |
||||||
|
|
||||||
|
export const useLanguage = language => dispatch => { |
||||||
|
dispatch({ |
||||||
|
type: LANGUAGE_USE, |
||||||
|
language, |
||||||
|
}); |
||||||
|
|
||||||
|
dispatch(saveSettings()); |
||||||
|
}; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue