From 546672e292dc3218e996048464c4c52e5d00f766 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 28 Aug 2022 04:00:39 +0200 Subject: Change "Allow trends without prior review" setting to include statuses (#17977) * Change "Allow trends without prior review" setting to include posts * Fix i18n-tasks --- config/initializers/simple_form.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'config/initializers') diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 3a2097d2f..92cffc5a2 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -11,7 +11,10 @@ end module RecommendedComponent def recommended(_wrapper_options = nil) return unless options[:recommended] - options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t('simple_form.recommended'), class: 'recommended')]) } + + key = options[:recommended].is_a?(Symbol) ? options[:recommended] : :recommended + options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t(key, scope: 'simple_form'), class: key)]) } + nil end end -- cgit From 0d6b878808a02aa4a544e894f06419c0f612c163 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 23 Sep 2022 23:00:12 +0200 Subject: Add user content translations with configurable backends (#19218) --- .../api/v1/statuses/translations_controller.rb | 29 ++++++++++++ app/javascript/mastodon/actions/statuses.js | 39 +++++++++++++++- app/javascript/mastodon/components/status.js | 16 ++++++- .../mastodon/components/status_content.js | 31 +++++++++---- .../mastodon/containers/status_container.js | 10 ++++ .../features/status/components/detailed_status.js | 13 +++++- app/javascript/mastodon/features/status/index.js | 13 ++++++ app/javascript/mastodon/reducers/statuses.js | 6 +++ app/lib/translation_service.rb | 23 ++++++++++ app/lib/translation_service/deepl.rb | 53 ++++++++++++++++++++++ app/lib/translation_service/libre_translate.rb | 43 ++++++++++++++++++ app/lib/translation_service/translation.rb | 5 ++ app/serializers/rest/translation_serializer.rb | 9 ++++ app/services/translate_status_service.rb | 24 ++++++++++ config/initializers/inflections.rb | 1 + config/routes.rb | 2 + 16 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 app/controllers/api/v1/statuses/translations_controller.rb create mode 100644 app/lib/translation_service.rb create mode 100644 app/lib/translation_service/deepl.rb create mode 100644 app/lib/translation_service/libre_translate.rb create mode 100644 app/lib/translation_service/translation.rb create mode 100644 app/serializers/rest/translation_serializer.rb create mode 100644 app/services/translate_status_service.rb (limited to 'config/initializers') diff --git a/app/controllers/api/v1/statuses/translations_controller.rb b/app/controllers/api/v1/statuses/translations_controller.rb new file mode 100644 index 000000000..540b17d00 --- /dev/null +++ b/app/controllers/api/v1/statuses/translations_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Api::V1::Statuses::TranslationsController < Api::BaseController + include Authorization + + before_action -> { doorkeeper_authorize! :read, :'read:statuses' } + before_action :set_status + before_action :set_translation + + rescue_from TranslationService::NotConfiguredError, with: :not_found + rescue_from TranslationService::UnexpectedResponseError, TranslationService::QuotaExceededError, TranslationService::TooManyRequestsError, with: :service_unavailable + + def create + render json: @translation, serializer: REST::TranslationSerializer + end + + private + + def set_status + @status = Status.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end + + def set_translation + @translation = TranslateStatusService.new.call(@status, content_locale) + end +end diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 32a4f1f85..4ae1b21e0 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -34,6 +34,11 @@ export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST'; export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS'; export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL'; +export const STATUS_TRANSLATE_REQUEST = 'STATUS_TRANSLATE_REQUEST'; +export const STATUS_TRANSLATE_SUCCESS = 'STATUS_TRANSLATE_SUCCESS'; +export const STATUS_TRANSLATE_FAIL = 'STATUS_TRANSLATE_FAIL'; +export const STATUS_TRANSLATE_UNDO = 'STATUS_TRANSLATE_UNDO'; + export function fetchStatusRequest(id, skipLoading) { return { type: STATUS_FETCH_REQUEST, @@ -309,4 +314,36 @@ export function toggleStatusCollapse(id, isCollapsed) { id, isCollapsed, }; -} +}; + +export const translateStatus = id => (dispatch, getState) => { + dispatch(translateStatusRequest(id)); + + api(getState).post(`/api/v1/statuses/${id}/translate`).then(response => { + dispatch(translateStatusSuccess(id, response.data)); + }).catch(error => { + dispatch(translateStatusFail(id, error)); + }); +}; + +export const translateStatusRequest = id => ({ + type: STATUS_TRANSLATE_REQUEST, + id, +}); + +export const translateStatusSuccess = (id, translation) => ({ + type: STATUS_TRANSLATE_SUCCESS, + id, + translation, +}); + +export const translateStatusFail = (id, error) => ({ + type: STATUS_TRANSLATE_FAIL, + id, + error, +}); + +export const undoStatusTranslation = id => ({ + type: STATUS_TRANSLATE_UNDO, + id, +}); diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 6fc132bf5..0d3b51f07 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -85,6 +85,7 @@ class Status extends ImmutablePureComponent { onHeightChange: PropTypes.func, onToggleHidden: PropTypes.func, onToggleCollapsed: PropTypes.func, + onTranslate: PropTypes.func, muted: PropTypes.bool, hidden: PropTypes.bool, unread: PropTypes.bool, @@ -171,6 +172,10 @@ class Status extends ImmutablePureComponent { this.props.onToggleCollapsed(this._properStatus(), isCollapsed); } + handleTranslate = () => { + this.props.onTranslate(this._properStatus()); + } + renderLoadingMediaGallery () { return
; } @@ -512,7 +517,16 @@ class Status extends ImmutablePureComponent {
- + {media} diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 724165ada..c8f7bc095 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -1,7 +1,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import Permalink from './permalink'; import classnames from 'classnames'; import PollContainer from 'mastodon/containers/poll_container'; @@ -10,7 +10,8 @@ import { autoPlayGif } from 'mastodon/initial_state'; const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) -export default class StatusContent extends React.PureComponent { +export default @injectIntl +class StatusContent extends React.PureComponent { static contextTypes = { router: PropTypes.object, @@ -21,9 +22,11 @@ export default class StatusContent extends React.PureComponent { expanded: PropTypes.bool, showThread: PropTypes.bool, onExpandedToggle: PropTypes.func, + onTranslate: PropTypes.func, onClick: PropTypes.func, collapsable: PropTypes.bool, onCollapsedToggle: PropTypes.func, + intl: PropTypes.object, }; state = { @@ -163,20 +166,26 @@ export default class StatusContent extends React.PureComponent { } } + handleTranslate = () => { + this.props.onTranslate(); + } + setRef = (c) => { this.node = c; } render () { - const { status } = this.props; + const { status, intl } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const renderReadMore = this.props.onClick && status.get('collapsed'); const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']); + const renderTranslate = this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && intl.locale !== status.get('language'); + const languageNames = new Intl.DisplayNames([intl.locale], { type: 'language' }); - const content = { __html: status.get('contentHtml') }; + const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') }; - const lang = status.get('language'); + const lang = status.get('translation') ? intl.locale : status.get('language'); const classNames = classnames('status__content', { 'status__content--with-action': this.props.onClick && this.context.router, 'status__content--with-spoiler': status.get('spoiler_text').length > 0, @@ -195,6 +204,12 @@ export default class StatusContent extends React.PureComponent { ); + const translateButton = ( + + ); + if (status.get('spoiler_text').length > 0) { let mentionsPlaceholder = ''; @@ -223,7 +238,7 @@ export default class StatusContent extends React.PureComponent {
{!hidden && !!status.get('poll') && } - + {!hidden && renderTranslate && translateButton} {renderViewThread && showThreadButton}
); @@ -233,7 +248,7 @@ export default class StatusContent extends React.PureComponent {
{!!status.get('poll') && } - + {renderTranslate && translateButton} {renderViewThread && showThreadButton}
, ]; @@ -249,7 +264,7 @@ export default class StatusContent extends React.PureComponent {
{!!status.get('poll') && } - + {renderTranslate && translateButton} {renderViewThread && showThreadButton}
); diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index 28698b082..9280a6ee3 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -25,6 +25,8 @@ import { revealStatus, toggleStatusCollapse, editStatus, + translateStatus, + undoStatusTranslation, } from '../actions/statuses'; import { unmuteAccount, @@ -150,6 +152,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ dispatch(editStatus(status.get('id'), history)); }, + onTranslate (status) { + if (status.get('translation')) { + dispatch(undoStatusTranslation(status.get('id'))); + } else { + dispatch(translateStatus(status.get('id'))); + } + }, + onDirect (account, router) { dispatch(directCompose(account, router)); }, diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js index 5c43c2038..320a847f7 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.js +++ b/app/javascript/mastodon/features/status/components/detailed_status.js @@ -37,6 +37,7 @@ class DetailedStatus extends ImmutablePureComponent { onOpenMedia: PropTypes.func.isRequired, onOpenVideo: PropTypes.func.isRequired, onToggleHidden: PropTypes.func.isRequired, + onTranslate: PropTypes.func.isRequired, measureHeight: PropTypes.bool, onHeightChange: PropTypes.func, domain: PropTypes.string.isRequired, @@ -103,6 +104,11 @@ class DetailedStatus extends ImmutablePureComponent { window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); } + handleTranslate = () => { + const { onTranslate, status } = this.props; + onTranslate(status); + } + render () { const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const outerStyle = { boxSizing: 'border-box' }; @@ -260,7 +266,12 @@ class DetailedStatus extends ImmutablePureComponent { - + {media} diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 4d7f24834..5ff7e060e 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -32,6 +32,8 @@ import { editStatus, hideStatus, revealStatus, + translateStatus, + undoStatusTranslation, } from '../../actions/statuses'; import { unblockAccount, @@ -339,6 +341,16 @@ class Status extends ImmutablePureComponent { } } + handleTranslate = status => { + const { dispatch } = this.props; + + if (status.get('translation')) { + dispatch(undoStatusTranslation(status.get('id'))); + } else { + dispatch(translateStatus(status.get('id'))); + } + } + handleBlockClick = (status) => { const { dispatch } = this.props; const account = status.get('account'); @@ -558,6 +570,7 @@ class Status extends ImmutablePureComponent { onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} onToggleHidden={this.handleToggleHidden} + onTranslate={this.handleTranslate} domain={domain} showMedia={this.state.showMedia} onToggleMediaVisibility={this.handleToggleMediaVisibility} diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index 53dec9585..7efb49d85 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -13,6 +13,8 @@ import { STATUS_REVEAL, STATUS_HIDE, STATUS_COLLAPSE, + STATUS_TRANSLATE_SUCCESS, + STATUS_TRANSLATE_UNDO, } from '../actions/statuses'; import { TIMELINE_DELETE } from '../actions/timelines'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; @@ -77,6 +79,10 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'collapsed'], action.isCollapsed); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.references); + case STATUS_TRANSLATE_SUCCESS: + return state.setIn([action.id, 'translation'], fromJS(action.translation)); + case STATUS_TRANSLATE_UNDO: + return state.deleteIn([action.id, 'translation']); default: return state; } diff --git a/app/lib/translation_service.rb b/app/lib/translation_service.rb new file mode 100644 index 000000000..526e26ae5 --- /dev/null +++ b/app/lib/translation_service.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class TranslationService + class Error < StandardError; end + class NotConfiguredError < Error; end + class TooManyRequestsError < Error; end + class QuotaExceededError < Error; end + class UnexpectedResponseError < Error; end + + def self.configured + if ENV['DEEPL_API_KEY'].present? + TranslationService::DeepL.new(ENV.fetch('DEEPL_PLAN', 'free'), ENV['DEEPL_API_KEY']) + elsif ENV['LIBRE_TRANSLATE_ENDPOINT'].present? + TranslationService::LibreTranslate.new(ENV['LIBRE_TRANSLATE_ENDPOINT'], ENV['LIBRE_TRANSLATE_API_KEY']) + else + raise NotConfiguredError + end + end + + def translate(_text, _source_language, _target_language) + raise NotImplementedError + end +end diff --git a/app/lib/translation_service/deepl.rb b/app/lib/translation_service/deepl.rb new file mode 100644 index 000000000..89ccf01e5 --- /dev/null +++ b/app/lib/translation_service/deepl.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +class TranslationService::DeepL < TranslationService + include JsonLdHelper + + def initialize(plan, api_key) + super() + + @plan = plan + @api_key = api_key + end + + def translate(text, source_language, target_language) + request(text, source_language, target_language).perform do |res| + case res.code + when 429 + raise TooManyRequestsError + when 456 + raise QuotaExceededError + when 200...300 + transform_response(res.body_with_limit) + else + raise UnexpectedResponseError + end + end + end + + private + + def request(text, source_language, target_language) + req = Request.new(:post, endpoint_url, form: { text: text, source_lang: source_language.upcase, target_lang: target_language, tag_handling: 'html' }) + req.add_headers('Authorization': "DeepL-Auth-Key #{@api_key}") + req + end + + def endpoint_url + if @plan == 'free' + 'https://api-free.deepl.com/v2/translate' + else + 'https://api.deepl.com/v2/translate' + end + end + + def transform_response(str) + json = Oj.load(str, mode: :strict) + + raise UnexpectedResponseError unless json.is_a?(Hash) + + Translation.new(text: json.dig('translations', 0, 'text'), detected_source_language: json.dig('translations', 0, 'detected_source_language')&.downcase) + rescue Oj::ParseError + raise UnexpectedResponseError + end +end diff --git a/app/lib/translation_service/libre_translate.rb b/app/lib/translation_service/libre_translate.rb new file mode 100644 index 000000000..66acdeed7 --- /dev/null +++ b/app/lib/translation_service/libre_translate.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class TranslationService::LibreTranslate < TranslationService + def initialize(base_url, api_key) + super() + + @base_url = base_url + @api_key = api_key + end + + def translate(text, source_language, target_language) + request(text, source_language, target_language).perform do |res| + case res.code + when 429 + raise TooManyRequestsError + when 403 + raise QuotaExceededError + when 200...300 + transform_response(res.body_with_limit, source_language) + else + raise UnexpectedResponseError + end + end + end + + private + + def request(text, source_language, target_language) + req = Request.new(:post, "#{@base_url}/translate", body: Oj.dump(q: text, source: source_language, target: target_language, format: 'html', api_key: @api_key)) + req.add_headers('Content-Type': 'application/json') + req + end + + def transform_response(str, source_language) + json = Oj.load(str, mode: :strict) + + raise UnexpectedResponseError unless json.is_a?(Hash) + + Translation.new(text: json['translatedText'], detected_source_language: source_language) + rescue Oj::ParseError + raise UnexpectedResponseError + end +end diff --git a/app/lib/translation_service/translation.rb b/app/lib/translation_service/translation.rb new file mode 100644 index 000000000..a55b82574 --- /dev/null +++ b/app/lib/translation_service/translation.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class TranslationService::Translation < ActiveModelSerializers::Model + attributes :text, :detected_source_language +end diff --git a/app/serializers/rest/translation_serializer.rb b/app/serializers/rest/translation_serializer.rb new file mode 100644 index 000000000..a06f23f32 --- /dev/null +++ b/app/serializers/rest/translation_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::TranslationSerializer < ActiveModel::Serializer + attributes :content, :detected_source_language + + def content + object.text + end +end diff --git a/app/services/translate_status_service.rb b/app/services/translate_status_service.rb new file mode 100644 index 000000000..b375226be --- /dev/null +++ b/app/services/translate_status_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class TranslateStatusService < BaseService + CACHE_TTL = 1.day.freeze + + def call(status, target_language) + raise Mastodon::NotPermittedError unless status.public_visibility? || status.unlisted_visibility? + + @status = status + @target_language = target_language + + Rails.cache.fetch("translations/#{@status.language}/#{@target_language}/#{content_hash}", expires_in: CACHE_TTL) { translation_backend.translate(@status.text, @status.language, @target_language) } + end + + private + + def translation_backend + TranslationService.configured + end + + def content_hash + Digest::SHA256.base64digest(@status.text) + end +end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 3e5a55617..a361cb0ec 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -25,6 +25,7 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'REST' inflect.acronym 'URL' inflect.acronym 'ASCII' + inflect.acronym 'DeepL' inflect.singular 'data', 'data' end diff --git a/config/routes.rb b/config/routes.rb index 13a4a1618..9491c5177 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -393,6 +393,8 @@ Rails.application.routes.draw do resource :history, only: :show resource :source, only: :show + + post :translate, to: 'translations#create' end member do -- cgit From bfc539cfb4f040fcffac740b36791c26c2a74119 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 2 Oct 2022 18:11:46 +0200 Subject: Revert "Change "Allow trends without prior review" setting to include statuses (#17977)" This reverts commit 546672e292dc3218e996048464c4c52e5d00f766. --- app/javascript/styles/mastodon/accounts.scss | 9 +-------- app/javascript/styles/mastodon/forms.scss | 3 +-- app/models/account.rb | 4 ---- app/views/admin/settings/edit.html.haml | 2 +- config/i18n-tasks.yml | 2 +- config/initializers/simple_form.rb | 5 +---- config/locales/en.yml | 4 ++-- config/locales/simple_form.en.yml | 1 - 8 files changed, 7 insertions(+), 23 deletions(-) (limited to 'config/initializers') diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index c007eb4b5..54b65bfc8 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -202,8 +202,7 @@ } .account-role, -.simple_form .recommended, -.simple_form .not_recommended { +.simple_form .recommended { display: inline-block; padding: 4px 6px; cursor: default; @@ -228,12 +227,6 @@ } } -.simple_form .not_recommended { - color: lighten($error-red, 12%); - background-color: rgba(lighten($error-red, 12%), 0.1); - border-color: rgba(lighten($error-red, 12%), 0.5); -} - .account__header__fields { max-width: 100vw; padding: 0; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index a6419821f..990903859 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -102,8 +102,7 @@ code { } } - .recommended, - .not_recommended { + .recommended { position: absolute; margin: 0 4px; margin-top: -2px; diff --git a/app/models/account.rb b/app/models/account.rb index 33870beda..f75750838 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -262,10 +262,6 @@ class Account < ApplicationRecord update!(memorial: true) end - def trendable - boolean_with_default('trendable', Setting.trendable_by_default) - end - def sign? true end diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index c8ebb3360..f2fdab90d 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -81,7 +81,7 @@ = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html') .fields-group - = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html'), recommended: :not_recommended + = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html') .fields-group = f.input :trending_status_cw, as: :boolean, wrapper: :with_label, label: t('admin.settings.trending_status_cw.title'), hint: t('admin.settings.trending_status_cw.desc_html') diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index cc607f72a..7139bcea7 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -51,7 +51,7 @@ ignore_unused: - 'activerecord.errors.*' - '{devise,pagination,doorkeeper}.*' - '{date,datetime,time,number}.*' - - 'simple_form.{yes,no,recommended,not_recommended}' + - 'simple_form.{yes,no,recommended}' - 'simple_form.{placeholders,hints,labels}.*' - 'simple_form.{error_notification,required}.:' - 'errors.messages.*' diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 92cffc5a2..3a2097d2f 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -11,10 +11,7 @@ end module RecommendedComponent def recommended(_wrapper_options = nil) return unless options[:recommended] - - key = options[:recommended].is_a?(Symbol) ? options[:recommended] : :recommended - options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t(key, scope: 'simple_form'), class: key)]) } - + options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t('simple_form.recommended'), class: 'recommended')]) } nil end end diff --git a/config/locales/en.yml b/config/locales/en.yml index dd341e0c8..6f0f3e953 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -807,8 +807,8 @@ en: title: Allow unauthenticated access to public timeline title: Site settings trendable_by_default: - desc_html: Specific trending content can still be explicitly disallowed - title: Allow trends without prior review + desc_html: Affects hashtags that have not been previously disallowed + title: Allow hashtags to trend without prior review trends: desc_html: Publicly display previously reviewed content that is currently trending title: Trends diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index db5b45e41..ec4c445e8 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -261,7 +261,6 @@ en: events: Enabled events url: Endpoint URL 'no': 'No' - not_recommended: Not recommended recommended: Recommended required: mark: "*" -- cgit From f7155becd058d3fde45d2ab548313e1a1f7575f9 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 19 Oct 2022 19:53:47 +0200 Subject: Fix config/locales-glitch not overriding translation strings (#1871) --- config/initializers/locale.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'config/initializers') diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb index fed182a71..b5664b0b0 100644 --- a/config/initializers/locale.rb +++ b/config/initializers/locale.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true -I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names.{rb,yml}').to_s] -I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names', '*.{rb,yml}').to_s] -I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names.{rb,yml}').to_s] -I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names', '*.{rb,yml}').to_s] -I18n.load_path += Dir[Rails.root.join('config', 'locales-glitch', '*.{rb,yml}').to_s] +Rails.application.configure do + I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names.{rb,yml}').to_s] + I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names', '*.{rb,yml}').to_s] + I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names.{rb,yml}').to_s] + I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names', '*.{rb,yml}').to_s] + I18n.load_path += Dir[Rails.root.join('config', 'locales-glitch', '*.{rb,yml}').to_s] +end -- cgit From 1e772c984b4681f6c036209b45dfa94b38b6ce83 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 19 Oct 2022 22:08:10 +0200 Subject: Actually fix config/locales-glitch not overriding translation strings (#1872) --- config/initializers/locale.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'config/initializers') diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb index b5664b0b0..4bcb1854c 100644 --- a/config/initializers/locale.rb +++ b/config/initializers/locale.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true Rails.application.configure do - I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names.{rb,yml}').to_s] - I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names', '*.{rb,yml}').to_s] - I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names.{rb,yml}').to_s] - I18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names', '*.{rb,yml}').to_s] - I18n.load_path += Dir[Rails.root.join('config', 'locales-glitch', '*.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join('app', 'javascript', 'flavours', '*', 'names', '*.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join('app', 'javascript', 'skins', '*', '*', 'names', '*.{rb,yml}').to_s] + config.i18n.load_path += Dir[Rails.root.join('config', 'locales-glitch', '*.{rb,yml}').to_s] end -- cgit From bf0ab3e0fac54515c13beef4ec09b0455f1bce67 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 26 Oct 2022 12:10:48 +0200 Subject: Fix vacuum scheduler missing lock, locks never expiring (#19458) Remove vacuuming of orphaned preview cards --- app/lib/vacuum/media_attachments_vacuum.rb | 2 +- app/lib/vacuum/preview_cards_vacuum.rb | 9 --------- app/workers/scheduler/vacuum_scheduler.rb | 2 +- config/initializers/sidekiq.rb | 1 + spec/lib/vacuum/preview_cards_vacuum_spec.rb | 4 ---- 5 files changed, 3 insertions(+), 15 deletions(-) (limited to 'config/initializers') diff --git a/app/lib/vacuum/media_attachments_vacuum.rb b/app/lib/vacuum/media_attachments_vacuum.rb index 7fb347ce4..7c0a85a9d 100644 --- a/app/lib/vacuum/media_attachments_vacuum.rb +++ b/app/lib/vacuum/media_attachments_vacuum.rb @@ -8,8 +8,8 @@ class Vacuum::MediaAttachmentsVacuum end def perform - vacuum_cached_files! if retention_period? vacuum_orphaned_records! + vacuum_cached_files! if retention_period? end private diff --git a/app/lib/vacuum/preview_cards_vacuum.rb b/app/lib/vacuum/preview_cards_vacuum.rb index 84ef100ed..14fdeda1c 100644 --- a/app/lib/vacuum/preview_cards_vacuum.rb +++ b/app/lib/vacuum/preview_cards_vacuum.rb @@ -9,7 +9,6 @@ class Vacuum::PreviewCardsVacuum def perform vacuum_cached_images! if retention_period? - vacuum_orphaned_records! end private @@ -21,18 +20,10 @@ class Vacuum::PreviewCardsVacuum end end - def vacuum_orphaned_records! - orphaned_preview_cards.in_batches.destroy_all - end - def preview_cards_past_retention_period PreviewCard.cached.where(PreviewCard.arel_table[:updated_at].lt(@retention_period.ago)) end - def orphaned_preview_cards - PreviewCard.where('NOT EXISTS (SELECT 1 FROM preview_cards_statuses WHERE preview_cards_statuses.preview_card_id = preview_cards.id)').where(PreviewCard.arel_table[:created_at].lt(TTL.ago)) - end - def retention_period? @retention_period.present? end diff --git a/app/workers/scheduler/vacuum_scheduler.rb b/app/workers/scheduler/vacuum_scheduler.rb index ce88ff204..9544f808b 100644 --- a/app/workers/scheduler/vacuum_scheduler.rb +++ b/app/workers/scheduler/vacuum_scheduler.rb @@ -3,7 +3,7 @@ class Scheduler::VacuumScheduler include Sidekiq::Worker - sidekiq_options retry: 0 + sidekiq_options retry: 0, lock: :until_executed def perform vacuum_operations.each do |operation| diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index c1327053d..9d2abf074 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -35,4 +35,5 @@ SidekiqUniqueJobs.configure do |config| config.reaper_count = 1000 config.reaper_interval = 600 config.reaper_timeout = 150 + config.lock_ttl = 50.days.to_i end diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb index 4a4a599fa..275f9ba92 100644 --- a/spec/lib/vacuum/preview_cards_vacuum_spec.rb +++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb @@ -28,9 +28,5 @@ RSpec.describe Vacuum::PreviewCardsVacuum do it 'does not delete attached preview cards' do expect(new_preview_card.reload).to be_persisted end - - it 'deletes preview cards not attached to any status' do - expect { orphaned_preview_card.reload }.to raise_error ActiveRecord::RecordNotFound - end end end -- cgit From aafbc82d88d54ad9c70c6fca0186fb48b423f338 Mon Sep 17 00:00:00 2001 From: prplecake Date: Wed, 26 Oct 2022 12:23:16 -0500 Subject: Add "unsafe-eval" to script-src CSP (#18817) --- config/initializers/content_security_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config/initializers') diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index c113b0f8b..be4ef50fc 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -36,7 +36,7 @@ Rails.application.config.content_security_policy do |p| p.worker_src :self, :blob, assets_host else p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url - p.script_src :self, assets_host + p.script_src :self, assets_host, :unsafe_eval p.child_src :self, :blob, assets_host p.worker_src :self, :blob, assets_host end -- cgit