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 frontpagemain
parent
babc6a1528
commit
b11fdc3ae3
3
Gemfile
3
Gemfile
|
@ -17,6 +17,7 @@ gem 'pg'
|
||||||
gem 'pghero'
|
gem 'pghero'
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
gem 'font-awesome-rails'
|
gem 'font-awesome-rails'
|
||||||
|
gem 'best_in_place', '~> 3.0.1'
|
||||||
|
|
||||||
gem 'paperclip', '~> 5.0'
|
gem 'paperclip', '~> 5.0'
|
||||||
gem 'paperclip-av-transcoder'
|
gem 'paperclip-av-transcoder'
|
||||||
|
@ -43,7 +44,7 @@ gem 'will_paginate'
|
||||||
gem 'rack-attack'
|
gem 'rack-attack'
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'rack-cors', require: 'rack/cors'
|
||||||
gem 'sidekiq'
|
gem 'sidekiq'
|
||||||
gem 'ledermann-rails-settings'
|
gem 'rails-settings-cached'
|
||||||
gem 'pg_search'
|
gem 'pg_search'
|
||||||
gem 'simple-navigation'
|
gem 'simple-navigation'
|
||||||
|
|
||||||
|
|
10
Gemfile.lock
10
Gemfile.lock
|
@ -60,6 +60,9 @@ GEM
|
||||||
babel-source (>= 4.0, < 6)
|
babel-source (>= 4.0, < 6)
|
||||||
execjs (~> 2.0)
|
execjs (~> 2.0)
|
||||||
bcrypt (3.1.11)
|
bcrypt (3.1.11)
|
||||||
|
best_in_place (3.0.3)
|
||||||
|
actionpack (>= 3.2)
|
||||||
|
railties (>= 3.2)
|
||||||
better_errors (2.1.1)
|
better_errors (2.1.1)
|
||||||
coderay (>= 1.0.0)
|
coderay (>= 1.0.0)
|
||||||
erubis (>= 2.6.6)
|
erubis (>= 2.6.6)
|
||||||
|
@ -172,8 +175,6 @@ GEM
|
||||||
json (1.8.3)
|
json (1.8.3)
|
||||||
launchy (2.4.3)
|
launchy (2.4.3)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
ledermann-rails-settings (2.4.2)
|
|
||||||
activerecord (>= 3.1)
|
|
||||||
letter_opener (1.4.1)
|
letter_opener (1.4.1)
|
||||||
launchy (~> 2.2)
|
launchy (~> 2.2)
|
||||||
link_header (0.0.8)
|
link_header (0.0.8)
|
||||||
|
@ -259,6 +260,8 @@ GEM
|
||||||
nokogiri (~> 1.6.0)
|
nokogiri (~> 1.6.0)
|
||||||
rails-html-sanitizer (1.0.3)
|
rails-html-sanitizer (1.0.3)
|
||||||
loofah (~> 2.0)
|
loofah (~> 2.0)
|
||||||
|
rails-settings-cached (0.6.5)
|
||||||
|
rails (>= 4.2.0)
|
||||||
rails_12factor (0.0.3)
|
rails_12factor (0.0.3)
|
||||||
rails_serve_static_assets
|
rails_serve_static_assets
|
||||||
rails_stdout_logging
|
rails_stdout_logging
|
||||||
|
@ -405,6 +408,7 @@ DEPENDENCIES
|
||||||
addressable
|
addressable
|
||||||
autoprefixer-rails
|
autoprefixer-rails
|
||||||
aws-sdk (>= 2.0)
|
aws-sdk (>= 2.0)
|
||||||
|
best_in_place (~> 3.0.1)
|
||||||
better_errors
|
better_errors
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
browserify-rails
|
browserify-rails
|
||||||
|
@ -426,7 +430,6 @@ DEPENDENCIES
|
||||||
i18n-tasks (~> 0.9.6)
|
i18n-tasks (~> 0.9.6)
|
||||||
jbuilder (~> 2.0)
|
jbuilder (~> 2.0)
|
||||||
jquery-rails
|
jquery-rails
|
||||||
ledermann-rails-settings
|
|
||||||
letter_opener
|
letter_opener
|
||||||
link_header
|
link_header
|
||||||
lograge
|
lograge
|
||||||
|
@ -445,6 +448,7 @@ DEPENDENCIES
|
||||||
rack-cors
|
rack-cors
|
||||||
rack-timeout-puma
|
rack-timeout-puma
|
||||||
rails (~> 5.0.1.0)
|
rails (~> 5.0.1.0)
|
||||||
|
rails-settings-cached
|
||||||
rails_12factor
|
rails_12factor
|
||||||
rails_autolink
|
rails_autolink
|
||||||
react-rails
|
react-rails
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
//= require extras
|
//= require extras
|
||||||
|
//= require best_in_place
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$(".best_in_place").best_in_place();
|
||||||
|
});
|
||||||
|
|
|
@ -36,6 +36,10 @@
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
samp {
|
samp {
|
||||||
|
|
|
@ -4,10 +4,10 @@ class AboutController < ApplicationController
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@description = Setting.site_description
|
||||||
end
|
end
|
||||||
|
|
||||||
def terms
|
def terms; end
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
|
@ -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 show; end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
current_user.settings(:notification_emails).follow = user_params[:notification_emails][:follow] == '1'
|
current_user.settings['notification_emails'] = {
|
||||||
current_user.settings(:notification_emails).follow_request = user_params[:notification_emails][:follow_request] == '1'
|
follow: user_params[:notification_emails][:follow] == '1',
|
||||||
current_user.settings(:notification_emails).reblog = user_params[:notification_emails][:reblog] == '1'
|
follow_request: user_params[:notification_emails][:follow_request] == '1',
|
||||||
current_user.settings(:notification_emails).favourite = user_params[:notification_emails][:favourite] == '1'
|
reblog: user_params[:notification_emails][:reblog] == '1',
|
||||||
current_user.settings(:notification_emails).mention = user_params[:notification_emails][:mention] == '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'] = {
|
||||||
current_user.settings(:interactions).must_be_following = user_params[:interactions][:must_be_following] == '1'
|
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))
|
if current_user.update(user_params.except(:notification_emails, :interactions))
|
||||||
redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg')
|
redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg')
|
||||||
|
|
|
@ -14,4 +14,8 @@ module SettingsHelper
|
||||||
def human_locale(locale)
|
def human_locale(locale)
|
||||||
HUMAN_LOCALES[locale]
|
HUMAN_LOCALES[locale]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def hash_to_object(hash)
|
||||||
|
HashObject.new(hash)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
@ -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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
|
include RailsSettings::Extend
|
||||||
|
|
||||||
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
|
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
|
||||||
|
|
||||||
belongs_to :account, inverse_of: :user
|
belongs_to :account, inverse_of: :user
|
||||||
|
@ -14,11 +16,6 @@ class User < ApplicationRecord
|
||||||
scope :recent, -> { order('id desc') }
|
scope :recent, -> { order('id desc') }
|
||||||
scope :admins, -> { where(admin: true) }
|
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)
|
def send_devise_notification(notification, *args)
|
||||||
devise_mailer.send(notification, self, *args).deliver_later
|
devise_mailer.send(notification, self, *args).deliver_later
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,8 +41,8 @@ class NotifyService < BaseService
|
||||||
blocked ||= @recipient.id == @notification.from_account.id # Skip for interactions with self
|
blocked ||= @recipient.id == @notification.from_account.id # Skip for interactions with self
|
||||||
blocked ||= @recipient.blocking?(@notification.from_account) # Skip for blocked accounts
|
blocked ||= @recipient.blocking?(@notification.from_account) # Skip for blocked accounts
|
||||||
blocked ||= (@notification.from_account.silenced? && !@recipient.following?(@notification.from_account)) # Hellban
|
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_follower'] && !@notification.from_account.following?(@recipient)) # Options
|
||||||
blocked ||= (@recipient.user.settings(:interactions).must_be_following && !@recipient.following?(@notification.from_account)) # Options
|
blocked ||= (@recipient.user.settings.interactions['must_be_following'] && !@recipient.following?(@notification.from_account)) # Options
|
||||||
blocked ||= send("blocked_#{@notification.type}?") # Type-dependent filters
|
blocked ||= send("blocked_#{@notification.type}?") # Type-dependent filters
|
||||||
blocked
|
blocked
|
||||||
end
|
end
|
||||||
|
@ -58,6 +58,6 @@ class NotifyService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def email_enabled?
|
def email_enabled?
|
||||||
@recipient.user.settings(:notification_emails).send(@notification.type)
|
@recipient.user.settings.notification_emails[@notification.type]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
%meta{ property: 'og:site_name', content: 'Mastodon' }/
|
%meta{ property: 'og:site_name', content: 'Mastodon' }/
|
||||||
%meta{ property: 'og:type', content: 'website' }/
|
%meta{ property: 'og:type', content: 'website' }/
|
||||||
%meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/
|
%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', content: asset_url('mastodon_small.jpg') }/
|
||||||
%meta{ property: 'og:image:width', content: '400' }/
|
%meta{ property: 'og:image:width', content: '400' }/
|
||||||
%meta{ property: 'og:image:height', content: '400' }/
|
%meta{ property: 'og:image:height', content: '400' }/
|
||||||
|
@ -24,6 +24,9 @@
|
||||||
|
|
||||||
.screenshot= image_tag 'screenshot.png'
|
.screenshot= image_tag 'screenshot.png'
|
||||||
|
|
||||||
|
- unless @description.blank?
|
||||||
|
%p= @description.html_safe
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
.info
|
.info
|
||||||
= link_to t('about.terms'), terms_path
|
= link_to t('about.terms'), terms_path
|
||||||
|
|
|
@ -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.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, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :follow_request, as: :boolean, wrapper: :with_label
|
= ff.input :follow_request, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :reblog, as: :boolean, wrapper: :with_label
|
= ff.input :reblog, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :favourite, as: :boolean, wrapper: :with_label
|
= ff.input :favourite, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :mention, 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_follower, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :must_be_following, 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 :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 :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url
|
||||||
primary.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_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
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,6 +58,7 @@ Rails.application.routes.draw do
|
||||||
namespace :admin do
|
namespace :admin do
|
||||||
resources :pubsubhubbub, only: [:index]
|
resources :pubsubhubbub, only: [:index]
|
||||||
resources :domain_blocks, only: [:index, :create]
|
resources :domain_blocks, only: [:index, :create]
|
||||||
|
resources :settings, only: [:index, :update]
|
||||||
|
|
||||||
resources :accounts, only: [:index, :show, :update] do
|
resources :accounts, only: [:index, :show, :update] do
|
||||||
member do
|
member do
|
||||||
|
|
|
@ -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
|
|
@ -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,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -240,11 +240,11 @@ ActiveRecord::Schema.define(version: 20170109120109) do
|
||||||
create_table "settings", force: :cascade do |t|
|
create_table "settings", force: :cascade do |t|
|
||||||
t.string "var", null: false
|
t.string "var", null: false
|
||||||
t.text "value"
|
t.text "value"
|
||||||
t.string "target_type", null: false
|
t.string "thing_type"
|
||||||
t.integer "target_id", null: false
|
t.integer "thing_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_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
|
end
|
||||||
|
|
||||||
create_table "statuses", force: :cascade do |t|
|
create_table "statuses", force: :cascade do |t|
|
||||||
|
|
Loading…
Reference in New Issue