Migrate from ledermann/rails-settings to rails-settings-cached which allows global settings
with YAML-defined defaults. Add admin page for editing global settings. Add "site_description" setting that would show as a paragraph on the frontpage
This commit is contained in:
		
							
								
								
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -17,6 +17,7 @@ gem 'pg' | ||||
| gem 'pghero' | ||||
| gem 'dotenv-rails' | ||||
| gem 'font-awesome-rails' | ||||
| gem 'best_in_place', '~> 3.0.1' | ||||
|  | ||||
| gem 'paperclip', '~> 5.0' | ||||
| gem 'paperclip-av-transcoder' | ||||
| @@ -43,7 +44,7 @@ gem 'will_paginate' | ||||
| gem 'rack-attack' | ||||
| gem 'rack-cors', require: 'rack/cors' | ||||
| gem 'sidekiq' | ||||
| gem 'ledermann-rails-settings' | ||||
| gem 'rails-settings-cached' | ||||
| gem 'pg_search' | ||||
| gem 'simple-navigation' | ||||
|  | ||||
|   | ||||
							
								
								
									
										10
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @@ -60,6 +60,9 @@ GEM | ||||
|       babel-source (>= 4.0, < 6) | ||||
|       execjs (~> 2.0) | ||||
|     bcrypt (3.1.11) | ||||
|     best_in_place (3.0.3) | ||||
|       actionpack (>= 3.2) | ||||
|       railties (>= 3.2) | ||||
|     better_errors (2.1.1) | ||||
|       coderay (>= 1.0.0) | ||||
|       erubis (>= 2.6.6) | ||||
| @@ -172,8 +175,6 @@ GEM | ||||
|     json (1.8.3) | ||||
|     launchy (2.4.3) | ||||
|       addressable (~> 2.3) | ||||
|     ledermann-rails-settings (2.4.2) | ||||
|       activerecord (>= 3.1) | ||||
|     letter_opener (1.4.1) | ||||
|       launchy (~> 2.2) | ||||
|     link_header (0.0.8) | ||||
| @@ -259,6 +260,8 @@ GEM | ||||
|       nokogiri (~> 1.6.0) | ||||
|     rails-html-sanitizer (1.0.3) | ||||
|       loofah (~> 2.0) | ||||
|     rails-settings-cached (0.6.5) | ||||
|       rails (>= 4.2.0) | ||||
|     rails_12factor (0.0.3) | ||||
|       rails_serve_static_assets | ||||
|       rails_stdout_logging | ||||
| @@ -405,6 +408,7 @@ DEPENDENCIES | ||||
|   addressable | ||||
|   autoprefixer-rails | ||||
|   aws-sdk (>= 2.0) | ||||
|   best_in_place (~> 3.0.1) | ||||
|   better_errors | ||||
|   binding_of_caller | ||||
|   browserify-rails | ||||
| @@ -426,7 +430,6 @@ DEPENDENCIES | ||||
|   i18n-tasks (~> 0.9.6) | ||||
|   jbuilder (~> 2.0) | ||||
|   jquery-rails | ||||
|   ledermann-rails-settings | ||||
|   letter_opener | ||||
|   link_header | ||||
|   lograge | ||||
| @@ -445,6 +448,7 @@ DEPENDENCIES | ||||
|   rack-cors | ||||
|   rack-timeout-puma | ||||
|   rails (~> 5.0.1.0) | ||||
|   rails-settings-cached | ||||
|   rails_12factor | ||||
|   rails_autolink | ||||
|   react-rails | ||||
|   | ||||
| @@ -1,3 +1,8 @@ | ||||
| //= require jquery | ||||
| //= require jquery_ujs | ||||
| //= require extras | ||||
| //= require best_in_place | ||||
|  | ||||
| $(function () { | ||||
|   $(".best_in_place").best_in_place(); | ||||
| }); | ||||
|   | ||||
| @@ -36,6 +36,10 @@ | ||||
|       text-decoration: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   strong { | ||||
|     font-weight: 500; | ||||
|   } | ||||
| } | ||||
|  | ||||
| samp { | ||||
|   | ||||
| @@ -4,10 +4,10 @@ class AboutController < ApplicationController | ||||
|   before_action :set_body_classes | ||||
|  | ||||
|   def index | ||||
|     @description = Setting.site_description | ||||
|   end | ||||
|  | ||||
|   def terms | ||||
|   end | ||||
|   def terms; end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   | ||||
							
								
								
									
										25
									
								
								app/controllers/admin/settings_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/controllers/admin/settings_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class Admin::SettingsController < ApplicationController | ||||
|   before_action :require_admin! | ||||
|  | ||||
|   layout 'admin' | ||||
|  | ||||
|   def index | ||||
|     @settings = Setting.all_as_records | ||||
|   end | ||||
|  | ||||
|   def update | ||||
|     @setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id]) | ||||
|  | ||||
|     if @setting.value != params[:setting][:value] | ||||
|       @setting.value = params[:setting][:value] | ||||
|       @setting.save | ||||
|     end | ||||
|  | ||||
|     respond_to do |format| | ||||
|       format.html { redirect_to admin_settings_path } | ||||
|       format.json { respond_with_bip(@setting) } | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -8,14 +8,18 @@ class Settings::PreferencesController < ApplicationController | ||||
|   def show; end | ||||
|  | ||||
|   def update | ||||
|     current_user.settings(:notification_emails).follow         = user_params[:notification_emails][:follow]         == '1' | ||||
|     current_user.settings(:notification_emails).follow_request = user_params[:notification_emails][:follow_request] == '1' | ||||
|     current_user.settings(:notification_emails).reblog         = user_params[:notification_emails][:reblog]         == '1' | ||||
|     current_user.settings(:notification_emails).favourite      = user_params[:notification_emails][:favourite]      == '1' | ||||
|     current_user.settings(:notification_emails).mention        = user_params[:notification_emails][:mention]        == '1' | ||||
|     current_user.settings['notification_emails'] = { | ||||
|       follow:         user_params[:notification_emails][:follow]         == '1', | ||||
|       follow_request: user_params[:notification_emails][:follow_request] == '1', | ||||
|       reblog:         user_params[:notification_emails][:reblog]         == '1', | ||||
|       favourite:      user_params[:notification_emails][:favourite]      == '1', | ||||
|       mention:        user_params[:notification_emails][:mention]        == '1', | ||||
|     } | ||||
|  | ||||
|     current_user.settings(:interactions).must_be_follower  = user_params[:interactions][:must_be_follower]  == '1' | ||||
|     current_user.settings(:interactions).must_be_following = user_params[:interactions][:must_be_following] == '1' | ||||
|     current_user.settings['interactions'] = { | ||||
|       must_be_follower:  user_params[:interactions][:must_be_follower]  == '1', | ||||
|       must_be_following: user_params[:interactions][:must_be_following] == '1', | ||||
|     } | ||||
|  | ||||
|     if current_user.update(user_params.except(:notification_emails, :interactions)) | ||||
|       redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg') | ||||
|   | ||||
| @@ -14,4 +14,8 @@ module SettingsHelper | ||||
|   def human_locale(locale) | ||||
|     HUMAN_LOCALES[locale] | ||||
|   end | ||||
|  | ||||
|   def hash_to_object(hash) | ||||
|     HashObject.new(hash) | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										10
									
								
								app/lib/hash_object.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/lib/hash_object.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class HashObject | ||||
|   def initialize(hash) | ||||
|     hash.each do |k, v| | ||||
|       instance_variable_set("@#{k}", v) | ||||
|       self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") }) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										31
									
								
								app/models/setting.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/models/setting.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class Setting < RailsSettings::Base | ||||
|   source Rails.root.join('config/settings.yml') | ||||
|   namespace Rails.env | ||||
|  | ||||
|   def to_param | ||||
|     var | ||||
|   end | ||||
|  | ||||
|   class << self | ||||
|     def all_as_records | ||||
|       vars    = thing_scoped | ||||
|       records = vars.map { |r| [r.var, r] }.to_h | ||||
|  | ||||
|       default_settings.each do |key, default_value| | ||||
|         next if records.key?(key) || default_value.is_a?(Hash) | ||||
|         records[key] = Setting.new(var: key, value: default_value) | ||||
|       end | ||||
|  | ||||
|       records | ||||
|     end | ||||
|  | ||||
|     private | ||||
|  | ||||
|     def default_settings | ||||
|       return {} unless RailsSettings::Default.enabled? | ||||
|       RailsSettings::Default.instance | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -1,6 +1,8 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class User < ApplicationRecord | ||||
|   include RailsSettings::Extend | ||||
|  | ||||
|   devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable | ||||
|  | ||||
|   belongs_to :account, inverse_of: :user | ||||
| @@ -14,11 +16,6 @@ class User < ApplicationRecord | ||||
|   scope :recent,   -> { order('id desc') } | ||||
|   scope :admins,   -> { where(admin: true) } | ||||
|  | ||||
|   has_settings do |s| | ||||
|     s.key :notification_emails, defaults: { follow: false, reblog: false, favourite: false, mention: false, follow_request: true } | ||||
|     s.key :interactions, defaults: { must_be_follower: false, must_be_following: false } | ||||
|   end | ||||
|  | ||||
|   def send_devise_notification(notification, *args) | ||||
|     devise_mailer.send(notification, self, *args).deliver_later | ||||
|   end | ||||
|   | ||||
| @@ -37,13 +37,13 @@ class NotifyService < BaseService | ||||
|   end | ||||
|  | ||||
|   def blocked? | ||||
|     blocked   = @recipient.suspended?                                                                                             # Skip if the recipient account is suspended anyway | ||||
|     blocked ||= @recipient.id == @notification.from_account.id                                                                    # Skip for interactions with self | ||||
|     blocked ||= @recipient.blocking?(@notification.from_account)                                                                  # Skip for blocked accounts | ||||
|     blocked ||= (@notification.from_account.silenced? && !@recipient.following?(@notification.from_account))                      # Hellban | ||||
|     blocked ||= (@recipient.user.settings(:interactions).must_be_follower  && !@notification.from_account.following?(@recipient)) # Options | ||||
|     blocked ||= (@recipient.user.settings(:interactions).must_be_following && !@recipient.following?(@notification.from_account)) # Options | ||||
|     blocked ||= send("blocked_#{@notification.type}?")                                                                            # Type-dependent filters | ||||
|     blocked   = @recipient.suspended?                                                                                              # Skip if the recipient account is suspended anyway | ||||
|     blocked ||= @recipient.id == @notification.from_account.id                                                                     # Skip for interactions with self | ||||
|     blocked ||= @recipient.blocking?(@notification.from_account)                                                                   # Skip for blocked accounts | ||||
|     blocked ||= (@notification.from_account.silenced? && !@recipient.following?(@notification.from_account))                       # Hellban | ||||
|     blocked ||= (@recipient.user.settings.interactions['must_be_follower']  && !@notification.from_account.following?(@recipient)) # Options | ||||
|     blocked ||= (@recipient.user.settings.interactions['must_be_following'] && !@recipient.following?(@notification.from_account)) # Options | ||||
|     blocked ||= send("blocked_#{@notification.type}?")                                                                             # Type-dependent filters | ||||
|     blocked | ||||
|   end | ||||
|  | ||||
| @@ -58,6 +58,6 @@ class NotifyService < BaseService | ||||
|   end | ||||
|  | ||||
|   def email_enabled? | ||||
|     @recipient.user.settings(:notification_emails).send(@notification.type) | ||||
|     @recipient.user.settings.notification_emails[@notification.type] | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|   %meta{ property: 'og:site_name', content: 'Mastodon' }/ | ||||
|   %meta{ property: 'og:type', content: 'website' }/ | ||||
|   %meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/ | ||||
|   %meta{ property: 'og:description', content: "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" }/ | ||||
|   %meta{ property: 'og:description', content: @description.blank? ? "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" : strip_tags(@description) }/ | ||||
|   %meta{ property: 'og:image', content: asset_url('mastodon_small.jpg') }/ | ||||
|   %meta{ property: 'og:image:width', content: '400' }/ | ||||
|   %meta{ property: 'og:image:height', content: '400' }/ | ||||
| @@ -24,6 +24,9 @@ | ||||
|  | ||||
|   .screenshot= image_tag 'screenshot.png' | ||||
|  | ||||
|   - unless @description.blank? | ||||
|     %p= @description.html_safe | ||||
|  | ||||
|   .actions | ||||
|     .info | ||||
|       = link_to t('about.terms'), terms_path | ||||
|   | ||||
							
								
								
									
										22
									
								
								app/views/admin/settings/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/views/admin/settings/index.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| - content_for :page_title do | ||||
|   Site Settings | ||||
|  | ||||
| %table.table | ||||
|   %colgroup | ||||
|     %col{ width: '35%' }/ | ||||
|   %thead | ||||
|     %tr | ||||
|       %th Setting | ||||
|       %th Click to edit | ||||
|   %tbody | ||||
|     %tr | ||||
|       %td | ||||
|         %strong Site description | ||||
|         %br/ | ||||
|         Displayed as a paragraph on the frontpage and used as a meta tag. | ||||
|         %br/ | ||||
|         You can use HTML tags, in particular | ||||
|         %code= '<a>' | ||||
|         and | ||||
|         %code= '<em>' | ||||
|       %td= best_in_place @settings['site_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_description']) | ||||
| @@ -6,14 +6,14 @@ | ||||
|  | ||||
|   = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) } | ||||
|  | ||||
|   = f.simple_fields_for :notification_emails, current_user.settings(:notification_emails) do |ff| | ||||
|   = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| | ||||
|     = ff.input :follow, as: :boolean, wrapper: :with_label | ||||
|     = ff.input :follow_request, as: :boolean, wrapper: :with_label | ||||
|     = ff.input :reblog, as: :boolean, wrapper: :with_label | ||||
|     = ff.input :favourite, as: :boolean, wrapper: :with_label | ||||
|     = ff.input :mention, as: :boolean, wrapper: :with_label | ||||
|  | ||||
|   = f.simple_fields_for :interactions, current_user.settings(:interactions) do |ff| | ||||
|   = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff| | ||||
|     = ff.input :must_be_follower, as: :boolean, wrapper: :with_label | ||||
|     = ff.input :must_be_following, as: :boolean, wrapper: :with_label | ||||
|  | ||||
|   | ||||
| @@ -7,5 +7,6 @@ SimpleNavigation::Configuration.run do |navigation| | ||||
|     primary.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url | ||||
|     primary.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url | ||||
|     primary.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url | ||||
|     primary.item :settings, safe_join([fa_icon('cogs fw'), 'Site Settings']), admin_settings_url | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -58,6 +58,7 @@ Rails.application.routes.draw do | ||||
|   namespace :admin do | ||||
|     resources :pubsubhubbub, only: [:index] | ||||
|     resources :domain_blocks, only: [:index, :create] | ||||
|     resources :settings, only: [:index, :update] | ||||
|  | ||||
|     resources :accounts, only: [:index, :show, :update] do | ||||
|       member do | ||||
|   | ||||
							
								
								
									
										23
									
								
								config/settings.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								config/settings.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # config/app.yml for rails-settings-cached | ||||
| defaults: &defaults | ||||
|   site_description: '' | ||||
|   site_contact_username: '' | ||||
|   site_contact_email: '' | ||||
|   notification_emails: | ||||
|     follow: false | ||||
|     reblog: false | ||||
|     favourite: false | ||||
|     mention: false | ||||
|     follow_request: true | ||||
|   interactions: | ||||
|     must_be_follower: false | ||||
|     must_be_following: false | ||||
|  | ||||
| development: | ||||
|   <<: *defaults | ||||
|  | ||||
| test: | ||||
|   <<: *defaults | ||||
|  | ||||
| production: | ||||
|   <<: *defaults | ||||
							
								
								
									
										19
									
								
								db/migrate/20170112154826_migrate_settings.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								db/migrate/20170112154826_migrate_settings.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| class MigrateSettings < ActiveRecord::Migration | ||||
|   def up | ||||
|     remove_index :settings, [:target_type, :target_id, :var] | ||||
|     rename_column :settings, :target_id, :thing_id | ||||
|     rename_column :settings, :target_type, :thing_type | ||||
|     change_column :settings, :thing_id, :integer, null: true, default: nil | ||||
|     change_column :settings, :thing_type, :string, null: true, default: nil | ||||
|     add_index :settings, [:thing_type, :thing_id, :var], unique: true | ||||
|   end | ||||
|  | ||||
|   def down | ||||
|     remove_index :settings, [:thing_type, :thing_id, :var] | ||||
|     rename_column :settings, :thing_id, :target_id | ||||
|     rename_column :settings, :thing_type, :target_type | ||||
|     change_column :settings, :target_id, :integer, null: false | ||||
|     change_column :settings, :target_type, :string, null: false, default: '' | ||||
|     add_index :settings, [:target_type, :target_id, :var], unique: true | ||||
|   end | ||||
| end | ||||
							
								
								
									
										10
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								db/schema.rb
									
									
									
									
									
								
							| @@ -10,7 +10,7 @@ | ||||
| # | ||||
| # It's strongly recommended that you check this file into your version control system. | ||||
|  | ||||
| ActiveRecord::Schema.define(version: 20170109120109) do | ||||
| ActiveRecord::Schema.define(version: 20170112154826) do | ||||
|  | ||||
|   # These are extensions that must be enabled in order to support this database | ||||
|   enable_extension "plpgsql" | ||||
| @@ -238,13 +238,13 @@ ActiveRecord::Schema.define(version: 20170109120109) do | ||||
|   end | ||||
|  | ||||
|   create_table "settings", force: :cascade do |t| | ||||
|     t.string   "var",         null: false | ||||
|     t.string   "var",        null: false | ||||
|     t.text     "value" | ||||
|     t.string   "target_type", null: false | ||||
|     t.integer  "target_id",   null: false | ||||
|     t.string   "thing_type" | ||||
|     t.integer  "thing_id" | ||||
|     t.datetime "created_at" | ||||
|     t.datetime "updated_at" | ||||
|     t.index ["target_type", "target_id", "var"], name: "index_settings_on_target_type_and_target_id_and_var", unique: true, using: :btree | ||||
|     t.index ["thing_type", "thing_id", "var"], name: "index_settings_on_thing_type_and_thing_id_and_var", unique: true, using: :btree | ||||
|   end | ||||
|  | ||||
|   create_table "statuses", force: :cascade do |t| | ||||
|   | ||||
		Reference in New Issue
	
	Block a user