mirror of
https://github.com/mastodon/mastodon.git
synced 2025-05-11 20:21:10 +00:00
Merge 7f0b5478e6
into dee744c793
This commit is contained in:
commit
b58ce8c360
|
@ -7,7 +7,7 @@ module Admin
|
|||
def index
|
||||
authorize :rule, :index?
|
||||
|
||||
@rules = Rule.ordered
|
||||
@rules = Rule.ordered.includes(:translations)
|
||||
@rule = Rule.new
|
||||
end
|
||||
|
||||
|
@ -23,7 +23,7 @@ module Admin
|
|||
if @rule.save
|
||||
redirect_to admin_rules_path
|
||||
else
|
||||
@rules = Rule.ordered
|
||||
@rules = Rule.ordered.includes(:translations)
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ module Admin
|
|||
|
||||
def resource_params
|
||||
params
|
||||
.expect(rule: [:text, :hint, :priority])
|
||||
.expect(rule: [:text, :hint, :priority, translations_attributes: [[:id, :language, :text, :hint, :_destroy]]])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,6 @@ class Api::V1::Instances::RulesController < Api::V1::Instances::BaseController
|
|||
private
|
||||
|
||||
def set_rules
|
||||
@rules = Rule.ordered
|
||||
@rules = Rule.ordered.includes(:translations)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -126,7 +126,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def set_rules
|
||||
@rules = Rule.ordered
|
||||
@rules = Rule.ordered.includes(:translations)
|
||||
end
|
||||
|
||||
def require_rules_acceptance!
|
||||
|
|
|
@ -44,6 +44,7 @@ const severityMessages = {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
server: state.getIn(['server', 'server']),
|
||||
locale: state.getIn(['meta', 'locale']),
|
||||
extendedDescription: state.getIn(['server', 'extendedDescription']),
|
||||
domainBlocks: state.getIn(['server', 'domainBlocks']),
|
||||
});
|
||||
|
@ -91,6 +92,7 @@ class About extends PureComponent {
|
|||
|
||||
static propTypes = {
|
||||
server: ImmutablePropTypes.map,
|
||||
locale: ImmutablePropTypes.string,
|
||||
extendedDescription: ImmutablePropTypes.map,
|
||||
domainBlocks: ImmutablePropTypes.contains({
|
||||
isLoading: PropTypes.bool,
|
||||
|
@ -114,7 +116,7 @@ class About extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn, intl, server, extendedDescription, domainBlocks } = this.props;
|
||||
const { multiColumn, intl, server, extendedDescription, domainBlocks, locale } = this.props;
|
||||
const isLoading = server.get('isLoading');
|
||||
|
||||
return (
|
||||
|
@ -168,12 +170,15 @@ class About extends PureComponent {
|
|||
<p><FormattedMessage id='about.not_available' defaultMessage='This information has not been made available on this server.' /></p>
|
||||
) : (
|
||||
<ol className='rules-list'>
|
||||
{server.get('rules').map(rule => (
|
||||
<li key={rule.get('id')}>
|
||||
<div className='rules-list__text'>{rule.get('text')}</div>
|
||||
{rule.get('hint').length > 0 && (<div className='rules-list__hint'>{rule.get('hint')}</div>)}
|
||||
</li>
|
||||
))}
|
||||
{server.get('rules').map(rule => {
|
||||
const text = rule.getIn(['translations', locale, 'text']) || rule.get('text');
|
||||
const hint = rule.getIn(['translations', locale, 'hint']) || rule.get('hint');
|
||||
return (
|
||||
<li key={rule.get('id')}>
|
||||
<div className='rules-list__text'>{text}</div>
|
||||
{hint.length > 0 && (<div className='rules-list__hint'>{hint}</div>)}
|
||||
</li>
|
||||
)})}
|
||||
</ol>
|
||||
))}
|
||||
</Section>
|
||||
|
|
|
@ -12,6 +12,7 @@ import Option from './components/option';
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
rules: state.getIn(['server', 'server', 'rules']),
|
||||
locale: state.getIn(['meta', 'locale']),
|
||||
});
|
||||
|
||||
class Rules extends PureComponent {
|
||||
|
@ -19,6 +20,7 @@ class Rules extends PureComponent {
|
|||
static propTypes = {
|
||||
onNextStep: PropTypes.func.isRequired,
|
||||
rules: ImmutablePropTypes.list,
|
||||
locale: PropTypes.string,
|
||||
selectedRuleIds: ImmutablePropTypes.set.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -34,7 +36,7 @@ class Rules extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { rules, selectedRuleIds } = this.props;
|
||||
const { rules, locale, selectedRuleIds } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -49,7 +51,7 @@ class Rules extends PureComponent {
|
|||
value={item.get('id')}
|
||||
checked={selectedRuleIds.includes(item.get('id'))}
|
||||
onToggle={this.handleRulesToggle}
|
||||
label={item.get('text')}
|
||||
label={item.getIn(['translations', locale, 'text']) || item.get('text')}
|
||||
multiple
|
||||
/>
|
||||
))}
|
||||
|
|
|
@ -19,6 +19,9 @@ class Rule < ApplicationRecord
|
|||
|
||||
self.discard_column = :deleted_at
|
||||
|
||||
has_many :translations, inverse_of: :rule, class_name: 'RuleTranslation', dependent: :destroy
|
||||
accepts_nested_attributes_for :translations, reject_if: :all_blank, allow_destroy: true
|
||||
|
||||
validates :text, presence: true, length: { maximum: TEXT_SIZE_LIMIT }
|
||||
|
||||
scope :ordered, -> { kept.order(priority: :asc, id: :asc) }
|
||||
|
|
20
app/models/rule_translation.rb
Normal file
20
app/models/rule_translation.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: rule_translations
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# hint :text default(""), not null
|
||||
# language :string not null
|
||||
# text :text default(""), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# rule_id :bigint(8) not null
|
||||
#
|
||||
class RuleTranslation < ApplicationRecord
|
||||
belongs_to :rule
|
||||
|
||||
validates :language, presence: true, uniqueness: { scope: :rule_id }
|
||||
validates :text, presence: true, length: { maximum: Rule::TEXT_SIZE_LIMIT }
|
||||
end
|
|
@ -47,7 +47,7 @@ class InstancePresenter < ActiveModelSerializers::Model
|
|||
end
|
||||
|
||||
def rules
|
||||
Rule.ordered
|
||||
Rule.ordered.includes(:translations)
|
||||
end
|
||||
|
||||
def user_count
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::RuleSerializer < ActiveModel::Serializer
|
||||
attributes :id, :text, :hint
|
||||
attributes :id, :text, :hint, :translations
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
|
||||
def translations
|
||||
object.translations.to_h do |translation|
|
||||
[translation.language, { text: translation.text, hint: translation.hint }]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
27
app/views/admin/rules/_translation_fields.html.haml
Normal file
27
app/views/admin/rules/_translation_fields.html.haml
Normal file
|
@ -0,0 +1,27 @@
|
|||
%tr.nested-fields
|
||||
%td
|
||||
.fields-row
|
||||
.fields-row__column.fields-group
|
||||
= f.input :language,
|
||||
collection: ui_languages,
|
||||
include_blank: false,
|
||||
label_method: ->(locale) { native_locale_name(locale) }
|
||||
|
||||
.fields-row__column.fields-group
|
||||
= f.hidden_field :id if f.object&.persisted? # Required so Rails doesn't put the field outside of the <tr/>
|
||||
= link_to_remove_association(f, class: 'table-action-link') do
|
||||
= safe_join([material_symbol('close'), t('filters.index.delete')])
|
||||
|
||||
.fields-group
|
||||
= f.input :text,
|
||||
label: I18n.t('simple_form.labels.rule.text'),
|
||||
hint: I18n.t('simple_form.hints.rule.text'),
|
||||
input_html: { lang: f.object&.language },
|
||||
wrapper: :with_block_label
|
||||
|
||||
.fields-group
|
||||
= f.input :hint,
|
||||
label: I18n.t('simple_form.labels.rule.hint'),
|
||||
hint: I18n.t('simple_form.hints.rule.hint'),
|
||||
input_html: { lang: f.object&.language },
|
||||
wrapper: :with_block_label
|
|
@ -6,5 +6,26 @@
|
|||
|
||||
= render form
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
%h4= t('admin.rules.translations')
|
||||
|
||||
%p.hint= t('admin.rules.translations_explanation')
|
||||
|
||||
.table-wrapper
|
||||
%table.table.keywords-table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('admin.rules.translation')
|
||||
%th
|
||||
%tbody
|
||||
= form.simple_fields_for :translations do |translation|
|
||||
= render 'translation_fields', f: translation
|
||||
%tfoot
|
||||
%tr
|
||||
%td{ colspan: 3 }
|
||||
= link_to_add_association form, :translations, class: 'table-action-link', partial: 'translation_fields', 'data-association-insertion-node': '.keywords-table tbody', 'data-association-insertion-method': 'append' do
|
||||
= safe_join([material_symbol('add'), t('admin.rules.add_translation')])
|
||||
|
||||
.actions
|
||||
= form.button :button, t('generic.save_changes'), type: :submit
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
|
||||
%ol.rules-list
|
||||
- @rules.each do |rule|
|
||||
- translation = rule.translations.find { |translation| translation.language == I18n.locale.to_s }
|
||||
%li
|
||||
%button{ type: 'button', aria: { expanded: 'false' } }
|
||||
.rules-list__text= rule.text
|
||||
.rules-list__hint= rule.hint
|
||||
.rules-list__text= translation&.text || rule.text
|
||||
.rules-list__hint= translation&.hint || rule.hint
|
||||
|
||||
.stacked-actions
|
||||
- accept_path = @invite_code.present? ? public_invite_url(invite_code: @invite_code, accept: @accept_token) : new_user_registration_path(accept: @accept_token)
|
||||
|
|
|
@ -786,11 +786,15 @@ en:
|
|||
title: Roles
|
||||
rules:
|
||||
add_new: Add rule
|
||||
add_translation: Add translation
|
||||
delete: Delete
|
||||
description_html: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. <strong>Make it easier to see your server's rules at a glance by providing them in a flat bullet point list.</strong> Try to keep individual rules short and simple, but try not to split them up into many separate items either.
|
||||
edit: Edit rule
|
||||
empty: No server rules have been defined yet.
|
||||
title: Server rules
|
||||
translation: Translation
|
||||
translations: Translations
|
||||
translations_explanation: You can optionally add translations for the rules. The default value will be shown if no translated version is available. Please always ensure any provided translation is in sync with the default value.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Manage server rules
|
||||
|
|
16
db/migrate/20250417154643_create_rule_translations.rb
Normal file
16
db/migrate/20250417154643_create_rule_translations.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateRuleTranslations < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :rule_translations do |t|
|
||||
t.text :text, null: false, default: ''
|
||||
t.text :hint, null: false, default: ''
|
||||
t.string :language, null: false
|
||||
t.references :rule, null: false, foreign_key: { on_delete: :cascade }, index: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :rule_translations, [:rule_id, :language], unique: true
|
||||
end
|
||||
end
|
13
db/schema.rb
13
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_04_17_154643) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
|
@ -937,6 +937,16 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do
|
|||
t.index ["target_account_id"], name: "index_reports_on_target_account_id"
|
||||
end
|
||||
|
||||
create_table "rule_translations", force: :cascade do |t|
|
||||
t.text "text", default: "", null: false
|
||||
t.text "hint", default: "", null: false
|
||||
t.string "language", null: false
|
||||
t.bigint "rule_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["rule_id", "language"], name: "index_rule_translations_on_rule_id_and_language", unique: true
|
||||
end
|
||||
|
||||
create_table "rules", force: :cascade do |t|
|
||||
t.integer "priority", default: 0, null: false
|
||||
t.datetime "deleted_at", precision: nil
|
||||
|
@ -1380,6 +1390,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do
|
|||
add_foreign_key "reports", "accounts", column: "target_account_id", name: "fk_eb37af34f0", on_delete: :cascade
|
||||
add_foreign_key "reports", "accounts", name: "fk_4b81f7522c", on_delete: :cascade
|
||||
add_foreign_key "reports", "oauth_applications", column: "application_id", on_delete: :nullify
|
||||
add_foreign_key "rule_translations", "rules", on_delete: :cascade
|
||||
add_foreign_key "scheduled_statuses", "accounts", on_delete: :cascade
|
||||
add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", name: "fk_957e5bda89", on_delete: :cascade
|
||||
add_foreign_key "session_activations", "users", name: "fk_e5fda67334", on_delete: :cascade
|
||||
|
|
8
spec/fabricators/rule_translation_fabricator.rb
Normal file
8
spec/fabricators/rule_translation_fabricator.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:rule_translation) do
|
||||
text 'MyText'
|
||||
hint 'MyText'
|
||||
language 'en'
|
||||
rule { Fabricate.build(:rule) }
|
||||
end
|
|
@ -11,7 +11,8 @@ RSpec.describe REST::RuleSerializer do
|
|||
it 'returns expected values' do
|
||||
expect(subject)
|
||||
.to include(
|
||||
'id' => be_a(String).and(eq('123'))
|
||||
'id' => be_a(String).and(eq('123')),
|
||||
'translations' => be_a(Hash)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user