trusty-cms 7.0.1 → 7.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +89 -102
  5. data/INSTALL.md +8 -6
  6. data/README.md +16 -11
  7. data/app/assets/stylesheets/admin/partials/_forms.scss +13 -0
  8. data/app/controllers/admin/configuration_controller.rb +1 -1
  9. data/app/controllers/admin/extensions_controller.rb +1 -0
  10. data/app/controllers/admin/layouts_controller.rb +2 -1
  11. data/app/controllers/admin/resource_controller.rb +10 -1
  12. data/app/controllers/admin/sites_controller.rb +1 -0
  13. data/app/controllers/admin/snippets_controller.rb +2 -1
  14. data/app/controllers/admin/users_controller.rb +2 -1
  15. data/app/helpers/admin/users_helper.rb +2 -1
  16. data/app/models/admins_site.rb +6 -0
  17. data/app/models/site.rb +2 -0
  18. data/app/models/trusty_cms/config.rb +2 -1
  19. data/app/models/user.rb +15 -4
  20. data/app/views/admin/layouts/_choose_site.html.haml +5 -3
  21. data/app/views/admin/layouts/_site_chooser.html.haml +2 -2
  22. data/app/views/admin/pages/_fields.html.haml +4 -3
  23. data/app/views/admin/pages/_node.html.haml +1 -1
  24. data/app/views/admin/snippets/_choose_site.html.haml +2 -1
  25. data/app/views/admin/users/_choose_site.html.haml +2 -1
  26. data/app/views/admin/users/_form.html.haml +3 -1
  27. data/bin/rails +2 -2
  28. data/config/application.rb +1 -0
  29. data/config/boot.rb +1 -1
  30. data/config/locales/en.yml +10 -8
  31. data/db/migrate/20241108172942_create_site_users.rb +8 -0
  32. data/lib/generators/extension_controller/extension_controller_generator.rb +1 -1
  33. data/lib/generators/extension_mailer/extension_mailer_generator.rb +1 -1
  34. data/lib/generators/extension_model/extension_model_generator.rb +1 -1
  35. data/lib/generators/instance/instance_generator.rb +24 -20
  36. data/lib/generators/trusty_cms/templates/boot.rb.erb +1 -1
  37. data/lib/login_system.rb +15 -15
  38. data/lib/tasks/framework.rake +3 -3
  39. data/lib/trusty_cms/available_locales.rb +1 -1
  40. data/lib/trusty_cms/task_support.rb +1 -1
  41. data/lib/trusty_cms/version.rb +1 -1
  42. data/spec/dummy/config/application.rb +2 -0
  43. data/spec/dummy/db/schema.rb +8 -1
  44. data/spec/factories/snippet.rb +10 -0
  45. data/spec/factories/user.rb +11 -11
  46. data/spec/models/snippets_spec.rb +29 -0
  47. data/spec/models/user_spec.rb +46 -0
  48. data/trusty_cms.gemspec +1 -1
  49. data/vendor/extensions/multi-site-extension/lib/multi_site/scoped_model.rb +17 -11
  50. data/vendor/extensions/multi-site-extension/lib/multi_site/site_chooser_helper.rb +10 -10
  51. metadata +12 -5
  52. data/app/users/_choose_site.html.haml +0 -4
@@ -2,7 +2,8 @@ module Admin::UsersHelper
2
2
  def roles(user)
3
3
  roles = []
4
4
  roles << I18n.t('admin') if user.admin?
5
- roles << I18n.t('designer') if user.designer?
5
+ roles << I18n.t('editor') if user.editor?
6
+ roles << I18n.t('content_editor') if user.content_editor?
6
7
  roles.join(', ')
7
8
  end
8
9
  end
@@ -0,0 +1,6 @@
1
+ class AdminsSite < ActiveRecord::Base
2
+ self.table_name = 'admins_sites'
3
+
4
+ belongs_to :admin, class_name: 'User'
5
+ belongs_to :site
6
+ end
data/app/models/site.rb CHANGED
@@ -7,6 +7,8 @@ class Site < ActiveRecord::Base
7
7
  belongs_to :created_by, class_name: 'User'
8
8
  belongs_to :updated_by, class_name: 'User'
9
9
  belongs_to :production_homepage, class_name: 'ProductionPage'
10
+ has_many :admins_sites
11
+ has_many :admins, through: :admins_sites, class_name: 'User'
10
12
 
11
13
  default_scope { order('position ASC') }
12
14
 
@@ -81,7 +81,8 @@ module TrustyCms
81
81
  TrustyCms::Config.initialize_cache
82
82
  end
83
83
  TrustyCms::Config.initialize_cache if TrustyCms::Config.stale_cache?
84
- Rails.cache.read('TrustyCms::Config')[key]
84
+ config_cache = Rails.cache.read('TrustyCms::Config')
85
+ config_cache ? config_cache[key] : nil
85
86
  end
86
87
  end
87
88
  end
data/app/models/user.rb CHANGED
@@ -7,7 +7,6 @@ class User < ActiveRecord::Base
7
7
  :recoverable, :rememberable, :trackable, :validatable
8
8
 
9
9
  alias_attribute :created_by_id, :id
10
-
11
10
  attr_accessor :skip_password_validation
12
11
 
13
12
  validate :password_complexity
@@ -18,6 +17,13 @@ class User < ActiveRecord::Base
18
17
  # Associations
19
18
  belongs_to :created_by, class_name: 'User'
20
19
  belongs_to :updated_by, class_name: 'User'
20
+ has_many :admins_sites, foreign_key: 'admin_id', class_name: 'AdminsSite', dependent: :destroy
21
+ has_many :sites, through: :admins_sites
22
+
23
+ # Roles
24
+ # Admin - all permissions
25
+ # Editor - all permissions except for users, sites editing
26
+ # Content Editor - all permissions except for users, sites, publishing and deleting
21
27
 
22
28
  def role?(role)
23
29
  case role
@@ -40,12 +46,16 @@ class User < ActiveRecord::Base
40
46
  designer
41
47
  end
42
48
 
49
+ def editor?
50
+ designer
51
+ end
52
+
43
53
  def content_editor?
44
54
  content_editor
45
55
  end
46
56
 
47
- def scoped?
48
- site_id.present?
57
+ def scoped_site?
58
+ sites.present?
49
59
  end
50
60
 
51
61
  def locale
@@ -67,4 +77,5 @@ class User < ActiveRecord::Base
67
77
 
68
78
  errors.add :password, 'Complexity requirement not met. Length should be 12 characters and include: 1 uppercase, 1 lowercase, 1 digit and 1 special character.'
69
79
  end
70
- end
80
+
81
+ end
@@ -1,7 +1,9 @@
1
1
  - unless current_user.site
2
2
  %label{:for=>'layout_site_id', :class => 'admin_only'}
3
3
  Site
4
- - sites = Site.all.map { |s| [s.name, s.id] }
5
- - selection = {:include_blank => Layout.is_shareable?}
6
- - selection[:selected] = current_site.id if @layout.new_record? && ! Layout.is_shareable?
4
+ :ruby
5
+ user_sites = current_user.admins_sites.pluck(:site_id)
6
+ sites = Site.where(:id => user_sites).map { |s| [s.name, s.id] }
7
+ selection = {:include_blank => Layout.is_shareable?}
8
+ selection[:selected] = current_site.id if @layout.new_record? && ! Layout.is_shareable?
7
9
  = select :layout, :site_id, sites, selection
@@ -1,9 +1,9 @@
1
- - if current_user.admin? && !current_user.scoped? && defined?(Site) && defined?(controller) && controller.sited_model? && controller.template_name == 'index' && Site.several?
1
+ - if current_user.scoped_site? && defined?(Site) && defined?(controller) && controller.sited_model? && controller.template_name == 'index' && Site.several?
2
2
  .site_chooser
3
3
  %ul.nav
4
4
  %li
5
5
  = "Current Site: #{current_site.name}"
6
6
  %ul.expansion
7
- - Site.all.each do |site|
7
+ - current_user.sites.each do |site|
8
8
  %li
9
9
  = link_to( site.name, "#{request.path}?site_id=#{site.id}", :class => site == current_site ? 'fg site-link' : 'site-link')
@@ -41,9 +41,10 @@
41
41
  = fields.label :class_name, t('page_type')
42
42
  = fields.select :class_name, [[t('select.normal'), '']] + Page.descendants.map { |p| [p.display_name, p.name] }.sort_by { |p| p.first }
43
43
  - layout.edit_status do
44
- .status
45
- = fields.label :status_id, t('status')
46
- = fields.select :status_id, status_to_display
44
+ - if current_user.admin? || current_user.editor?
45
+ .status
46
+ = fields.label :status_id, t('status')
47
+ = fields.select :status_id, status_to_display
47
48
  - layout.edit_published_at do
48
49
  .published-date
49
50
  #published_at{:class => (@page.published? ? nil : 'hidden')}
@@ -18,5 +18,5 @@
18
18
  - unless simple
19
19
  %td.actions
20
20
  = page.add_child_option
21
- - if current_user.admin?
21
+ - if current_user.editor? || current_user.admin?
22
22
  = page.remove_option
@@ -1,5 +1,6 @@
1
1
  - unless current_user.site
2
2
  %label{:for=>'snippet_site_id', :Class => 'admin_only'}
3
3
  Site
4
- - sites = Site.all.map { |s| [s.name, s.id] }
4
+ - user_sites = current_user.admins_sites.pluck(:site_id)
5
+ - sites = Site.where(:id => user_sites).map { |s| [s.name, s.id] }
5
6
  = select :snippet, :site_id, sites, :include_blank => Snippet.is_shareable?, :selected => @snippet.site_id || current_site.id
@@ -1,4 +1,5 @@
1
1
  - if current_user.admin?
2
2
  %label{:for=>'user_admin'} Can edit site
3
- = select :user, :site_id, [['<any>', '']] + Site.all.map { |s| [s.name, s.id] }
3
+ .caption (hold ctrl or cmd to select multiple)
4
4
  .caption A user with no site is able to act (to whatever extent their status allows) on any site.
5
+ = select :user, :site_ids, options_for_select(Site.all.map { |s| [s.name, s.id] }, selected: @user.site_ids), {}, { multiple: true }
@@ -25,7 +25,9 @@
25
25
  = f.check_box 'admin', :class => 'checkbox'
26
26
  = f.label :admin, t('admin'), :class => 'checkbox'
27
27
  = f.check_box 'designer', :class => 'checkbox'
28
- = f.label :designer, t('designer'), :class => 'checkbox'
28
+ = f.label :designer, t('editor'), :class => 'checkbox'
29
+ = f.check_box 'content_editor', :class => 'checkbox'
30
+ = f.label :content_editor, t('content_editor'), :class => 'checkbox'
29
31
 
30
32
  - form.edit_notes do
31
33
  %fieldset
data/bin/rails CHANGED
@@ -3,8 +3,8 @@
3
3
  # installed from the root of your application.
4
4
 
5
5
  ENGINE_ROOT = File.expand_path('..', __dir__)
6
- ENGINE_PATH = File.expand_path('../lib/trusty/cms/engine', __dir__)
7
- APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/trusty_cms/engine', __dir__)
7
+ APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__)
8
8
 
9
9
  # Set up gems listed in the Gemfile.
10
10
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
@@ -20,6 +20,7 @@ module TrustyCms
20
20
  Rails.autoloaders.log!
21
21
  # Enable the asset pipeline
22
22
  config.assets.enabled = true
23
+ config.active_record.legacy_connection_handling = false
23
24
 
24
25
  # Version of your assets, change this if you want to expire all your assets
25
26
  config.assets.version = '1.0'
data/config/boot.rb CHANGED
@@ -4,4 +4,4 @@
4
4
  # Set up gems listed in the Gemfile.
5
5
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
6
6
 
7
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
7
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
@@ -140,7 +140,7 @@ en:
140
140
  secret: 'Secret'
141
141
  skip_filetype_validation: 'Skip Filetype Validation'
142
142
  storage: 'Storage'
143
- url: 'Url'
143
+ url: 'Url'
144
144
  defaults:
145
145
  locale: 'default language'
146
146
  page:
@@ -159,17 +159,18 @@ en:
159
159
  allow_password_reset?: "allow password reset"
160
160
  content: 'Content'
161
161
  content_type: 'Content&#8209;Type'
162
+ content_editor: 'Content Editor'
162
163
  creating_status: 'Creating %{model}&#8230;'
163
164
  date:
164
- abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
165
- abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
166
- day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
165
+ abbr_day_names: [ Sun, Mon, Tue, Wed, Thu, Fri, Sat ]
166
+ abbr_month_names: [ ~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec ]
167
+ day_names: [ Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday ]
167
168
  formats:
168
169
  default: "%Y-%m-%d"
169
170
  long: "%B %e, %Y"
170
171
  only_day: "%e"
171
172
  short: "%e %b"
172
- month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
173
+ month_names: [ ~, January, February, March, April, May, June, July, August, September, October, November, December ]
173
174
  order:
174
175
  - :year
175
176
  - :month
@@ -183,9 +184,10 @@ en:
183
184
  designer: 'Designer'
184
185
  draft: 'Draft'
185
186
  edit: 'Edit'
187
+ editor: 'Editor'
186
188
  edit_configuration: 'Edit Configuration'
187
189
  edit_layout: 'Edit Layout'
188
- edit_page: 'Edit Page'
190
+ edit_page: 'Edit Page'
189
191
  edit_preferences: 'Edit Preferences'
190
192
  edit_settings: 'Edit Settings'
191
193
  edit_user: 'Edit User'
@@ -242,7 +244,7 @@ en:
242
244
  published: 'Published'
243
245
  published_at: 'Published at'
244
246
  published_on: 'Published On'
245
- reference: 'Reference'
247
+ reference: 'Reference'
246
248
  remember_me: 'Remember me'
247
249
  remember_me_in_this_browser: 'Remember me in this browser'
248
250
  remove: 'Remove'
@@ -277,7 +279,7 @@ en:
277
279
  snippets: 'Snippets'
278
280
  snippet: 'Snippet'
279
281
  status: 'Status'
280
- # Warnings and info text:
282
+ # Warnings and info text:
281
283
  testing: Testing
282
284
  text:
283
285
  layouts:
@@ -0,0 +1,8 @@
1
+ class CreateSiteUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :admins_sites do |t|
4
+ t.integer :admin_id, null: false, index: true
5
+ t.integer :site_id, null: false, index: true
6
+ end
7
+ end
8
+ end
@@ -71,7 +71,7 @@ class ExtensionControllerGenerator < ControllerGenerator
71
71
  end
72
72
 
73
73
  def extension_uses_rspec?
74
- File.exists?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
74
+ File.exist?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
75
75
  end
76
76
 
77
77
  def add_options!(opt)
@@ -55,7 +55,7 @@ class ExtensionMailerGenerator < MailerGenerator
55
55
  end
56
56
 
57
57
  def extension_uses_rspec?
58
- File.exists?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
58
+ File.exist?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
59
59
  end
60
60
 
61
61
  def add_options!(opt)
@@ -55,7 +55,7 @@ class ExtensionModelGenerator < ModelGenerator
55
55
  end
56
56
 
57
57
  def extension_uses_rspec?
58
- File.exists?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
58
+ File.exist?(File.join(destination_root, 'spec')) && !options[:with_test_unit]
59
59
  end
60
60
 
61
61
  def add_options!(opt)
@@ -120,28 +120,32 @@ class InstanceGenerator < Rails::Generator::Base
120
120
 
121
121
  protected
122
122
 
123
- def banner
124
- "Usage: #{$0} /path/to/trusty_cms/app [options]"
125
- end
123
+ def banner
124
+ "Usage: #{$0} /path/to/trusty_cms/app [options]"
125
+ end
126
126
 
127
- def add_options!(opt)
128
- opt.separator ''
129
- opt.separator 'Options:'
130
- opt.on("-r", "--ruby=path", String,
131
- "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
132
- "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
133
- opt.on("-d", "--database=name", String,
134
- "Preconfigure for selected database (options: #{DATABASES.join(", ")}).",
135
- "Default: sqlite3") { |v| options[:db] = v }
136
- end
127
+ def add_options!(opt)
128
+ opt.separator ''
129
+ opt.separator 'Options:'
130
+ opt.on(
131
+ "-r", "--ruby=path", String,
132
+ "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
133
+ "Default: #{DEFAULT_SHEBANG}"
134
+ ) { |v| options[:shebang] = v }
135
+ opt.on(
136
+ "-d", "--database=name", String,
137
+ "Preconfigure for selected database (options: #{DATABASES.join(", ")}).",
138
+ "Default: sqlite3"
139
+ ) { |v| options[:db] = v }
140
+ end
137
141
 
138
- def mysql_socket_location
139
- RUBY_PLATFORM =~ /mswin32/ ? MYSQL_SOCKET_LOCATIONS.find { |f| File.exists?(f) } : nil
140
- end
142
+ def mysql_socket_location
143
+ RUBY_PLATFORM =~ /mswin32/ ? MYSQL_SOCKET_LOCATIONS.find { |f| File.exist?(f) } : nil
144
+ end
141
145
 
142
146
  private
143
147
 
144
- def radiant_root(filename = '')
145
- File.join("..", "..", "..", "..", filename)
146
- end
147
- end
148
+ def radiant_root(filename = '')
149
+ File.join("..", "..", "..", "..", filename)
150
+ end
151
+ end
@@ -6,4 +6,4 @@ require 'rubygems'
6
6
  # Set up gems listed in the Gemfile.
7
7
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
8
8
 
9
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
9
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
data/lib/login_system.rb CHANGED
@@ -40,27 +40,27 @@ module LoginSystem
40
40
  true
41
41
  end
42
42
 
43
- def authorize
44
- # puts _process_action_callbacks.map(&:filter)
45
- # action = action_name.to_s.intern
46
- # if user_has_access_to_action?(action)
47
- # true
48
- # else
49
- # permissions = self.class.controller_permissions[action]
50
- # flash[:error] = permissions[:denied_message] || 'Access denied.'
51
- # respond_to do |format|
52
- # format.html { redirect_to(permissions[:denied_url] || { :action => :index }) }
53
- # format.any(:xml, :json) { head :forbidden }
54
- # end
55
- # false
56
- # end
57
- true
43
+ def authorize_role
44
+ action = action_name.to_s.intern
45
+ return true if user_has_access_to_action?(action)
46
+
47
+ handle_unauthorized_access(action)
48
+ false
58
49
  end
59
50
 
60
51
  def user_has_access_to_action?(action)
61
52
  self.class.user_has_access_to_action?(current_user, action, self)
62
53
  end
63
54
 
55
+ def handle_unauthorized_access(action)
56
+ permissions = self.class.controller_permissions[action]
57
+ flash[:error] = permissions[:denied_message] || 'Access denied.'
58
+ respond_to do |format|
59
+ format.html { redirect_to(permissions[:denied_url] || { action: :index }) }
60
+ format.any(:xml, :json) { head :forbidden }
61
+ end
62
+ end
63
+
64
64
  def login_from_session
65
65
  User.unscoped.find(session['user_id'])
66
66
  rescue StandardError
@@ -98,7 +98,7 @@ unless File.directory? "#{Rails.root}/app"
98
98
  task :javascripts do
99
99
  FileUtils.mkdir_p("#{Rails.root}/public/javascripts/admin/")
100
100
  copy_javascripts = proc do |project_dir, scripts|
101
- scripts.reject! { |s| File.basename(s) == 'overrides.js' } if File.exists?(project_dir + 'overrides.js')
101
+ scripts.reject! { |s| File.basename(s) == 'overrides.js' } if File.exist?(project_dir + 'overrides.js')
102
102
  FileUtils.cp(scripts, project_dir)
103
103
  end
104
104
  copy_javascripts[Rails.root + '/public/javascripts/', Dir["#{File.dirname(__FILE__)}/../../public/javascripts/*.js"]]
@@ -228,7 +228,7 @@ the new files: #{warning}"
228
228
  project_dir = Rails.root + '/public/stylesheets/admin/'
229
229
 
230
230
  copy_stylesheets = proc do |project_dir, styles|
231
- styles.reject! { |s| File.basename(s) == 'overrides.css' } if File.exists?(project_dir + 'overrides.css')
231
+ styles.reject! { |s| File.basename(s) == 'overrides.css' } if File.exist?(project_dir + 'overrides.css')
232
232
  FileUtils.cp(styles, project_dir)
233
233
  end
234
234
  copy_stylesheets[Rails.root + '/public/stylesheets/admin/', Dir["#{File.dirname(__FILE__)}/../../public/stylesheets/admin/*.css"]]
@@ -237,7 +237,7 @@ the new files: #{warning}"
237
237
  desc 'Update admin sass files from your current trusty install'
238
238
  task :sass do
239
239
  copy_sass = proc do |project_dir, sass_files|
240
- sass_files.reject! { |s| File.basename(s) == 'overrides.sass' } if File.exists?(project_dir + 'overrides.sass') || File.exists?(project_dir + '../overrides.css')
240
+ sass_files.reject! { |s| File.basename(s) == 'overrides.sass' } if File.exist?(project_dir + 'overrides.sass') || File.exist?(project_dir + '../overrides.css')
241
241
  sass_files.reject! { |s| File.directory?(s) }
242
242
  FileUtils.mkpath(project_dir)
243
243
  FileUtils.cp(sass_files, project_dir)
@@ -4,7 +4,7 @@ module TrustyCms::AvailableLocales
4
4
  def self.locales
5
5
  available_locales = {}
6
6
  TrustyCms.configuration.i18n.load_path.each do |path|
7
- if File.exists?(path) && path !~ /_available_tags/
7
+ if File.exist?(path) && path !~ /_available_tags/
8
8
  locale_yaml = YAML.load_file(path)
9
9
  stem = File.basename(path, '.yml')
10
10
  if locale_yaml[stem] && lang = locale_yaml[stem]['this_file_language']
@@ -47,7 +47,7 @@ module TrustyCms
47
47
  end.join("\n\n")
48
48
 
49
49
  cache_path = File.join(dir, cache_file)
50
- File.delete(cache_path) if File.exists?(cache_path)
50
+ File.delete(cache_path) if File.exist?(cache_path)
51
51
  File.open(cache_path, 'w+') { |f| f.write(cache_content) }
52
52
  end
53
53
 
@@ -1,4 +1,4 @@
1
1
  module TrustyCms
2
- VERSION = '7.0.1'.freeze
2
+ VERSION = '7.0.3'.freeze
3
3
  end
4
4
 
@@ -31,6 +31,8 @@ module TrustyCms
31
31
 
32
32
  # Initialize extension paths
33
33
  config.initialize_extension_paths
34
+ config.active_record.legacy_connection_handling = false
35
+
34
36
  extension_loader = ExtensionLoader.instance { |l| l.initializer = self }
35
37
  extension_loader.paths(:load).reverse_each do |path, value|
36
38
  config.autoload_paths.unshift path
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema[7.0].define(version: 2023_10_19_192097) do
13
+ ActiveRecord::Schema[7.0].define(version: 2024_11_08_172942) do
14
14
  create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
15
15
  t.string "name", null: false
16
16
  t.string "record_type", null: false
@@ -69,6 +69,13 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_19_192097) do
69
69
  t.datetime "updated_at", precision: nil, null: false
70
70
  end
71
71
 
72
+ create_table "admins_sites", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
73
+ t.integer "admin_id", null: false
74
+ t.integer "site_id", null: false
75
+ t.index ["admin_id"], name: "index_admins_sites_on_admin_id"
76
+ t.index ["site_id"], name: "index_admins_sites_on_site_id"
77
+ end
78
+
72
79
  create_table "assets", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
73
80
  t.string "caption"
74
81
  t.string "title"
@@ -0,0 +1,10 @@
1
+ FactoryBot.define do
2
+
3
+ factory :snippet do
4
+ name { 'test_snippet' }
5
+ content { <<-CONTENT }
6
+ <p>Snippet Stuff</p>
7
+ CONTENT
8
+ end
9
+
10
+ end
@@ -1,34 +1,34 @@
1
1
  FactoryBot.define do
2
2
  factory :user do
3
- name { 'User' }
3
+ first_name { 'FirstName' }
4
+ last_name { 'LastName' }
4
5
  email { 'email@test.com' }
5
- login { 'user' }
6
- password { 'password' }
6
+ password { 'ComplexPass1!' }
7
7
  password_confirmation { password }
8
8
 
9
9
  factory :admin do
10
- name { 'Admin' }
11
- login { 'admin' }
10
+ first_name { 'FirstName' }
11
+ last_name { 'LastName' }
12
12
  email { 'admin@example.com' }
13
13
  admin { true }
14
14
  end
15
15
 
16
16
  factory :existing do
17
- name { 'Existing' }
18
- login { 'existing' }
17
+ first_name { 'FirstName' }
18
+ last_name { 'LastName' }
19
19
  email { 'existing@example.com' }
20
20
  end
21
21
 
22
22
  factory :designer do
23
- name { 'Designer' }
24
- login { 'designer' }
23
+ first_name { 'FirstName' }
24
+ last_name { 'LastName' }
25
25
  email { '' }
26
26
  designer { true }
27
27
  end
28
28
 
29
29
  factory :non_admin do
30
- name { 'Non Admin' }
31
- login { 'non_admin' }
30
+ first_name { 'FirstName' }
31
+ last_name { 'LastName' }
32
32
  admin { false }
33
33
  end
34
34
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Snippet do
4
+
5
+ let(:snippet) { FactoryBot.build(:snippet) }
6
+
7
+ describe 'name' do
8
+ it 'is invalid when blank' do
9
+ snippet = FactoryBot.build(:snippet, name: '')
10
+ snippet.valid?
11
+ expect(snippet.errors[:name]).to include("This field is required.")
12
+ end
13
+
14
+ it 'should validate uniqueness of' do
15
+ snippet = FactoryBot.build(:snippet, name: 'test_snippet', content: "Content!")
16
+ snippet.save!
17
+ other = FactoryBot.build(:snippet, name: 'test_snippet', content: "Content!")
18
+ expect { other.save! }.to raise_error(ActiveRecord::RecordInvalid)
19
+ end
20
+
21
+ it 'should validate length of' do
22
+ snippet = FactoryBot.build(:snippet, name: 'x' * 100)
23
+ expect(snippet.errors[:name]).to be_blank
24
+ snippet = FactoryBot.build(:snippet, name: 'x' * 101)
25
+ expect { snippet.save! }.to raise_error(ActiveRecord::RecordInvalid)
26
+ expect(snippet.errors[:name]).to include("This must not be longer than 100 characters")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+ describe '#role?' do
5
+ let(:user) { create(:user) }
6
+
7
+ it 'returns true for admin role' do
8
+ user.admin = true
9
+ expect(user.role?(:admin)).to be true
10
+ end
11
+
12
+ it 'returns false for non-admin role' do
13
+ user.admin = false
14
+ expect(user.role?(:admin)).to be false
15
+ end
16
+ end
17
+
18
+ describe '#password_complexity' do
19
+ let(:user) { build(:user) }
20
+
21
+ it 'is valid with a complex password' do
22
+ user.password = 'ComplexPass1!'
23
+ expect(user).to be_valid
24
+ end
25
+
26
+ it 'is invalid with a simple password' do
27
+ user.password = 'simple'
28
+ user.valid?
29
+ expect(user.errors[:password]).to include('Complexity requirement not met. Length should be 12 characters and include: 1 uppercase, 1 lowercase, 1 digit and 1 special character.')
30
+ end
31
+ end
32
+
33
+ describe '#password_required?' do
34
+ let(:user) { build(:user) }
35
+
36
+ it 'returns false if skip_password_validation is true' do
37
+ user.skip_password_validation = true
38
+ expect(user.password_required?).to be false
39
+ end
40
+
41
+ it 'returns true if skip_password_validation is false' do
42
+ user.skip_password_validation = false
43
+ expect(user.password_required?).to be true
44
+ end
45
+ end
46
+ end
data/trusty_cms.gemspec CHANGED
@@ -43,7 +43,7 @@ a general purpose content management system--not merely a blogging engine.'
43
43
  s.add_dependency 'mini_racer'
44
44
  s.add_dependency 'mutex_m'
45
45
  s.add_dependency 'mysql2'
46
- s.add_dependency 'rack', '>= 2.0.1', '< 3.1.0'
46
+ s.add_dependency 'rack', '>= 2.0.1', '< 3.2.0'
47
47
  s.add_dependency 'rack-cache', '~> 1.7'
48
48
  s.add_dependency 'radius', '~> 0.7'
49
49
  s.add_dependency 'rails', '~> 7.0.0'