module Admin
class SettingsController < BaseController
- ADMIN_SETTINGS = %w(
- site_contact_username
- site_contact_email
- site_title
- site_short_description
- site_description
- site_extended_description
- site_terms
- registrations_mode
- closed_registrations_message
- open_deletion
- timeline_preview
- show_staff_badge
- bootstrap_timeline_accounts
- theme
- thumbnail
- hero
- mascot
- min_invite_role
- activity_api_enabled
- peers_api_enabled
- show_known_fediverse_at_about_page
- preview_sensitive_media
- custom_css
- profile_directory
- ).freeze
-
- BOOLEAN_SETTINGS = %w(
- open_deletion
- timeline_preview
- show_staff_badge
- activity_api_enabled
- peers_api_enabled
- show_known_fediverse_at_about_page
- preview_sensitive_media
- profile_directory
- ).freeze
-
- UPLOAD_SETTINGS = %w(
- thumbnail
- hero
- mascot
- ).freeze
-
def edit
authorize :settings, :show?
+
@admin_settings = Form::AdminSettings.new
end
def update
authorize :settings, :update?
- settings_params.each do |key, value|
- if UPLOAD_SETTINGS.include?(key)
- upload = SiteUpload.where(var: key).first_or_initialize(var: key)
- upload.update(file: value)
- else
- setting = Setting.where(var: key).first_or_initialize(var: key)
- setting.update(value: value_for_update(key, value))
- end
- end
+ @admin_settings = Form::AdminSettings.new(settings_params)
- flash[:notice] = I18n.t('generic.changes_saved_msg')
- redirect_to edit_admin_settings_path
+ if @admin_settings.save
+ flash[:notice] = I18n.t('generic.changes_saved_msg')
+ redirect_to edit_admin_settings_path
+ else
+ render :edit
+ end
end
private
def settings_params
- params.require(:form_admin_settings).permit(ADMIN_SETTINGS)
- end
-
- def value_for_update(key, value)
- if BOOLEAN_SETTINGS.include?(key)
- value == '1'
- else
- value
- end
+ params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end
end
end
class Form::AdminSettings
include ActiveModel::Model
- delegate(
- :site_contact_username,
- :site_contact_username=,
- :site_contact_email,
- :site_contact_email=,
- :site_title,
- :site_title=,
- :site_short_description,
- :site_short_description=,
- :site_description,
- :site_description=,
- :site_extended_description,
- :site_extended_description=,
- :site_terms,
- :site_terms=,
- :registrations_mode,
- :registrations_mode=,
- :closed_registrations_message,
- :closed_registrations_message=,
- :open_deletion,
- :open_deletion=,
- :timeline_preview,
- :timeline_preview=,
- :show_staff_badge,
- :show_staff_badge=,
- :bootstrap_timeline_accounts,
- :bootstrap_timeline_accounts=,
- :theme,
- :theme=,
- :min_invite_role,
- :min_invite_role=,
- :activity_api_enabled,
- :activity_api_enabled=,
- :peers_api_enabled,
- :peers_api_enabled=,
- :show_known_fediverse_at_about_page,
- :show_known_fediverse_at_about_page=,
- :preview_sensitive_media,
- :preview_sensitive_media=,
- :custom_css,
- :custom_css=,
- :profile_directory,
- :profile_directory=,
- to: Setting
- )
+ KEYS = %i(
+ site_contact_username
+ site_contact_email
+ site_title
+ site_short_description
+ site_description
+ site_extended_description
+ site_terms
+ registrations_mode
+ closed_registrations_message
+ open_deletion
+ timeline_preview
+ show_staff_badge
+ bootstrap_timeline_accounts
+ theme
+ min_invite_role
+ activity_api_enabled
+ peers_api_enabled
+ show_known_fediverse_at_about_page
+ preview_sensitive_media
+ custom_css
+ profile_directory
+ ).freeze
+
+ BOOLEAN_KEYS = %i(
+ open_deletion
+ timeline_preview
+ show_staff_badge
+ activity_api_enabled
+ peers_api_enabled
+ show_known_fediverse_at_about_page
+ preview_sensitive_media
+ profile_directory
+ ).freeze
+
+ UPLOAD_KEYS = %i(
+ thumbnail
+ hero
+ mascot
+ ).freeze
+
+ attr_accessor(*KEYS)
+
+ validates :site_short_description, :site_description, :site_extended_description, :site_terms, :closed_registrations_message, html: true
+ validates :registrations_mode, inclusion: { in: %w(open approved none) }
+ validates :min_invite_role, inclusion: { in: %w(disabled user moderator admin) }
+ validates :site_contact_email, :site_contact_username, presence: true
+ validates :site_contact_username, existing_username: true
+ validates :bootstrap_timeline_accounts, existing_username: { multiple: true }
+
+ def initialize(_attributes = {})
+ super
+ initialize_attributes
+ end
+
+ def save
+ return false unless valid?
+
+ KEYS.each do |key|
+ value = instance_variable_get("@#{key}")
+
+ if UPLOAD_KEYS.include?(key)
+ upload = SiteUpload.where(var: key).first_or_initialize(var: key)
+ upload.update(file: value)
+ else
+ setting = Setting.where(var: key).first_or_initialize(var: key)
+ setting.update(value: typecast_value(key, value))
+ end
+ end
+ end
+
+ private
+
+ def initialize_attributes
+ KEYS.each do |key|
+ instance_variable_set("@#{key}", Setting.public_send(key)) if instance_variable_get("@#{key}").nil?
+ end
+ end
+
+ def typecast_value(key, value)
+ if BOOLEAN_KEYS.include?(key)
+ value == '1'
+ else
+ value
+ end
+ end
end
--- /dev/null
+# frozen_string_literal: true
+
+class ExistingUsernameValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ return if value.blank?
+
+ if options[:multiple]
+ missing_usernames = value.split(',').map { |username| username unless Account.find_local(username) }.compact
+ record.errors.add(attribute, I18n.t('existing_username_validator.not_found_multiple', usernames: missing_usernames.join(', '))) if missing_usernames.any?
+ else
+ record.errors.add(attribute, I18n.t('existing_username_validator.not_found')) unless Account.find_local(value)
+ end
+ end
+
+ private
+
+ def valid_html?(str)
+ Nokogiri::HTML.fragment(str).to_s == str
+ end
+end
--- /dev/null
+# frozen_string_literal: true
+
+class HtmlValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ return if value.blank?
+ record.errors.add(attribute, I18n.t('html_validator.invalid_markup')) unless valid_html?(value)
+ end
+
+ private
+
+ def valid_html?(str)
+ Nokogiri::HTML.fragment(str).to_s == str
+ end
+end
= t('admin.settings.title')
= simple_form_for @admin_settings, url: admin_settings_path, html: { method: :patch } do |f|
+ = render 'shared/error_messages', object: @admin_settings
.fields-group
= f.input :site_title, wrapper: :with_label, label: t('admin.settings.site_title')
content: We're sorry, but something went wrong on our end.
title: This page is not correct
noscript_html: To use the Mastodon web application, please enable JavaScript. Alternatively, try one of the <a href="%{apps_path}">native apps</a> for Mastodon for your platform.
+ existing_username_validator:
+ not_found: could not find a local user with that username
+ not_found_multiple: could not find %{usernames}
exports:
archive_takeout:
date: Date
validation_errors:
one: Something isn't quite right yet! Please review the error below
other: Something isn't quite right yet! Please review %{count} errors below
+ html_validator:
+ invalid_markup: contains invalid HTML markup
identity_proofs:
active: Active
authorize: Yes, authorize
primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_dashboard_url, if: proc { current_user.staff? } do |admin|
admin.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url
- admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }
+ admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/settings}
admin.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
admin.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/relays}
admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url, if: -> { current_user.admin? }
end
describe 'PUT #update' do
+ before do
+ allow_any_instance_of(Form::AdminSettings).to receive(:valid?).and_return(true)
+ end
+
describe 'for a record that doesnt exist' do
around do |example|
before = Setting.site_extended_description