Merge pull request #1995 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
0194bd33fe
25 changed files with 479 additions and 63 deletions
2
.github/workflows/check-i18n.yml
vendored
2
.github/workflows/check-i18n.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
|||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.0'
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Check locale file normalization
|
||||
run: bundle exec i18n-tasks check-normalized
|
||||
|
|
2
.github/workflows/linter.yml
vendored
2
.github/workflows/linter.yml
vendored
|
@ -53,7 +53,7 @@ jobs:
|
|||
- name: Set-up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version-file: .nvmrc
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
|
71
Vagrantfile
vendored
71
Vagrantfile
vendored
|
@ -3,16 +3,14 @@
|
|||
|
||||
ENV["PORT"] ||= "3000"
|
||||
|
||||
$provision = <<SCRIPT
|
||||
|
||||
cd /vagrant # This is where the host folder/repo is mounted
|
||||
$provisionA = <<SCRIPT
|
||||
|
||||
# Add the yarn repo + yarn repo keys
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||
|
||||
# Add repo for NodeJS
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
|
||||
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
|
||||
|
||||
# Add firewall rule to redirect 80 to PORT and save
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
||||
|
@ -33,32 +31,56 @@ sudo apt-get install \
|
|||
redis-tools \
|
||||
postgresql \
|
||||
postgresql-contrib \
|
||||
yarn \
|
||||
libicu-dev \
|
||||
libidn11-dev \
|
||||
libreadline-dev \
|
||||
libpam0g-dev \
|
||||
libreadline6-dev \
|
||||
autoconf \
|
||||
bison \
|
||||
build-essential \
|
||||
ffmpeg \
|
||||
file \
|
||||
gcc \
|
||||
libffi-dev \
|
||||
libgdbm-dev \
|
||||
libjemalloc-dev \
|
||||
libncurses5-dev \
|
||||
libprotobuf-dev \
|
||||
libssl-dev \
|
||||
libyaml-dev \
|
||||
pkg-config \
|
||||
protobuf-compiler \
|
||||
zlib1g-dev \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
read RUBY_VERSION < .ruby-version
|
||||
sudo apt-add-repository -y ppa:rael-gc/rvm
|
||||
sudo apt-get install rvm -y
|
||||
|
||||
curl -sSL https://rvm.io/mpapis.asc | gpg --import
|
||||
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import
|
||||
sudo usermod -a -G rvm $USER
|
||||
|
||||
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
|
||||
source /home/vagrant/.rvm/scripts/rvm
|
||||
SCRIPT
|
||||
|
||||
$provisionB = <<SCRIPT
|
||||
|
||||
source "/etc/profile.d/rvm.sh"
|
||||
|
||||
# Install Ruby
|
||||
rvm reinstall ruby-$RUBY_VERSION --disable-binary
|
||||
read RUBY_VERSION < /vagrant/.ruby-version
|
||||
rvm install ruby-$RUBY_VERSION --disable-binary
|
||||
|
||||
# Configure database
|
||||
sudo -u postgres createuser -U postgres vagrant -s
|
||||
sudo -u postgres createdb -U postgres mastodon_development
|
||||
|
||||
# Install gems and node modules
|
||||
cd /vagrant # This is where the host folder/repo is mounted
|
||||
|
||||
# Install gems
|
||||
gem install bundler foreman
|
||||
bundle install
|
||||
|
||||
# Install node modules
|
||||
sudo corepack enable
|
||||
yarn set version classic
|
||||
yarn install
|
||||
|
||||
# Build Mastodon
|
||||
|
@ -72,18 +94,11 @@ echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
|
|||
|
||||
SCRIPT
|
||||
|
||||
$start = <<SCRIPT
|
||||
|
||||
echo 'To start server'
|
||||
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"'
|
||||
|
||||
SCRIPT
|
||||
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
config.vm.box = "ubuntu/focal64"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
|
@ -100,7 +115,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
# Use "virtio" network interfaces for better performance.
|
||||
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
|
||||
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
||||
|
||||
end
|
||||
|
||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||
|
@ -118,7 +132,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
end
|
||||
|
||||
if config.vm.networks.any? { |type, options| type == :private_network }
|
||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp', 'actimeo=1']
|
||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1']
|
||||
else
|
||||
config.vm.synced_folder ".", "/vagrant"
|
||||
end
|
||||
|
@ -129,9 +143,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
||||
|
||||
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
||||
config.vm.provision :shell, inline: $provision, privileged: false
|
||||
config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true
|
||||
config.vm.provision :shell, inline: $provisionB, privileged: false
|
||||
|
||||
# Start up script, runs on every 'vagrant up'
|
||||
config.vm.provision :shell, inline: $start, run: 'always', privileged: false
|
||||
config.vm.post_up_message = <<MESSAGE
|
||||
To start server
|
||||
$ vagrant ssh -c "cd /vagrant && foreman start"
|
||||
MESSAGE
|
||||
|
||||
end
|
||||
|
|
|
@ -55,12 +55,14 @@ module Admin
|
|||
def approve
|
||||
authorize @account.user, :approve?
|
||||
@account.user.approve!
|
||||
log_action :approve, @account.user
|
||||
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def reject
|
||||
authorize @account.user, :reject?
|
||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
log_action :reject, @account.user
|
||||
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
|
|
|
@ -54,12 +54,14 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
|||
def approve
|
||||
authorize @account.user, :approve?
|
||||
@account.user.approve!
|
||||
log_action :approve, @account.user
|
||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
||||
end
|
||||
|
||||
def reject
|
||||
authorize @account.user, :reject?
|
||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
log_action :reject, @account.user
|
||||
render_empty
|
||||
end
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class Api::V1::FiltersController < Api::BaseController
|
|||
|
||||
def create
|
||||
ApplicationRecord.transaction do
|
||||
filter_category = current_account.custom_filters.create!(resource_params)
|
||||
filter_category = current_account.custom_filters.create!(filter_params)
|
||||
@filter = filter_category.keywords.create!(keyword_params)
|
||||
end
|
||||
|
||||
|
@ -52,11 +52,11 @@ class Api::V1::FiltersController < Api::BaseController
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:phrase, :expires_in, :irreversible, context: [])
|
||||
params.permit(:phrase, :expires_in, :irreversible, :whole_word, context: [])
|
||||
end
|
||||
|
||||
def filter_params
|
||||
resource_params.slice(:expires_in, :irreversible, :context)
|
||||
resource_params.slice(:phrase, :expires_in, :irreversible, :context)
|
||||
end
|
||||
|
||||
def keyword_params
|
||||
|
|
|
@ -7,6 +7,7 @@ import Avatar from 'flavours/glitch/components/avatar';
|
|||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
|
||||
const Account = connect(state => ({
|
||||
account: state.getIn(['accounts', me]),
|
||||
|
@ -16,7 +17,14 @@ const Account = connect(state => ({
|
|||
</Permalink>
|
||||
));
|
||||
|
||||
export default @withRouter
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openClosedRegistrationsModal() {
|
||||
dispatch(openModal('CLOSED_REGISTRATIONS'));
|
||||
},
|
||||
});
|
||||
|
||||
export default @connect(null, mapDispatchToProps)
|
||||
@withRouter
|
||||
class Header extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -24,12 +32,13 @@ class Header extends React.PureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { location } = this.props;
|
||||
const { location, openClosedRegistrationsModal } = this.props;
|
||||
|
||||
let content;
|
||||
|
||||
|
@ -41,10 +50,26 @@ class Header extends React.PureComponent {
|
|||
</>
|
||||
);
|
||||
} else {
|
||||
let signupButton;
|
||||
|
||||
if (registrationsOpen) {
|
||||
signupButton = (
|
||||
<a href='/auth/sign_up' className='button button-tertiary'>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
signupButton = (
|
||||
<button className='button button-tertiary' onClick={openClosedRegistrationsModal}>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
content = (
|
||||
<>
|
||||
<a href='/auth/sign_in' className='button'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
|
||||
<a href={registrationsOpen ? '/auth/sign_up' : 'https://joinmastodon.org/servers'} className='button button-tertiary'><FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /></a>
|
||||
{signupButton}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ $ui-header-height: 55px;
|
|||
z-index: 2;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
&__logo {
|
||||
display: inline-flex;
|
||||
|
@ -81,10 +82,15 @@ $ui-header-height: 55px;
|
|||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
|
||||
.button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.button-tertiary {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1306,7 +1306,8 @@ img.modal-warning {
|
|||
width: 600px;
|
||||
background: $ui-base-color;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { registrationsOpen, me } from 'mastodon/initial_state';
|
|||
import Avatar from 'mastodon/components/avatar';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
|
||||
const Account = connect(state => ({
|
||||
account: state.getIn(['accounts', me]),
|
||||
|
@ -15,7 +16,14 @@ const Account = connect(state => ({
|
|||
</Link>
|
||||
));
|
||||
|
||||
export default @withRouter
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openClosedRegistrationsModal() {
|
||||
dispatch(openModal('CLOSED_REGISTRATIONS'));
|
||||
},
|
||||
});
|
||||
|
||||
export default @connect(null, mapDispatchToProps)
|
||||
@withRouter
|
||||
class Header extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -23,12 +31,13 @@ class Header extends React.PureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { location } = this.props;
|
||||
const { location, openClosedRegistrationsModal } = this.props;
|
||||
|
||||
let content;
|
||||
|
||||
|
@ -40,10 +49,26 @@ class Header extends React.PureComponent {
|
|||
</>
|
||||
);
|
||||
} else {
|
||||
let signupButton;
|
||||
|
||||
if (registrationsOpen) {
|
||||
signupButton = (
|
||||
<a href='/auth/sign_up' className='button button-tertiary'>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
signupButton = (
|
||||
<button className='button button-tertiary' onClick={openClosedRegistrationsModal}>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
content = (
|
||||
<>
|
||||
<a href='/auth/sign_in' className='button'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
|
||||
<a href={registrationsOpen ? '/auth/sign_up' : 'https://joinmastodon.org/servers'} className='button button-tertiary'><FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /></a>
|
||||
{signupButton}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2217,6 +2217,7 @@ $ui-header-height: 55px;
|
|||
z-index: 2;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
&__logo {
|
||||
display: inline-flex;
|
||||
|
@ -2233,10 +2234,15 @@ $ui-header-height: 55px;
|
|||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
|
||||
.button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.button-tertiary {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7138,10 +7144,12 @@ noscript {
|
|||
|
||||
.verified {
|
||||
border: 1px solid rgba($valid-value-color, 0.5);
|
||||
margin-top: -1px;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
@ -7973,7 +7981,8 @@ noscript {
|
|||
width: 600px;
|
||||
background: $ui-base-color;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
|
|
|
@ -59,7 +59,7 @@ class AccountMigration < ApplicationRecord
|
|||
|
||||
def set_target_account
|
||||
self.target_account = ResolveAccountService.new.call(acct, skip_cache: true)
|
||||
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error
|
||||
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError
|
||||
# Validation will take care of it
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ module DomainMaterializable
|
|||
|
||||
Instance.refresh
|
||||
count_unique_subdomains!
|
||||
|
||||
end
|
||||
|
||||
def count_unique_subdomains!
|
||||
|
|
|
@ -54,7 +54,7 @@ class CustomFilter < ApplicationRecord
|
|||
end
|
||||
|
||||
def irreversible=(value)
|
||||
self.action = value ? :hide : :warn
|
||||
self.action = ActiveModel::Type::Boolean.new.cast(value) ? :hide : :warn
|
||||
end
|
||||
|
||||
def irreversible?
|
||||
|
|
|
@ -32,7 +32,7 @@ class Form::Redirect
|
|||
|
||||
def set_target_account
|
||||
@target_account = ResolveAccountService.new.call(acct, skip_cache: true)
|
||||
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error
|
||||
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError
|
||||
# Validation will take care of it
|
||||
end
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ module Mastodon
|
|||
:af,
|
||||
:ar,
|
||||
:ast,
|
||||
:be,
|
||||
:bg,
|
||||
:bn,
|
||||
:br,
|
||||
|
|
|
@ -2,6 +2,7 @@ default: &default
|
|||
adapter: postgresql
|
||||
pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %>
|
||||
timeout: 5000
|
||||
connect_timeout: 15
|
||||
encoding: unicode
|
||||
sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %>
|
||||
|
||||
|
|
|
@ -79,69 +79,72 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1]
|
|||
safety_assured do
|
||||
AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log|
|
||||
next if log.account.nil?
|
||||
log.update(human_identifier: log.account.acct)
|
||||
log.update_attribute('human_identifier', log.account.acct)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log|
|
||||
next if log.user.nil?
|
||||
log.update(human_identifier: log.user.account.acct, route_param: log.user.account_id)
|
||||
log.update_attribute('human_identifier', log.user.account.acct)
|
||||
log.update_attribute('route_param', log.user.account_id)
|
||||
end
|
||||
|
||||
Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text')
|
||||
|
||||
AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log|
|
||||
next if log.domain_block.nil?
|
||||
log.update(human_identifier: log.domain_block.domain)
|
||||
log.update_attribute('human_identifier', log.domain_block.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log|
|
||||
next if log.domain_allow.nil?
|
||||
log.update(human_identifier: log.domain_allow.domain)
|
||||
log.update_attribute('human_identifier', log.domain_allow.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log|
|
||||
next if log.email_domain_block.nil?
|
||||
log.update(human_identifier: log.email_domain_block.domain)
|
||||
log.update_attribute('human_identifier', log.email_domain_block.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log|
|
||||
next if log.unavailable_domain.nil?
|
||||
log.update(human_identifier: log.unavailable_domain.domain)
|
||||
log.update_attribute('human_identifier', log.unavailable_domain.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log|
|
||||
next if log.status.nil?
|
||||
log.update(human_identifier: log.status.account.acct, permalink: log.status.uri)
|
||||
log.update_attribute('human_identifier', log.status.account.acct)
|
||||
log.update_attribute('permalink', log.status.uri)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log|
|
||||
next if log.account_warning.nil?
|
||||
log.update(human_identifier: log.account_warning.account.acct)
|
||||
log.update_attribute('human_identifier', log.account_warning.account.acct)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log|
|
||||
next if log.announcement.nil?
|
||||
log.update(human_identifier: log.announcement.text)
|
||||
log.update_attribute('human_identifier', log.announcement.text)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log|
|
||||
next if log.ip_block.nil?
|
||||
log.update(human_identifier: "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}")
|
||||
log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}")
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log|
|
||||
next if log.custom_emoji.nil?
|
||||
log.update(human_identifier: log.custom_emoji.shortcode)
|
||||
log.update_attribute('human_identifier', log.custom_emoji.shortcode)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log|
|
||||
next if log.canonical_email_block.nil?
|
||||
log.update(human_identifier: log.canonical_email_block.canonical_email_hash)
|
||||
log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log|
|
||||
next if log.appeal.nil?
|
||||
log.update(human_identifier: log.appeal.account.acct, route_param: log.appeal.account_warning_id)
|
||||
log.update_attribute('human_identifier', log.appeal.account.acct)
|
||||
log.update_attribute('route_param', log.appeal.account_warning_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Account < ApplicationRecord
|
||||
# Dummy class, to make migration possible across version changes
|
||||
has_one :user, inverse_of: :account
|
||||
|
||||
def local?
|
||||
domain.nil?
|
||||
end
|
||||
|
||||
def acct
|
||||
local? ? username : "#{username}@#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
class User < ApplicationRecord
|
||||
# Dummy class, to make migration possible across version changes
|
||||
belongs_to :account
|
||||
end
|
||||
|
||||
class Status < ApplicationRecord
|
||||
include RoutingHelper
|
||||
|
||||
# Dummy class, to make migration possible across version changes
|
||||
belongs_to :account
|
||||
|
||||
def local?
|
||||
attributes['local'] || attributes['uri'].nil?
|
||||
end
|
||||
|
||||
def uri
|
||||
local? ? activity_account_status_url(account, self) : attributes['uri']
|
||||
end
|
||||
end
|
||||
|
||||
class DomainBlock < ApplicationRecord; end
|
||||
class DomainAllow < ApplicationRecord; end
|
||||
class EmailDomainBlock < ApplicationRecord; end
|
||||
class UnavailableDomain < ApplicationRecord; end
|
||||
|
||||
class AccountWarning < ApplicationRecord
|
||||
# Dummy class, to make migration possible across version changes
|
||||
belongs_to :account
|
||||
end
|
||||
|
||||
class Announcement < ApplicationRecord; end
|
||||
class IpBlock < ApplicationRecord; end
|
||||
class CustomEmoji < ApplicationRecord; end
|
||||
class CanonicalEmailBlock < ApplicationRecord; end
|
||||
|
||||
class Appeal < ApplicationRecord
|
||||
# Dummy class, to make migration possible across version changes
|
||||
belongs_to :account
|
||||
end
|
||||
|
||||
class AdminActionLog < ApplicationRecord
|
||||
# Dummy class, to make migration possible across version changes
|
||||
|
||||
# Cannot use usual polymorphic support because of namespacing issues
|
||||
belongs_to :status, foreign_key: :target_id
|
||||
belongs_to :account, foreign_key: :target_id
|
||||
belongs_to :user, foreign_key: :user_id
|
||||
belongs_to :domain_block, foreign_key: :target_id
|
||||
belongs_to :domain_allow, foreign_key: :target_id
|
||||
belongs_to :email_domain_block, foreign_key: :target_id
|
||||
belongs_to :unavailable_domain, foreign_key: :target_id
|
||||
belongs_to :account_warning, foreign_key: :target_id
|
||||
belongs_to :announcement, foreign_key: :target_id
|
||||
belongs_to :ip_block, foreign_key: :target_id
|
||||
belongs_to :custom_emoji, foreign_key: :target_id
|
||||
belongs_to :canonical_email_block, foreign_key: :target_id
|
||||
belongs_to :appeal, foreign_key: :target_id
|
||||
end
|
||||
|
||||
def up
|
||||
safety_assured do
|
||||
AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log|
|
||||
next if log.account.nil?
|
||||
log.update_attribute('human_identifier', log.account.acct)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log|
|
||||
next if log.user.nil?
|
||||
log.update_attribute('human_identifier', log.user.account.acct)
|
||||
log.update_attribute('route_param', log.user.account_id)
|
||||
end
|
||||
|
||||
Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text')
|
||||
|
||||
AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log|
|
||||
next if log.domain_block.nil?
|
||||
log.update_attribute('human_identifier', log.domain_block.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log|
|
||||
next if log.domain_allow.nil?
|
||||
log.update_attribute('human_identifier', log.domain_allow.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log|
|
||||
next if log.email_domain_block.nil?
|
||||
log.update_attribute('human_identifier', log.email_domain_block.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log|
|
||||
next if log.unavailable_domain.nil?
|
||||
log.update_attribute('human_identifier', log.unavailable_domain.domain)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log|
|
||||
next if log.status.nil?
|
||||
log.update_attribute('human_identifier', log.status.account.acct)
|
||||
log.update_attribute('permalink', log.status.uri)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log|
|
||||
next if log.account_warning.nil?
|
||||
log.update_attribute('human_identifier', log.account_warning.account.acct)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log|
|
||||
next if log.announcement.nil?
|
||||
log.update_attribute('human_identifier', log.announcement.text)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log|
|
||||
next if log.ip_block.nil?
|
||||
log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}")
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log|
|
||||
next if log.custom_emoji.nil?
|
||||
log.update_attribute('human_identifier', log.custom_emoji.shortcode)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log|
|
||||
next if log.canonical_email_block.nil?
|
||||
log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash)
|
||||
end
|
||||
|
||||
AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log|
|
||||
next if log.appeal.nil?
|
||||
log.update_attribute('human_identifier', log.appeal.account.acct)
|
||||
log.update_attribute('route_param', log.appeal.account_warning_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_11_04_133904) do
|
||||
ActiveRecord::Schema.define(version: 2022_12_06_114142) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
|
|
@ -43,6 +43,16 @@ namespace :tests do
|
|||
puts 'CustomFilterKeyword records not created as expected'
|
||||
exit(1)
|
||||
end
|
||||
|
||||
unless Admin::ActionLog.find_by(target_type: 'DomainBlock', target_id: 1).human_identifier == 'example.org'
|
||||
puts 'Admin::ActionLog domain block records not updated as expected'
|
||||
exit(1)
|
||||
end
|
||||
|
||||
unless Admin::ActionLog.find_by(target_type: 'EmailDomainBlock', target_id: 1).human_identifier == 'example.org'
|
||||
puts 'Admin::ActionLog email domain block records not updated as expected'
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Populate the database with test data for 2.4.3'
|
||||
|
@ -84,8 +94,8 @@ namespace :tests do
|
|||
VALUES
|
||||
(1, 'destroy', 'Account', 1, now(), now()),
|
||||
(1, 'destroy', 'User', 1, now(), now()),
|
||||
(1, 'destroy', 'DomainBlock', 1312, now(), now()),
|
||||
(1, 'destroy', 'EmailDomainBlock', 1312, now(), now()),
|
||||
(1, 'destroy', 'DomainBlock', 1, now(), now()),
|
||||
(1, 'destroy', 'EmailDomainBlock', 1, now(), now()),
|
||||
(1, 'destroy', 'Status', 1, now(), now()),
|
||||
(1, 'destroy', 'CustomEmoji', 3, now(), now());
|
||||
SQL
|
||||
|
|
|
@ -147,6 +147,87 @@ RSpec.describe Admin::AccountsController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'POST #approve' do
|
||||
subject { post :approve, params: { id: account.id } }
|
||||
|
||||
let(:current_user) { Fabricate(:user, role: role) }
|
||||
let(:account) { user.account }
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
account.user.update(approved: false)
|
||||
end
|
||||
|
||||
context 'when user is admin' do
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
|
||||
it 'succeeds in approving account' do
|
||||
is_expected.to redirect_to admin_accounts_path(status: 'pending')
|
||||
expect(user.reload).to be_approved
|
||||
end
|
||||
|
||||
it 'logs action' do
|
||||
is_expected.to have_http_status :found
|
||||
|
||||
log_item = Admin::ActionLog.last
|
||||
|
||||
expect(log_item).to_not be_nil
|
||||
expect(log_item.action).to eq :approve
|
||||
expect(log_item.account_id).to eq current_user.account_id
|
||||
expect(log_item.target_id).to eq account.user.id
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not admin' do
|
||||
let(:role) { UserRole.everyone }
|
||||
|
||||
it 'fails to approve account' do
|
||||
is_expected.to have_http_status :forbidden
|
||||
expect(user.reload).not_to be_approved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #reject' do
|
||||
subject { post :reject, params: { id: account.id } }
|
||||
|
||||
let(:current_user) { Fabricate(:user, role: role) }
|
||||
let(:account) { user.account }
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
account.user.update(approved: false)
|
||||
end
|
||||
|
||||
context 'when user is admin' do
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
|
||||
it 'succeeds in rejecting account' do
|
||||
is_expected.to redirect_to admin_accounts_path(status: 'pending')
|
||||
end
|
||||
|
||||
it 'logs action' do
|
||||
is_expected.to have_http_status :found
|
||||
|
||||
log_item = Admin::ActionLog.last
|
||||
|
||||
expect(log_item).to_not be_nil
|
||||
expect(log_item.action).to eq :reject
|
||||
expect(log_item.account_id).to eq current_user.account_id
|
||||
expect(log_item.target_id).to eq account.user.id
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not admin' do
|
||||
let(:role) { UserRole.everyone }
|
||||
|
||||
it 'fails to reject account' do
|
||||
is_expected.to have_http_status :forbidden
|
||||
expect(user.reload).not_to be_approved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #redownload' do
|
||||
subject { post :redownload, params: { id: account.id } }
|
||||
|
||||
|
|
|
@ -100,6 +100,15 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
|
|||
it 'approves user' do
|
||||
expect(account.reload.user_approved?).to be true
|
||||
end
|
||||
|
||||
it 'logs action' do
|
||||
log_item = Admin::ActionLog.last
|
||||
|
||||
expect(log_item).to_not be_nil
|
||||
expect(log_item.action).to eq :approve
|
||||
expect(log_item.account_id).to eq user.account_id
|
||||
expect(log_item.target_id).to eq account.user.id
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #reject' do
|
||||
|
@ -118,6 +127,15 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
|
|||
it 'removes user' do
|
||||
expect(User.where(id: account.user.id).count).to eq 0
|
||||
end
|
||||
|
||||
it 'logs action' do
|
||||
log_item = Admin::ActionLog.last
|
||||
|
||||
expect(log_item).to_not be_nil
|
||||
expect(log_item.action).to eq :reject
|
||||
expect(log_item.account_id).to eq user.account_id
|
||||
expect(log_item.target_id).to eq account.user.id
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #enable' do
|
||||
|
|
|
@ -22,9 +22,11 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
|
|||
|
||||
describe 'POST #create' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:irreversible) { true }
|
||||
let(:whole_word) { false }
|
||||
|
||||
before do
|
||||
post :create, params: { phrase: 'magic', context: %w(home), irreversible: true }
|
||||
post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
|
@ -34,11 +36,29 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
|
|||
it 'creates a filter' do
|
||||
filter = user.account.custom_filters.first
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.keywords.pluck(:keyword)).to eq ['magic']
|
||||
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
|
||||
expect(filter.context).to eq %w(home)
|
||||
expect(filter.irreversible?).to be true
|
||||
expect(filter.irreversible?).to be irreversible
|
||||
expect(filter.expires_at).to be_nil
|
||||
end
|
||||
|
||||
context 'with different parameters' do
|
||||
let(:irreversible) { false }
|
||||
let(:whole_word) { true }
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates a filter' do
|
||||
filter = user.account.custom_filters.first
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
|
||||
expect(filter.context).to eq %w(home)
|
||||
expect(filter.irreversible?).to be irreversible
|
||||
expect(filter.expires_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
|
|
|
@ -1,5 +1,48 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountMigration, type: :model do
|
||||
describe 'validations' do
|
||||
let(:source_account) { Fabricate(:account) }
|
||||
let(:target_acct) { target_account.acct }
|
||||
|
||||
let(:subject) { AccountMigration.new(account: source_account, acct: target_acct) }
|
||||
|
||||
context 'with valid properties' do
|
||||
let(:target_account) { Fabricate(:account, username: 'target', domain: 'remote.org') }
|
||||
|
||||
before do
|
||||
target_account.aliases.create!(acct: source_account.acct)
|
||||
|
||||
service_double = double
|
||||
allow(ResolveAccountService).to receive(:new).and_return(service_double)
|
||||
allow(service_double).to receive(:call).with(target_acct, anything).and_return(target_account)
|
||||
end
|
||||
|
||||
it 'passes validations' do
|
||||
expect(subject).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unresolveable account' do
|
||||
let(:target_acct) { 'target@remote' }
|
||||
|
||||
before do
|
||||
service_double = double
|
||||
allow(ResolveAccountService).to receive(:new).and_return(service_double)
|
||||
allow(service_double).to receive(:call).with(target_acct, anything).and_return(nil)
|
||||
end
|
||||
|
||||
it 'has errors on acct field' do
|
||||
expect(subject).to model_have_error_on_field(:acct)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a space in the domain part' do
|
||||
let(:target_acct) { 'target@remote. org' }
|
||||
|
||||
it 'has errors on acct field' do
|
||||
expect(subject).to model_have_error_on_field(:acct)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue