Merge branch 'main' into glitch-soc/merge-upstream

This commit is contained in:
Claire
2021-03-24 10:53:50 +01:00
53 changed files with 684 additions and 404 deletions

View File

@@ -5,8 +5,6 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
force_ssl if: :https_enabled?
include Localized
include UserTrackingConcern
include SessionTrackingConcern
@@ -43,10 +41,6 @@ class ApplicationController < ActionController::Base
private
def https_enabled?
Rails.env.production? && !request.path.start_with?('/health') && !request.headers["Host"].end_with?(".onion")
end
def authorized_fetch_mode?
ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.whitelist_mode
end

View File

@@ -133,7 +133,15 @@ class ComposeForm extends ImmutablePureComponent {
}
}
componentDidMount () {
this._updateFocusAndSelection({ });
}
componentDidUpdate (prevProps) {
this._updateFocusAndSelection(prevProps);
}
_updateFocusAndSelection = (prevProps) => {
// This statement does several things:
// - If we're beginning a reply, and,
// - Replying to zero or one users, places the cursor at the end of the textbox.

View File

@@ -59,7 +59,7 @@ class ColumnSettings extends React.PureComponent {
{this.modeLabel(mode)}
</span>
<NonceProvider nonce={document.querySelector('meta[name=style-nonce]').content}>
<NonceProvider nonce={document.querySelector('meta[name=style-nonce]').content} cacheKey='tags'>
<AsyncSelect
isMulti
autoFocus

View File

@@ -70,8 +70,12 @@ class ColumnsArea extends ImmutablePureComponent {
children: PropTypes.node,
};
// Corresponds to (max-width: 600px + (285px * 1) + (10px * 1)) in SCSS
mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 895px)');
state = {
shouldAnimate: false,
renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
}
componentWillReceiveProps() {
@@ -85,6 +89,11 @@ class ColumnsArea extends ImmutablePureComponent {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
}
if (this.mediaQuery) {
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
this.setState({ renderComposePanel: !this.mediaQuery.matches });
}
this.lastIndex = getIndex(this.context.router.history.location.pathname);
this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
@@ -114,6 +123,10 @@ class ColumnsArea extends ImmutablePureComponent {
if (!this.props.singleColumn) {
this.node.removeEventListener('wheel', this.handleWheel);
}
if (this.mediaQuery) {
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
}
}
handleChildrenContentChange() {
@@ -123,6 +136,10 @@ class ColumnsArea extends ImmutablePureComponent {
}
}
handleLayoutChange = (e) => {
this.setState({ renderComposePanel: !e.matches });
}
handleSwipe = (index) => {
this.pendingIndex = index;
@@ -186,7 +203,7 @@ class ColumnsArea extends ImmutablePureComponent {
render () {
const { columns, children, singleColumn, isModalOpen, intl } = this.props;
const { shouldAnimate } = this.state;
const { shouldAnimate, renderComposePanel } = this.state;
const columnIndex = getIndex(this.context.router.history.location.pathname);
@@ -205,7 +222,7 @@ class ColumnsArea extends ImmutablePureComponent {
<div className='columns-area__panels'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
<ComposePanel />
{renderComposePanel && <ComposePanel />}
</div>
</div>

View File

@@ -707,7 +707,6 @@ html {
.public-account-bio,
.hero-widget__text {
background: $account-background-color;
border: 1px solid lighten($ui-base-color, 8%);
}
.header {

View File

@@ -43,9 +43,9 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
end
def visibility_from_audience
if audience_to.include?(ActivityPub::TagManager::COLLECTIONS[:public])
if audience_to.any? { |to| ActivityPub::TagManager.instance.public_collection?(to) }
:public
elsif audience_cc.include?(ActivityPub::TagManager::COLLECTIONS[:public])
elsif audience_cc.any? { |cc| ActivityPub::TagManager.instance.public_collection?(cc) }
:unlisted
elsif audience_to.include?(@account.followers_url)
:private

View File

@@ -123,7 +123,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def process_audience
(audience_to + audience_cc).uniq.each do |audience|
next if audience == ActivityPub::TagManager::COLLECTIONS[:public]
next if ActivityPub::TagManager.instance.public_collection?(audience)
# Unlike with tags, there is no point in resolving accounts we don't already
# know here, because silent mentions would only be used for local access
@@ -356,9 +356,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def visibility_from_audience
if audience_to.include?(ActivityPub::TagManager::COLLECTIONS[:public])
if audience_to.any? { |to| ActivityPub::TagManager.instance.public_collection?(to) }
:public
elsif audience_cc.include?(ActivityPub::TagManager::COLLECTIONS[:public])
elsif audience_cc.any? { |cc| ActivityPub::TagManager.instance.public_collection?(cc) }
:unlisted
elsif audience_to.include?(@account.followers_url)
:private

View File

@@ -12,6 +12,10 @@ class ActivityPub::TagManager
public: 'https://www.w3.org/ns/activitystreams#Public',
}.freeze
def public_collection?(uri)
uri == COLLECTIONS[:public] || uri == 'as:Public' || uri == 'Public'
end
def url_for(target)
return target.url if target.respond_to?(:local?) && !target.local?

View File

@@ -29,7 +29,7 @@ class DeliveryFailureTracker
class << self
def without_unavailable(urls)
unavailable_domains_map = Rails.cache.fetch('unavailable_domains') { UnavailableDomain.pluck(:domain).each_with_object({}) { |domain, hash| hash[domain] = true } }
unavailable_domains_map = Rails.cache.fetch('unavailable_domains') { UnavailableDomain.pluck(:domain).index_with(true) }
urls.reject do |url|
host = Addressable::URI.parse(url).normalized_host

View File

@@ -591,12 +591,12 @@ class FeedManager
arr
end
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).each_with_object({}) { |domain, mapping| mapping[domain] = true }
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).index_with(true)
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).index_with(true)
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).index_with(true)
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).index_with(true)
crutches
end

View File

@@ -64,7 +64,7 @@ module Settings
class << self
def default_settings
defaulting = DEFAULTING_TO_UNSCOPED.each_with_object({}) { |k, h| h[k] = Setting[k] }
defaulting = DEFAULTING_TO_UNSCOPED.index_with { |k| Setting[k] }
Setting.default_settings.merge!(defaulting)
end
end

View File

@@ -67,7 +67,7 @@ module AccountInteractions
private
def follow_mapping(query, field)
query.pluck(field).each_with_object({}) { |id, mapping| mapping[id] = true }
query.pluck(field).index_with(true)
end
end

View File

@@ -68,7 +68,6 @@ module Omniauthable
def user_params_from_auth(email, auth)
{
email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0, 20],
agreement: true,
external: true,
account_attributes: {

View File

@@ -32,7 +32,7 @@ class Report < ApplicationRecord
scope :unresolved, -> { where(action_taken: false) }
scope :resolved, -> { where(action_taken: true) }
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].each_with_object({}) { |k, h| h[k] = { user: [:invite_request, :invite] } }) }
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) }
validates :comment, length: { maximum: 1000 }

View File

@@ -468,7 +468,7 @@ class User < ApplicationRecord
end
def validate_email_dns?
email_changed? && !(Rails.env.test? || Rails.env.development?)
email_changed? && !external? && !(Rails.env.test? || Rails.env.development?)
end
def invite_text_required?

View File

@@ -188,8 +188,7 @@ class DeleteAccountService < BaseService
ids = favourites.pluck(:status_id)
StatusStat.where(status_id: ids).update_all('favourites_count = GREATEST(0, favourites_count - 1)')
Chewy.strategy.current.update(StatusesIndex::Status, ids) if Chewy.enabled?
# Rails.cache.delete_multi would be better, but we don't have it yet
ids.each { |id| Rails.cache.delete("statuses/#{id}") }
Rails.cache.delete_multi(ids.map { |id| "statuses/#{id}" })
favourites.delete_all
end
end

View File

@@ -45,7 +45,7 @@ class ImportService < BaseService
items = @data.take(ROWS_PROCESSING_LIMIT).map { |row| row['#domain'].strip }
if @import.overwrite?
presence_hash = items.each_with_object({}) { |id, mapping| mapping[id] = true }
presence_hash = items.index_with(true)
@account.domain_blocks.find_each do |domain_block|
if presence_hash[domain_block.domain]
@@ -96,7 +96,7 @@ class ImportService < BaseService
items = @data.take(ROWS_PROCESSING_LIMIT).map { |row| row['#uri'].strip }
if @import.overwrite?
presence_hash = items.each_with_object({}) { |id, mapping| mapping[id] = true }
presence_hash = items.index_with(true)
@account.bookmarks.find_each do |bookmark|
if presence_hash[bookmark.status.uri]

View File

@@ -10,7 +10,7 @@ class EmailMxValidator < ActiveModel::Validator
if domain.blank?
user.errors.add(:email, :invalid)
else
elsif !on_allowlist?(domain)
ips, hostnames = resolve_mx(domain)
if ips.empty?
@@ -33,6 +33,12 @@ class EmailMxValidator < ActiveModel::Validator
nil
end
def on_allowlist?(domain)
return false if Rails.configuration.x.email_domains_whitelist.blank?
Rails.configuration.x.email_domains_whitelist.include?(domain)
end
def resolve_mx(domain)
hostnames = []
ips = []

View File

@@ -4,6 +4,6 @@
= image_tag action_log.account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar'
.log-entry__content
.log-entry__title
= t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target')).html_safe
= t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}_html", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target'))
.log-entry__timestamp
%time.formatted{ datetime: action_log.created_at.iso8601 }

View File

@@ -1,6 +1,6 @@
.speech-bubble.positive
.speech-bubble__bubble
= t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target')).html_safe
= t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}_html", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target'))
.speech-bubble__owner
= admin_account_link_to(action_log.account)
%time.formatted{ datetime: action_log.created_at.iso8601 }= l action_log.created_at

View File

@@ -39,7 +39,7 @@
= render partial: 'layouts/theme', object: @theme
- if Setting.custom_css.present?
= stylesheet_link_tag custom_css_path, media: 'all'
= stylesheet_link_tag custom_css_path, host: request.host, media: 'all'
%body{ class: body_classes }
= content_for?(:content) ? yield(:content) : yield