about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock16
-rw-r--r--app/controllers/admin/settings_controller.rb81
-rw-r--r--app/javascript/styles/mastodon/forms.scss36
-rw-r--r--app/models/form/admin_settings.rb149
-rw-r--r--app/models/user.rb12
-rw-r--r--app/presenters/instance_presenter.rb1
-rw-r--r--app/services/vote_service.rb4
-rw-r--r--app/validators/existing_username_validator.rb20
-rw-r--r--app/validators/html_validator.rb14
-rw-r--r--app/views/about/_registration.html.haml29
-rw-r--r--app/views/admin/settings/edit.html.haml1
-rw-r--r--config/locales/en.yml9
-rw-r--r--config/navigation.rb4
-rw-r--r--spec/controllers/admin/settings_controller_spec.rb4
15 files changed, 231 insertions, 151 deletions
diff --git a/Gemfile b/Gemfile
index 088e25eea..1a2c2f78f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -15,7 +15,7 @@ gem 'makara', '~> 0.4'
 gem 'pghero', '~> 2.2'
 gem 'dotenv-rails', '~> 2.7'
 
-gem 'aws-sdk-s3', '~> 1.33', require: false
+gem 'aws-sdk-s3', '~> 1.34', require: false
 gem 'fog-core', '<= 2.1.0'
 gem 'fog-openstack', '~> 0.3', require: false
 gem 'paperclip', '~> 6.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 572a454ea..fc9e9a6dc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,17 +76,17 @@ GEM
     av (0.9.0)
       cocaine (~> 0.5.3)
     aws-eventstream (1.0.2)
-    aws-partitions (1.144.0)
-    aws-sdk-core (3.48.0)
+    aws-partitions (1.145.0)
+    aws-sdk-core (3.48.2)
       aws-eventstream (~> 1.0, >= 1.0.2)
       aws-partitions (~> 1.0)
       aws-sigv4 (~> 1.1)
       jmespath (~> 1.0)
-    aws-sdk-kms (1.15.0)
-      aws-sdk-core (~> 3, >= 3.48.0)
+    aws-sdk-kms (1.16.0)
+      aws-sdk-core (~> 3, >= 3.48.2)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.33.0)
-      aws-sdk-core (~> 3, >= 3.48.0)
+    aws-sdk-s3 (1.34.0)
+      aws-sdk-core (~> 3, >= 3.48.2)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.0)
     aws-sigv4 (1.1.0)
@@ -336,7 +336,7 @@ GEM
       mimemagic (~> 0.3.2)
     mario-redis-lock (1.2.1)
       redis (>= 3.0.5)
-    memory_profiler (0.9.12)
+    memory_profiler (0.9.13)
     method_source (0.9.2)
     microformats (4.1.0)
       json (~> 2.1)
@@ -660,7 +660,7 @@ DEPENDENCIES
   active_record_query_trace (~> 1.6)
   addressable (~> 2.6)
   annotate (~> 2.7)
-  aws-sdk-s3 (~> 1.33)
+  aws-sdk-s3 (~> 1.34)
   better_errors (~> 2.5)
   binding_of_caller (~> 0.7)
   bootsnap (~> 1.4)
diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb
index a64e98868..dc1c79b7f 100644
--- a/app/controllers/admin/settings_controller.rb
+++ b/app/controllers/admin/settings_controller.rb
@@ -2,94 +2,29 @@
 
 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
-      flavour
-      skin
-      flavour_and_skin
-      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
-      hide_followers_count
-    ).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
-      hide_followers_count
-    ).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 = settings_params
-      flavours_and_skin = settings.delete('flavour_and_skin')
-      if flavours_and_skin
-        settings['flavour'], settings['skin'] = flavours_and_skin.split('/', 2)
-      end
+      @admin_settings = Form::AdminSettings.new(settings_params)
 
-      settings.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
+      if @admin_settings.save
+        flash[:notice] = I18n.t('generic.changes_saved_msg')
+        redirect_to edit_admin_settings_path
+      else
+        render :edit
       end
-
-      flash[:notice] = I18n.t('generic.changes_saved_msg')
-      redirect_to edit_admin_settings_path
     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
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 9ef45e425..3ea104786 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -475,6 +475,42 @@ code {
       }
     }
   }
+
+  &__overlay-area {
+    position: relative;
+
+    &__overlay {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background: rgba($ui-base-color, 0.65);
+      backdrop-filter: blur(2px);
+      border-radius: 4px;
+
+      &__content {
+        text-align: center;
+
+        &.rich-formatting {
+          &,
+          p {
+            color: $primary-text-color;
+          }
+        }
+      }
+    }
+  }
+}
+
+.block-icon {
+  display: block;
+  margin: 0 auto;
+  margin-bottom: 10px;
+  font-size: 24px;
 }
 
 .flash-message {
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index 929c65793..0fcbd0605 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -3,57 +3,108 @@
 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=,
-    :hide_followers_count,
-    :hide_followers_count=,
-    :flavour,
-    :flavour=,
-    :skin,
-    :skin=,
-    :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
+    flavour
+    skin
+    min_invite_role
+    activity_api_enabled
+    peers_api_enabled
+    show_known_fediverse_at_about_page
+    preview_sensitive_media
+    custom_css
+    profile_directory
+    hide_followers_count
+    flavour_and_skin
+  ).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
+    hide_followers_count
+  ).freeze
+
+  UPLOAD_KEYS = %i(
+    thumbnail
+    hero
+    mascot
+  ).freeze
+
+  PSEUDO_KEYS = %i(
+    flavour_and_skin
+  ).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|
+      next if PSEUDO_KEYS.include?(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
 
   def flavour_and_skin
     "#{Setting.flavour}/#{Setting.skin}"
   end
+
+  def flavour_and_skin=(value)
+    @flavour, @skin = value.split('/', 2)
+  end
+
+  private
+
+  def initialize_attributes
+    KEYS.each do |key|
+      next if PSEUDO_KEYS.include?(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
diff --git a/app/models/user.rb b/app/models/user.rb
index 47657a670..66c1543ff 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -124,7 +124,8 @@ class User < ApplicationRecord
   end
 
   def confirm
-    new_user = !confirmed?
+    new_user      = !confirmed?
+    self.approved = true if open_registrations?
 
     super
 
@@ -136,7 +137,8 @@ class User < ApplicationRecord
   end
 
   def confirm!
-    new_user = !confirmed?
+    new_user      = !confirmed?
+    self.approved = true if open_registrations?
 
     skip_confirmation!
     save!
@@ -264,7 +266,11 @@ class User < ApplicationRecord
   private
 
   def set_approved
-    self.approved = Setting.registrations_mode == 'open' || invited?
+    self.approved = open_registrations? || invited?
+  end
+
+  def open_registrations?
+    Setting.registrations_mode == 'open'
   end
 
   def sanitize_languages
diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb
index 94a2c1692..d234516e0 100644
--- a/app/presenters/instance_presenter.rb
+++ b/app/presenters/instance_presenter.rb
@@ -8,6 +8,7 @@ class InstancePresenter
     :site_description,
     :site_extended_description,
     :site_terms,
+    :closed_registrations_message,
     to: Setting
   )
 
diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb
index 0cace6c00..81af9ef3a 100644
--- a/app/services/vote_service.rb
+++ b/app/services/vote_service.rb
@@ -11,14 +11,14 @@ class VoteService < BaseService
     @choices = choices
     @votes   = []
 
-    return if @poll.expired?
-
     ApplicationRecord.transaction do
       @choices.each do |choice|
         @votes << @poll.votes.create!(account: @account, choice: choice)
       end
     end
 
+    ActivityTracker.increment('activity:interactions')
+
     if @poll.account.local?
       distribute_poll!
     else
diff --git a/app/validators/existing_username_validator.rb b/app/validators/existing_username_validator.rb
new file mode 100644
index 000000000..4388a0c98
--- /dev/null
+++ b/app/validators/existing_username_validator.rb
@@ -0,0 +1,20 @@
+# 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
diff --git a/app/validators/html_validator.rb b/app/validators/html_validator.rb
new file mode 100644
index 000000000..882c35d41
--- /dev/null
+++ b/app/validators/html_validator.rb
@@ -0,0 +1,14 @@
+# 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
diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml
index 9cb4eb2bc..4823ff375 100644
--- a/app/views/about/_registration.html.haml
+++ b/app/views/about/_registration.html.haml
@@ -1,16 +1,23 @@
 = simple_form_for(new_user, url: user_registration_path) do |f|
-  %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname))
+  .simple_form__overlay-area
+    %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname))
 
-  .fields-group
-    = f.simple_fields_for :account do |account_fields|
-      = account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations?
+    .fields-group
+      = f.simple_fields_for :account do |account_fields|
+        = account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations?
 
-    = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
-    = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
-    = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
+      = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
+      = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
+      = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
 
-  .fields-group
-    = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations?
+    .fields-group
+      = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations?
 
-  .actions
-    = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations?
+    .actions
+      = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations?
+
+    - if closed_registrations? && @instance_presenter.closed_registrations_message.present?
+      .simple_form__overlay-area__overlay
+        .simple_form__overlay-area__overlay__content.rich-formatting
+          .block-icon= fa_icon 'warning'
+          = @instance_presenter.closed_registrations_message.html_safe
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index 9995e0b2a..475fb3a2f 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -2,6 +2,7 @@
   = 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')
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7e1d92884..f88706434 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -431,13 +431,13 @@ en:
         desc_html: Show a staff badge on a user page
         title: Show staff badge
       site_description:
-        desc_html: Introductory paragraph on the frontpage. Describe what makes this Mastodon server special and anything else important. You can use HTML tags, in particular <code>&lt;a&gt;</code> and <code>&lt;em&gt;</code>.
+        desc_html: Introductory paragraph on the API. Describe what makes this Mastodon server special and anything else important. You can use HTML tags, in particular <code>&lt;a&gt;</code> and <code>&lt;em&gt;</code>.
         title: Server description
       site_description_extended:
         desc_html: A good place for your code of conduct, rules, guidelines and other things that set your server apart. You can use HTML tags
         title: Custom extended information
       site_short_description:
-        desc_html: Displayed in sidebar and meta tags. Describe what Mastodon is and what makes this server special in a single paragraph. If empty, defaults to server description.
+        desc_html: Displayed in sidebar and meta tags. Describe what Mastodon is and what makes this server special in a single paragraph.
         title: Short server description
       site_terms:
         desc_html: You can write your own privacy policy, terms of service or other legalese. You can use HTML tags
@@ -589,6 +589,9 @@ en:
       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
@@ -637,6 +640,8 @@ en:
     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
diff --git a/config/navigation.rb b/config/navigation.rb
index 86c2572d7..34b566188 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -14,7 +14,7 @@ SimpleNavigation::Configuration.run do |navigation|
       settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url
       settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url
       settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
-      settings.item :identity_proofs, safe_join([fa_icon('key fw'), t('settings.identity_proofs')]), settings_identity_proofs_path, highlights_on: %r{/settings/identity_proofs*}
+      settings.item :identity_proofs, safe_join([fa_icon('key fw'), t('settings.identity_proofs')]), settings_identity_proofs_path, highlights_on: %r{/settings/identity_proofs*}, if: proc { current_account.identity_proofs.exists? }
     end
 
     primary.item :flavours, safe_join([fa_icon('paint-brush fw'), t('settings.flavours')]), settings_flavours_url do |flavours|
@@ -43,7 +43,7 @@ SimpleNavigation::Configuration.run do |navigation|
 
     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? }
diff --git a/spec/controllers/admin/settings_controller_spec.rb b/spec/controllers/admin/settings_controller_spec.rb
index 34f6bbdae..6cf0ee20a 100644
--- a/spec/controllers/admin/settings_controller_spec.rb
+++ b/spec/controllers/admin/settings_controller_spec.rb
@@ -19,6 +19,10 @@ RSpec.describe Admin::SettingsController, type: :controller do
     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