trusty-cms 7.0.1 → 7.0.3

Sign up to get free protection for your applications and to get access to all the features.
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'