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

Conflicts:
- `.github/workflows/build-image.yml`:
  Upstream switched to pushing to both DockerHub and GitHub Container
  Repository, while glitch-soc was already pushing to the latter only.
  Updated our configuration to be slightly more consistent with upstream's
  naming and styling, but kept our behavior.
- `Gemfile.lock`:
  Updated dependencies textually too close to glitch-soc only hcaptcha
  dependency.
  Updated dependencies as upstream did.
- `README.md`:
  Upstream updated its README, but we have a completely different one.
  Kept our README, though it probably should be reworked at some point.
- `app/views/auth/sessions/two_factor.html.haml`:
  Minor style fix upstream that's on a line glitch-soc removed because
  of its different theming system.
  Kept our file as is.
- `spec/controllers/health_controller_spec.rb`:
  This file apparently did not exist upstream, upstream created it with
  different contents but it is functionally the same.
  Switched to upstream's version of the file.
- `spec/presenters/instance_presenter_spec.rb`:
  Upstream changed the specs around `GITHUB_REPOSITORY`, while glitch-soc
  had its own code because it's a fork and does not have the same default
  source URL.
  Took upstream's change, but with glitch-soc's repo as the default case.
- `yarn.lock`:
  Upstream dependencies textually too close to a glitch-soc only one.
  Updated dependencies as upstream did.
This commit is contained in:
Claire
2023-03-15 09:08:12 +01:00
171 changed files with 2089 additions and 1778 deletions

View File

@@ -63,7 +63,11 @@ class ApplicationController < ActionController::Base
end
def after_sign_out_path_for(_resource_or_scope)
new_user_session_path
if ENV['OMNIAUTH_ONLY'] == 'true' && ENV['OIDC_ENABLED'] == 'true'
'/auth/auth/openid_connect/logout'
else
new_user_session_path
end
end
protected

View File

@@ -33,7 +33,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def after_sign_in_path_for(resource)
if resource.email_present?
root_path
stored_location_for(resource) || root_path
else
auth_setup_path(missing_email: '1')
end

View File

@@ -52,7 +52,7 @@ module Settings
end
else
flash[:error] = I18n.t('webauthn_credentials.create.error')
status = :internal_server_error
status = :unprocessable_entity
end
else
flash[:error] = t('webauthn_credentials.create.error')

View File

@@ -56,6 +56,8 @@ const messages = defineMessages({
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
editMessage: { id: 'confirmations.edit.message', defaultMessage: 'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
});
@@ -149,7 +151,18 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
},
onEdit (status, history) {
dispatch(editStatus(status.get('id'), history));
dispatch((_, getState) => {
let state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.editMessage),
confirm: intl.formatMessage(messages.editConfirm),
onConfirm: () => dispatch(editStatus(status.get('id'), history)),
}));
} else {
dispatch(editStatus(status.get('id'), history));
}
});
},
onTranslate (status) {

View File

@@ -166,6 +166,8 @@
"confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
"confirmations.domain_block.confirm": "Block entire domain",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
"confirmations.edit.confirm": "Edit",
"confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.logout.confirm": "Log out",
"confirmations.logout.message": "Are you sure you want to log out?",
"confirmations.mute.confirm": "Mute",

View File

@@ -4509,6 +4509,7 @@ a.status-card.compact:hover {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: $secondary-text-color;
font-size: 18px;
font-weight: 500;

View File

@@ -12,7 +12,7 @@ class ActivityPub::Forwarder
end
def forward!
ActivityPub::LowPriorityDeliveryWorker.push_bulk(inboxes) do |inbox_url|
ActivityPub::LowPriorityDeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
[payload, signature_account_id, inbox_url]
end
end

View File

@@ -31,7 +31,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
def running_version
@running_version ||= begin
Chewy.client.info['version']['number']
rescue Faraday::ConnectionFailed
rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error
nil
end
end

View File

@@ -18,7 +18,7 @@ class PlainTextFormatter
if local?
text
else
strip_tags(insert_newlines).chomp
html_entities.decode(strip_tags(insert_newlines)).chomp
end
end
@@ -27,4 +27,8 @@ class PlainTextFormatter
def insert_newlines
text.gsub(NEWLINE_TAGS_RE) { |match| "#{match}\n" }
end
def html_entities
HTMLEntities.new
end
end

View File

@@ -1,69 +0,0 @@
# frozen_string_literal: true
class TOCGenerator
TARGET_ELEMENTS = %w(h1 h2 h3 h4 h5 h6).freeze
LISTED_ELEMENTS = %w(h2 h3).freeze
class Section
attr_accessor :depth, :title, :children, :anchor
def initialize(depth, title, anchor)
@depth = depth
@title = title
@children = []
@anchor = anchor
end
delegate :<<, to: :children
end
def initialize(source_html)
@source_html = source_html
@processed = false
@target_html = ''
@headers = []
@slugs = Hash.new { |h, k| h[k] = 0 }
end
def html
parse_and_transform unless @processed
@target_html
end
def toc
parse_and_transform unless @processed
@headers
end
private
def parse_and_transform
return if @source_html.blank?
parsed_html = Nokogiri::HTML.fragment(@source_html)
parsed_html.traverse do |node|
next unless TARGET_ELEMENTS.include?(node.name)
anchor = node['id'] || node.text.parameterize.presence || 'sec'
@slugs[anchor] += 1
anchor = "#{anchor}-#{@slugs[anchor]}" if @slugs[anchor] > 1
node['id'] = anchor
next unless LISTED_ELEMENTS.include?(node.name)
depth = node.name[1..-1]
latest_section = @headers.last
if latest_section.nil? || latest_section.depth >= depth
@headers << Section.new(depth, node.text, anchor)
else
latest_section << Section.new(depth, node.text, anchor)
end
end
@target_html = parsed_html.to_s
@processed = true
end
end

View File

@@ -516,11 +516,14 @@ class User < ApplicationRecord
def prepare_new_user!
BootstrapTimelineWorker.perform_async(account_id)
ActivityTracker.increment('activity:accounts:local')
ActivityTracker.record('activity:logins', id)
UserMailer.welcome(self).deliver_later
TriggerWebhookWorker.perform_async('account.approved', 'Account', account_id)
end
def prepare_returning_user!
return unless confirmed?
ActivityTracker.record('activity:logins', id)
regenerate_feed! if needs_feed_update?
end

View File

@@ -257,11 +257,11 @@ class DeleteAccountService < BaseService
end
def delete_actor!
ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes, limit: 1_000) do |inbox_url|
[delete_actor_json, @account.id, inbox_url]
end
ActivityPub::LowPriorityDeliveryWorker.push_bulk(low_priority_delivery_inboxes) do |inbox_url|
ActivityPub::LowPriorityDeliveryWorker.push_bulk(low_priority_delivery_inboxes, limit: 1_000) do |inbox_url|
[delete_actor_json, @account.id, inbox_url]
end
end

View File

@@ -90,7 +90,7 @@ class RemoveStatusService < BaseService
status_reach_finder = StatusReachFinder.new(@status, unsafe: true)
ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes, limit: 1_000) do |inbox_url|
[signed_activity_json, @account.id, inbox_url]
end
end

View File

@@ -31,13 +31,13 @@ class SuspendAccountService < BaseService
# counterpart to this operation, i.e. you can't then force a remote
# account to re-follow you, so this part is not reversible.
follows = Follow.where(account: @account).to_a
Follow.where(account: @account).find_in_batches do |follows|
ActivityPub::DeliveryWorker.push_bulk(follows) do |follow|
[Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url]
end
ActivityPub::DeliveryWorker.push_bulk(follows) do |follow|
[Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url]
follows.each(&:destroy)
end
follows.each(&:destroy)
end
def distribute_update_actor!
@@ -45,7 +45,7 @@ class SuspendAccountService < BaseService
account_reach_finder = AccountReachFinder.new(@account)
ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes, limit: 1_000) do |inbox_url|
[signed_activity_json, @account.id, inbox_url]
end
end

View File

@@ -41,7 +41,7 @@ class UnsuspendAccountService < BaseService
account_reach_finder = AccountReachFinder.new(@account)
ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes, limit: 1_000) do |inbox_url|
[signed_activity_json, @account.id, inbox_url]
end
end

View File

@@ -22,7 +22,7 @@ class UpdateAccountService < BaseService
def authorize_all_follow_requests(account)
follow_requests = FollowRequest.where(target_account: account)
follow_requests = follow_requests.preload(:account).select { |req| !req.account.silenced? }
AuthorizeFollowWorker.push_bulk(follow_requests) do |req|
AuthorizeFollowWorker.push_bulk(follow_requests, limit: 1_000) do |req|
[req.account_id, req.target_account_id]
end
end

View File

@@ -206,7 +206,7 @@
- if @deletion_request.present?
= link_to t('admin.accounts.delete'), admin_account_path(@account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, @account)
- else
%div.action-buttons
.action-buttons
%div
- if @account.local? && @account.user_approved?
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
@@ -276,9 +276,9 @@
%hr.spacer/
- if @account.user&.invite_request&.text&.present?
%div.speech-bubble
%div.speech-bubble__bubble
.speech-bubble
.speech-bubble__bubble
= @account.user&.invite_request&.text
%div.speech-bubble__owner
.speech-bubble__owner
= admin_account_link_to @account
= t('admin.accounts.invite_request_text')

View File

@@ -16,7 +16,7 @@
= select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key]}, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all')
- if @action_logs.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.action_logs.empty'
- else
.report-notes

View File

@@ -12,7 +12,7 @@
%li= filter_link_to safe_join([t('admin.announcements.live'), "(#{number_with_delimiter(Announcement.published.count)})"], ' '), published: '1', unpublished: nil
- if @announcements.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.announcements.empty'
- else
.announcements-list

View File

@@ -10,7 +10,7 @@
%li= filter_link_to t('admin.trends.rejected'), status: 'rejected'
- if @appeals.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.disputes.appeals.empty'
- else
.announcements-list

View File

@@ -44,7 +44,7 @@
%hr.spacer/
- if @instances.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.instances.empty'
- else
= render partial: 'instance', collection: @instances

View File

@@ -54,15 +54,15 @@
.strike-card__statuses-list__item
- if (status = status_map[status_id.to_i])
.one-liner
= link_to short_account_status_url(@report.target_account, status_id), class: 'emojify' do
= one_line_preview(status)
.emojify= one_line_preview(status)
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
.strike-card__statuses-list__item__meta
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
- unless status.application.nil?
·
= status.application.name

View File

@@ -18,7 +18,7 @@
%hr.spacer/
- if @rules.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.rules.empty'
- else
.announcements-list

View File

@@ -17,7 +17,7 @@
%hr.spacer/
- if @warning_presets.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.warning_presets.empty'
- else
.announcements-list

View File

@@ -9,7 +9,7 @@
%hr.spacer/
- if @webhooks.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'admin.webhooks.empty'
- else
.applications-list

View File

@@ -1,16 +0,0 @@
.hero-widget
.hero-widget__img
= image_tag @instance_presenter.thumbnail&.file&.url(:'@1x') || asset_pack_path('media/images/preview.png'), alt: @instance_presenter.title
.hero-widget__text
%p= @instance_presenter.description.html_safe.presence || t('about.about_mastodon_html')
- if Setting.trends && !(user_signed_in? && !current_user.setting_trends)
- trends = Trends.tags.query.allowed.limit(3)
- unless trends.empty?
.endorsements-widget.trends-widget
%h4.emojify= t('footer.trending_now')
- trends.each do |tag|
= react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer, scope: current_user, scope_name: :current_user).as_json

View File

@@ -50,15 +50,15 @@
.strike-card__statuses-list__item
- if (status = status_map[status_id.to_i])
.one-liner
= link_to short_account_status_url(@strike.target_account, status_id), class: 'emojify' do
= one_line_preview(status)
.emojify= one_line_preview(status)
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
.strike-card__statuses-list__item__meta
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
- unless status.application.nil?
·
= status.application.name

View File

@@ -35,6 +35,6 @@
= render 'keyword_fields', f: keyword
%tfoot
%tr
%td{ colspan: 3}
%td{ colspan: 3 }
= link_to_add_association f, :keywords, class: 'table-action-link', partial: 'keyword_fields', 'data-association-insertion-node': '.keywords-table tbody', 'data-association-insertion-method': 'append' do
= safe_join([fa_icon('plus'), t('filters.edit.add_keyword')])

View File

@@ -5,7 +5,7 @@
= link_to t('filters.new.title'), new_filter_path, class: 'button'
- if @filters.empty?
%div.muted-hint.center-text= t 'filters.index.empty'
.muted-hint.center-text= t 'filters.index.empty'
- else
.applications-list
= render partial: 'filter', collection: @filters

View File

@@ -1,9 +1,11 @@
-# Link to the "Next" page
-# available local variables
-# url: url to the next page
-# current_page: a page object for the currently displayed page
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-#
Link to the "Next" page
available local variables
url: url to the next page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
%span.next
= link_to_unless current_page.last?, safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), url, rel: 'next', remote: remote

View File

@@ -1,10 +1,11 @@
-# The container tag
-# available local variables
-# current_page: a page object for the currently displayed page
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-# paginator: the paginator that renders the pagination tags inside
-#
The container tag
available local variables
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
paginator: the paginator that renders the pagination tags inside
= paginator.render do
%nav.pagination
= prev_page_tag unless current_page.first?

View File

@@ -1,9 +1,10 @@
-# Link to the "Previous" page
-# available local variables
-# url: url to the previous page
-# current_page: a page object for the currently displayed page
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-#
Link to the "Previous" page
available local variables
url: url to the previous page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
%span.prev
= link_to_unless current_page.first?, safe_join([fa_icon('chevron-left'), t('pagination.prev')], ' '), url, rel: 'prev', remote: remote

View File

@@ -12,6 +12,6 @@
.modal-layout__mastodon
%div
%img{alt: '', draggable: 'false', src: mascot_url }
%img{ alt: '', draggable: 'false', src: mascot_url }
= render template: 'layouts/application'

View File

@@ -26,11 +26,11 @@
= "@#{status.account.pretty_acct}"
- if status.spoiler_text?
%div.auto-dir
.auto-dir
%p
= status.spoiler_text
%div.auto-dir
.auto-dir
= status_content_format(status)
- if status.ordered_media_attachments.size > 0

View File

@@ -3,5 +3,5 @@
%p= t('doorkeeper.authorizations.show.title')
.input-copy
.input-copy__wrapper
%input{ type: 'text', class: 'oauth-code', spellcheck: 'false', readonly: true, value: params[:code] }
%input.oauth-code{ type: 'text', spellcheck: 'false', readonly: true, value: params[:code] }
%button{ type: :button }= t('generic.copy')

View File

@@ -5,7 +5,7 @@
= link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button'
- if @applications.empty?
%div.muted-hint.center-text=t 'doorkeeper.applications.index.empty'
.muted-hint.center-text= t 'doorkeeper.applications.index.empty'
- else
.table-wrapper
%table.table

View File

@@ -6,7 +6,7 @@
.table-wrapper
%table.table
%tbody
%tr
%tr
%th= t('doorkeeper.applications.show.application_id')
%td
%code= @application.uid
@@ -15,7 +15,7 @@
%td
%code= @application.secret
%tr
%th{ rowspan: 2}= t('applications.your_token')
%th{ rowspan: 2 }= t('applications.your_token')
%td
%code= current_user.token_for_app(@application).token
%tr

View File

@@ -6,7 +6,7 @@
%hr.spacer/
- if @login_activities.empty?
%div.muted-hint.center-text
.muted-hint.center-text
= t 'login_activities.empty'
- else
.announcements-list

View File

@@ -1,5 +1,5 @@
- thumbnail = @instance_presenter.thumbnail
- description ||= strip_tags(@instance_presenter.description.presence || t('about.about_mastodon_html'))
- description ||= @instance_presenter.description.presence || strip_tags(t('about.about_mastodon_html'))
%meta{ name: 'description', content: description }/

View File

@@ -21,7 +21,7 @@
%span.poll__chart
- else
%label.poll__option><
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil }><
%span.poll__option__text
= prerender_custom_emojis(h(option.title), status.emojis)
.poll__footer

View File

@@ -12,7 +12,7 @@ class ActivityPub::DistributePollUpdateWorker
return if @status.preloadable_poll.nil? || @status.local_only?
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
[payload, @account.id, inbox_url]
end

View File

@@ -10,7 +10,7 @@ class ActivityPub::MoveDistributionWorker
@migration = AccountMigration.find(migration_id)
@account = @migration.account
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
[signed_payload, @account.id, inbox_url]
end

View File

@@ -25,7 +25,7 @@ class ActivityPub::RawDistributionWorker
def distribute!
return if inboxes.empty?
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
[payload, source_account_id, inbox_url, options]
end
end