commit: b11fdc3ae3f90731c01149a5a36dc64e065d4ea2
parent: babc6a1528e2f7d777d81a972f2d50c58381f45e
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 12 Jan 2017 20:46:24 +0100
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
Diffstat:
20 files changed, 189 insertions(+), 35 deletions(-)
diff --git a/Gemfile b/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'
diff --git a/Gemfile.lock b/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
diff --git a/app/assets/javascripts/application_public.js b/app/assets/javascripts/application_public.js
@@ -1,3 +1,8 @@
//= require jquery
//= require jquery_ujs
//= require extras
+//= require best_in_place
+
+$(function () {
+ $(".best_in_place").best_in_place();
+});
diff --git a/app/assets/stylesheets/tables.scss b/app/assets/stylesheets/tables.scss
@@ -36,6 +36,10 @@
text-decoration: none;
}
}
+
+ strong {
+ font-weight: 500;
+ }
}
samp {
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
@@ -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
diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb
@@ -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
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
@@ -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(: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['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',
+ 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')
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
@@ -14,4 +14,8 @@ module SettingsHelper
def human_locale(locale)
HUMAN_LOCALES[locale]
end
+
+ def hash_to_object(hash)
+ HashObject.new(hash)
+ end
end
diff --git a/app/lib/hash_object.rb b/app/lib/hash_object.rb
@@ -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
diff --git a/app/models/setting.rb b/app/models/setting.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
@@ -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
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
@@ -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
diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml
@@ -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
diff --git a/app/views/admin/settings/index.html.haml b/app/views/admin/settings/index.html.haml
@@ -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'])
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
@@ -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
diff --git a/config/navigation.rb b/config/navigation.rb
@@ -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
diff --git a/config/routes.rb b/config/routes.rb
@@ -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
diff --git a/config/settings.yml b/config/settings.yml
@@ -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
diff --git a/db/migrate/20170112154826_migrate_settings.rb b/db/migrate/20170112154826_migrate_settings.rb
@@ -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
diff --git a/db/schema.rb b/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|