Merge branch 'master' into master
This commit is contained in:
commit
db4a41cf58
46 changed files with 565 additions and 75 deletions
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 59 KiB |
|
@ -43,7 +43,7 @@ const GettingStarted = ({ intl, me }) => {
|
|||
|
||||
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className='static-content getting-started'>
|
||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
|
|
|
@ -25,7 +25,7 @@ const en = {
|
|||
"getting_started.heading": "Getting started",
|
||||
"getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.",
|
||||
"getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
|
||||
"column.home": "Home",
|
||||
"column.community": "Local timeline",
|
||||
"column.public": "Federated timeline",
|
||||
|
|
|
@ -9,6 +9,24 @@ class Admin::DomainBlocksController < ApplicationController
|
|||
@blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
|
||||
end
|
||||
|
||||
def new
|
||||
@domain_block = DomainBlock.new
|
||||
end
|
||||
|
||||
def create
|
||||
@domain_block = DomainBlock.new(resource_params)
|
||||
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id)
|
||||
redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed'
|
||||
else
|
||||
render action: :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,19 +16,19 @@ class Admin::ReportsController < ApplicationController
|
|||
end
|
||||
|
||||
def resolve
|
||||
@report.update(action_taken: true)
|
||||
@report.update(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def suspend
|
||||
Admin::SuspensionWorker.perform_async(@report.target_account.id)
|
||||
@report.update(action_taken: true)
|
||||
Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def silence
|
||||
@report.target_account.update(silenced: true)
|
||||
@report.update(action_taken: true)
|
||||
Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,12 @@ class Api::V1::AppsController < ApiController
|
|||
respond_to :json
|
||||
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website])
|
||||
@app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::FollowsController < ApiController
|
|||
respond_to :json
|
||||
|
||||
def create
|
||||
raise ActiveRecord::RecordNotFound if params[:uri].blank?
|
||||
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
|
||||
|
||||
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
|
||||
render action: :show
|
||||
|
@ -16,6 +16,10 @@ class Api::V1::FollowsController < ApiController
|
|||
private
|
||||
|
||||
def target_uri
|
||||
params[:uri].strip.gsub(/\A@/, '')
|
||||
follow_params[:uri].strip.gsub(/\A@/, '')
|
||||
end
|
||||
|
||||
def follow_params
|
||||
params.permit(:uri)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,10 +10,16 @@ class Api::V1::MediaController < ApiController
|
|||
respond_to :json
|
||||
|
||||
def create
|
||||
@media = MediaAttachment.create!(account: current_user.account, file: params[:file])
|
||||
@media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: { error: 'File type of uploaded media could not be verified' }, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def media_params
|
||||
params.permit(:file)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,13 +12,19 @@ class Api::V1::ReportsController < ApiController
|
|||
end
|
||||
|
||||
def create
|
||||
status_ids = params[:status_ids].is_a?(Enumerable) ? params[:status_ids] : [params[:status_ids]]
|
||||
status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
|
||||
|
||||
@report = Report.create!(account: current_account,
|
||||
target_account: Account.find(params[:account_id]),
|
||||
target_account: Account.find(report_params[:account_id]),
|
||||
status_ids: Status.find(status_ids).pluck(:id),
|
||||
comment: params[:comment])
|
||||
comment: report_params[:comment])
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report_params
|
||||
params.permit(:account_id, :comment, status_ids: [])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,11 +62,11 @@ class Api::V1::StatusesController < ApiController
|
|||
end
|
||||
|
||||
def create
|
||||
@status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids],
|
||||
sensitive: params[:sensitive],
|
||||
spoiler_text: params[:spoiler_text],
|
||||
visibility: params[:visibility],
|
||||
application: doorkeeper_token.application)
|
||||
@status = PostStatusService.new.call(current_user.account, status_params[:status], status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), media_ids: status_params[:media_ids],
|
||||
sensitive: status_params[:sensitive],
|
||||
spoiler_text: status_params[:spoiler_text],
|
||||
visibility: status_params[:visibility],
|
||||
application: doorkeeper_token.application)
|
||||
render action: :show
|
||||
end
|
||||
|
||||
|
@ -111,4 +111,8 @@ class Api::V1::StatusesController < ApiController
|
|||
@status = Status.find(params[:id])
|
||||
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,14 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def set_user_activity
|
||||
current_user.touch(:current_sign_in_at) if !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
|
||||
return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
|
||||
|
||||
# Mark user as signed-in today
|
||||
current_user.update_tracked_fields(request)
|
||||
|
||||
# If the sign in is after a two week break, we need to regenerate their feed
|
||||
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
|
||||
return
|
||||
end
|
||||
|
||||
def check_suspension
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||
skip_before_action :authenticate_resource_owner!
|
||||
|
||||
before_action :set_locale
|
||||
before_action :store_current_location
|
||||
before_action :authenticate_resource_owner!
|
||||
|
||||
|
@ -11,4 +12,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
def store_current_location
|
||||
store_location_for(:user, request.url)
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = current_user.try(:locale) || I18n.default_locale
|
||||
rescue I18n::InvalidLocale
|
||||
I18n.locale = I18n.default_locale
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,4 +4,5 @@ module Mastodon
|
|||
class Error < StandardError; end
|
||||
class NotPermittedError < Error; end
|
||||
class ValidationError < Error; end
|
||||
class RaceConditionError < Error; end
|
||||
end
|
||||
|
|
|
@ -10,17 +10,9 @@ class Feed
|
|||
max_id = '+inf' if max_id.blank?
|
||||
since_id = '-inf' if since_id.blank?
|
||||
unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i)
|
||||
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
|
||||
|
||||
# If we're after most recent items and none are there, we need to precompute the feed
|
||||
if unhydrated.empty? && max_id == '+inf' && since_id == '-inf'
|
||||
RegenerationWorker.perform_async(@account.id, @type)
|
||||
@statuses = Status.send("as_#{@type}_timeline", @account).cache_ids.paginate_by_max_id(limit, nil, nil)
|
||||
else
|
||||
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
|
||||
@statuses = unhydrated.map { |id| status_map[id] }.compact
|
||||
end
|
||||
|
||||
@statuses
|
||||
unhydrated.map { |id| status_map[id] }.compact
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Report < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
belongs_to :action_taken_by_account, class_name: 'Account'
|
||||
|
||||
scope :unresolved, -> { where(action_taken: false) }
|
||||
scope :resolved, -> { where(action_taken: true) }
|
||||
|
|
|
@ -188,7 +188,7 @@ class Status < ApplicationRecord
|
|||
end
|
||||
|
||||
before_validation do
|
||||
text.strip!
|
||||
text&.strip!
|
||||
spoiler_text&.strip!
|
||||
|
||||
self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BlockDomainService < BaseService
|
||||
def call(domain, severity)
|
||||
DomainBlock.where(domain: domain).first_or_create!(domain: domain, severity: severity)
|
||||
|
||||
if severity == :silence
|
||||
Account.where(domain: domain).update_all(silenced: true)
|
||||
def call(domain_block)
|
||||
if domain_block.silence?
|
||||
Account.where(domain: domain_block.domain).update_all(silenced: true)
|
||||
else
|
||||
Account.where(domain: domain).find_each do |account|
|
||||
Account.where(domain: domain_block.domain).find_each do |account|
|
||||
account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed?
|
||||
SuspendAccountService.new.call(account)
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ class FanOutOnWriteService < BaseService
|
|||
# Push a status into home and mentions feeds
|
||||
# @param [Status] status
|
||||
def call(status)
|
||||
raise Mastodon::RaceConditionError if status.visibility.nil?
|
||||
|
||||
deliver_to_self(status) if status.account.local?
|
||||
|
||||
if status.direct_visibility?
|
||||
|
|
|
@ -14,3 +14,4 @@
|
|||
%td= block.severity
|
||||
|
||||
= will_paginate @blocks, pagination_options
|
||||
= link_to 'Add new', new_admin_domain_block_path, class: 'button'
|
||||
|
|
18
app/views/admin/domain_blocks/new.html.haml
Normal file
18
app/views/admin/domain_blocks/new.html.haml
Normal file
|
@ -0,0 +1,18 @@
|
|||
- content_for :page_title do
|
||||
New domain block
|
||||
|
||||
= simple_form_for @domain_block, url: admin_domain_blocks_path do |f|
|
||||
= render 'shared/error_messages', object: @domain_block
|
||||
|
||||
%p.hint The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
|
||||
|
||||
= f.input :domain, placeholder: 'Domain'
|
||||
= f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false
|
||||
|
||||
%p.hint
|
||||
%strong Silence
|
||||
will make the account's posts invisible to anyone who isn't following them.
|
||||
%strong Suspend
|
||||
will remove all of the account's content, media, and profile data.
|
||||
.actions
|
||||
= f.button :button, 'Create block', type: :submit
|
|
@ -8,20 +8,25 @@
|
|||
%li= filter_link_to 'Unresolved', action_taken: nil
|
||||
%li= filter_link_to 'Resolved', action_taken: '1'
|
||||
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th ID
|
||||
%th Target
|
||||
%th Reported by
|
||||
%th Comment
|
||||
%th
|
||||
%tbody
|
||||
- @reports.each do |report|
|
||||
= form_tag do
|
||||
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%td= "##{report.id}"
|
||||
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
|
||||
%td= link_to report.account.acct, admin_account_path(report.account.id)
|
||||
%td= truncate(report.comment, length: 30, separator: ' ')
|
||||
%td= table_link_to 'circle', 'View', admin_report_path(report)
|
||||
%th
|
||||
%th ID
|
||||
%th Target
|
||||
%th Reported by
|
||||
%th Comment
|
||||
%th
|
||||
%tbody
|
||||
- @reports.each do |report|
|
||||
%tr
|
||||
%td= check_box_tag 'select', report.id
|
||||
%td= "##{report.id}"
|
||||
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
|
||||
%td= link_to report.account.acct, admin_account_path(report.account.id)
|
||||
%td= truncate(report.comment, length: 30, separator: ' ')
|
||||
%td= table_link_to 'circle', 'View', admin_report_path(report)
|
||||
|
||||
= will_paginate @reports, pagination_options
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
= link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do
|
||||
= fa_icon 'trash'
|
||||
|
||||
- unless @report.action_taken?
|
||||
- if !@report.action_taken?
|
||||
%hr/
|
||||
|
||||
%div{ style: 'overflow: hidden' }
|
||||
|
@ -36,3 +36,9 @@
|
|||
= link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button'
|
||||
%div{ style: 'float: left' }
|
||||
= link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button'
|
||||
- elsif !@report.action_taken_by_account.nil?
|
||||
%hr/
|
||||
|
||||
%p
|
||||
%strong Action taken by:
|
||||
= @report.action_taken_by_account.acct
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class AfterRemoteFollowRequestWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: 5
|
||||
sidekiq_options queue: 'pull', retry: 5
|
||||
|
||||
def perform(follow_request_id)
|
||||
follow_request = FollowRequest.find(follow_request_id)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class AfterRemoteFollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: 5
|
||||
sidekiq_options queue: 'pull', retry: 5
|
||||
|
||||
def perform(follow_id)
|
||||
follow = Follow.find(follow_id)
|
||||
|
|
11
app/workers/domain_block_worker.rb
Normal file
11
app/workers/domain_block_worker.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DomainBlockWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(domain_block_id)
|
||||
BlockDomainService.new.call(DomainBlock.find(domain_block_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
|
@ -5,7 +5,7 @@ require 'csv'
|
|||
class ImportWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: false
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(import_id)
|
||||
import = Import.find(import_id)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class LinkCrawlWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: false
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(status_id)
|
||||
FetchLinkCardService.new.call(Status.find(status_id))
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
class MergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(from_account_id, into_account_id)
|
||||
FeedManager.instance.merge_into_timeline(Account.find(from_account_id), Account.find(into_account_id))
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class NotificationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: 5
|
||||
sidekiq_options queue: 'push', retry: 5
|
||||
|
||||
def perform(xml, source_account_id, target_account_id)
|
||||
SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class ProcessingWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
sidekiq_options queue: 'pull', backtrace: true
|
||||
|
||||
def perform(account_id, body)
|
||||
ProcessFeedService.new.call(body, Account.find(account_id))
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
class RegenerationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, timeline_type)
|
||||
PrecomputeFeedService.new.call(timeline_type, Account.find(account_id))
|
||||
sidekiq_options queue: 'pull', backtrace: true
|
||||
|
||||
def perform(account_id, _ = :home)
|
||||
PrecomputeFeedService.new.call(:home, Account.find(account_id))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class SalmonWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
sidekiq_options queue: 'pull', backtrace: true
|
||||
|
||||
def perform(account_id, body)
|
||||
ProcessInteractionService.new.call(body, Account.find(account_id))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class ThreadResolveWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options retry: false
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(child_status_id, parent_url)
|
||||
child_status = Status.find(child_status_id)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
class UnmergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(from_account_id, into_account_id)
|
||||
FeedManager.instance.unmerge_from_timeline(Account.find(from_account_id), Account.find(into_account_id))
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue