Skip to content

Commit

Permalink
Merge pull request #2885 from ClearlyClaire/glitch-soc/backports-4.3
Browse files Browse the repository at this point in the history
Merge upstream changes (stable-4.3)
  • Loading branch information
ClearlyClaire authored Oct 16, 2024
2 parents a3f4030 + 8d37565 commit 3a5e83b
Show file tree
Hide file tree
Showing 79 changed files with 850 additions and 532 deletions.
9 changes: 9 additions & 0 deletions .env.production.sample
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ DB_PORT=5432
SECRET_KEY_BASE=
OTP_SECRET=

# Encryption secrets
# ------------------
# Must be available (and set to same values) for all server processes
# These are private/secret values, do not share outside hosting environment
# Use `bin/rails db:encryption:init` to generate fresh secrets
# ------------------
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=
# ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=

# Web Push
# --------
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
postgres:
- 14-alpine
- 15-alpine
- 16-alpine
- 17-alpine

services:
postgres:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg libpam-dev
additional-system-dependencies: ffmpeg imagemagick libpam-dev

- name: Load database schema
run: |
Expand Down Expand Up @@ -245,7 +245,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
additional-system-dependencies: ffmpeg libpam-dev

- name: Load database schema
run: './bin/rails db:create db:schema:load db:seed'
Expand Down Expand Up @@ -325,7 +325,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg
additional-system-dependencies: ffmpeg imagemagick

- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
Expand Down Expand Up @@ -445,7 +445,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg
additional-system-dependencies: ffmpeg imagemagick

- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/statuses/translations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ def create
private

def set_translation
@translation = TranslateStatusService.new.call(@status, content_locale)
@translation = TranslateStatusService.new.call(@status, I18n.locale.to_s)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return;
}

setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);

const handleLeftNav = useCallback(() => {
Expand All @@ -146,8 +151,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return;
}

setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);

const handleDismiss = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import { replyCompose } from 'flavours/glitch/actions/compose';
import { toggleReblog, toggleFavourite } from 'flavours/glitch/actions/interactions';
import { openModal } from 'flavours/glitch/actions/modal';
Expand Down Expand Up @@ -161,16 +163,20 @@ class Footer extends ImmutablePureComponent {
replyTitle = intl.formatMessage(messages.replyAll);
}

let reblogTitle = '';
let reblogTitle, reblogIconComponent;

if (status.get('reblogged')) {
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
} else if (publicStatus) {
reblogTitle = intl.formatMessage(messages.reblog);
reblogIconComponent = RepeatIcon;
} else if (reblogPrivate) {
reblogTitle = intl.formatMessage(messages.reblog_private);
reblogIconComponent = RepeatPrivateIcon;
} else {
reblogTitle = intl.formatMessage(messages.cannot_reblog);
reblogIconComponent = RepeatDisabledIcon;
}

let replyButton = null;
Expand Down Expand Up @@ -201,7 +207,7 @@ class Footer extends ImmutablePureComponent {
return (
<div className='picture-in-picture__footer'>
{replyButton}
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={status.get('url')} />}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export const MuteModal = ({ accountId, acct }) => {
<div className='safety-action-modal__bottom__collapsible'>
<div className='safety-action-modal__field-group'>
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='21600' label={intl.formatMessage(messages.hours, { number: 6 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
Expand Down
14 changes: 5 additions & 9 deletions app/javascript/flavours/glitch/styles/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11354,21 +11354,17 @@ noscript {
color: $darker-text-color;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
max-height: 4 * 22px;
max-height: none;
overflow: hidden;

p {
display: none;

&:first-child {
display: initial;
}
}

p,
a {
color: inherit;
}

p {
margin-bottom: 8px;
}
}

.reply-indicator__attachments {
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/flavours/glitch/styles/rtl.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ body.rtl {
direction: rtl;
}

.column-back-button__icon {
transform: scale(-1, 1);
}

.simple_form select {
background: $ui-base-color
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return;
}

setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);

const handleLeftNav = useCallback(() => {
Expand All @@ -146,8 +151,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return;
}

setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);

const handleDismiss = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import { replyCompose } from 'mastodon/actions/compose';
import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
import { openModal } from 'mastodon/actions/modal';
Expand Down Expand Up @@ -159,22 +161,26 @@ class Footer extends ImmutablePureComponent {
replyTitle = intl.formatMessage(messages.replyAll);
}

let reblogTitle = '';
let reblogTitle, reblogIconComponent;

if (status.get('reblogged')) {
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
} else if (publicStatus) {
reblogTitle = intl.formatMessage(messages.reblog);
reblogIconComponent = RepeatIcon;
} else if (reblogPrivate) {
reblogTitle = intl.formatMessage(messages.reblog_private);
reblogIconComponent = RepeatPrivateIcon;
} else {
reblogTitle = intl.formatMessage(messages.cannot_reblog);
reblogIconComponent = RepeatDisabledIcon;
}

return (
<div className='picture-in-picture__footer'>
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export const MuteModal = ({ accountId, acct }) => {
<div className='safety-action-modal__bottom__collapsible'>
<div className='safety-action-modal__field-group'>
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='21600' label={intl.formatMessage(messages.hours, { number: 6 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
Expand Down
14 changes: 5 additions & 9 deletions app/javascript/styles/mastodon/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10804,21 +10804,17 @@ noscript {
color: $darker-text-color;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
max-height: 4 * 22px;
max-height: none;
overflow: hidden;

p {
display: none;

&:first-child {
display: initial;
}
}

p,
a {
color: inherit;
}

p {
margin-bottom: 8px;
}
}

.reply-indicator__attachments {
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/styles/mastodon/rtl.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ body.rtl {
direction: rtl;
}

.column-back-button__icon {
transform: scale(-1, 1);
}

.simple_form select {
background: $ui-base-color
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
Expand Down
10 changes: 9 additions & 1 deletion app/lib/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def url_from_configured_asset_host
end

def cdn_host_value
s3_alias_host || s3_cloudfront_host || azure_alias_host || s3_hostname_host
s3_alias_host || s3_cloudfront_host || azure_alias_host || s3_hostname_host || swift_object_url
end

def paperclip_root_url
Expand Down Expand Up @@ -76,6 +76,14 @@ def s3_hostname_host
host_to_url ENV.fetch('S3_HOSTNAME', nil)
end

def swift_object_url
url = ENV.fetch('SWIFT_OBJECT_URL', nil)
return if url.blank? || !url.start_with?('https://')

url += '/' unless url.end_with?('/')
url
end

def uri_from_configuration_and_string(host_string)
Addressable::URI.parse("#{host_protocol}://#{host_string}").tap do |uri|
uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
Expand Down
3 changes: 2 additions & 1 deletion app/models/follow_recommendation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ class FollowRecommendation < ApplicationRecord
belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
belongs_to :account

scope :localized, ->(locale) { joins(:account_summary).merge(AccountSummary.localized(locale)) }
scope :unsupressed, -> { where.not(FollowRecommendationSuppression.where(FollowRecommendationSuppression.arel_table[:account_id].eq(arel_table[:account_id])).select(1).arel.exists) }
scope :localized, ->(locale) { unsupressed.joins(:account_summary).merge(AccountSummary.localized(locale)) }
end
10 changes: 8 additions & 2 deletions app/services/translate_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class TranslateStatusService < BaseService
def call(status, target_language)
@status = status
@source_texts = source_texts

target_language = target_language.split(/[_-]/).first unless target_languages.include?(target_language)
@target_language = target_language

raise Mastodon::NotPermittedError unless permitted?
Expand All @@ -32,11 +34,15 @@ def translation_backend
def permitted?
return false unless @status.distributable? && TranslationService.configured?

languages[@status.language]&.include?(@target_language)
target_languages.include?(@target_language)
end

def languages
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { TranslationService.configured.languages }
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { translation_backend.languages }
end

def target_languages
languages[@status.language] || []
end

def content_hash
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/invites/_invite.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%tr
%tr{ id: dom_id(invite) }
%td
.input-copy
.input-copy__wrapper
Expand Down
1 change: 1 addition & 0 deletions config/initializers/active_record_encryption.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
Run `bin/rails db:encryption:init` to generate new secrets and then assign the environment variables.
Do not change the secrets once they are set, as doing so may cause data loss and other issues that will be difficult or impossible to recover from.
MESSAGE
end

Expand Down
11 changes: 11 additions & 0 deletions lib/tasks/db.rake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ namespace :db do
namespace :encryption do
desc 'Generate a set of keys for configuring Active Record encryption in a given environment'
task :init do # rubocop:disable Rails/RakeEnvironment
if %w(
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
).any? { |key| ENV.key?(key) }
pastel = Pastel.new
puts pastel.red(<<~MSG)
WARNING: It looks like encryption secrets have already been set. Please ensure you are not changing secrets for a Mastodon installation that already uses them, as this will cause data loss and other issues that are difficult to recover from.
MSG
end

puts <<~MSG
Add the following secret environment variables to your Mastodon environment (e.g. .env.production), ensure they are shared across all your nodes and do not change them after they are set:#{' '}
Expand Down
Loading

0 comments on commit 3a5e83b

Please sign in to comment.