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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile +2 -2
- data/Gemfile.lock +89 -102
- data/INSTALL.md +8 -6
- data/README.md +16 -11
- data/app/assets/stylesheets/admin/partials/_forms.scss +13 -0
- data/app/controllers/admin/configuration_controller.rb +1 -1
- data/app/controllers/admin/extensions_controller.rb +1 -0
- data/app/controllers/admin/layouts_controller.rb +2 -1
- data/app/controllers/admin/resource_controller.rb +10 -1
- data/app/controllers/admin/sites_controller.rb +1 -0
- data/app/controllers/admin/snippets_controller.rb +2 -1
- data/app/controllers/admin/users_controller.rb +2 -1
- data/app/helpers/admin/users_helper.rb +2 -1
- data/app/models/admins_site.rb +6 -0
- data/app/models/site.rb +2 -0
- data/app/models/trusty_cms/config.rb +2 -1
- data/app/models/user.rb +15 -4
- data/app/views/admin/layouts/_choose_site.html.haml +5 -3
- data/app/views/admin/layouts/_site_chooser.html.haml +2 -2
- data/app/views/admin/pages/_fields.html.haml +4 -3
- data/app/views/admin/pages/_node.html.haml +1 -1
- data/app/views/admin/snippets/_choose_site.html.haml +2 -1
- data/app/views/admin/users/_choose_site.html.haml +2 -1
- data/app/views/admin/users/_form.html.haml +3 -1
- data/bin/rails +2 -2
- data/config/application.rb +1 -0
- data/config/boot.rb +1 -1
- data/config/locales/en.yml +10 -8
- data/db/migrate/20241108172942_create_site_users.rb +8 -0
- data/lib/generators/extension_controller/extension_controller_generator.rb +1 -1
- data/lib/generators/extension_mailer/extension_mailer_generator.rb +1 -1
- data/lib/generators/extension_model/extension_model_generator.rb +1 -1
- data/lib/generators/instance/instance_generator.rb +24 -20
- data/lib/generators/trusty_cms/templates/boot.rb.erb +1 -1
- data/lib/login_system.rb +15 -15
- data/lib/tasks/framework.rake +3 -3
- data/lib/trusty_cms/available_locales.rb +1 -1
- data/lib/trusty_cms/task_support.rb +1 -1
- data/lib/trusty_cms/version.rb +1 -1
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/db/schema.rb +8 -1
- data/spec/factories/snippet.rb +10 -0
- data/spec/factories/user.rb +11 -11
- data/spec/models/snippets_spec.rb +29 -0
- data/spec/models/user_spec.rb +46 -0
- data/trusty_cms.gemspec +1 -1
- data/vendor/extensions/multi-site-extension/lib/multi_site/scoped_model.rb +17 -11
- data/vendor/extensions/multi-site-extension/lib/multi_site/site_chooser_helper.rb +10 -10
- metadata +12 -5
- 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('
|
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
|
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')
|
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
|
48
|
-
|
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
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
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.
|
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
|
-
-
|
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
|
-
.
|
45
|
-
|
46
|
-
|
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')}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
- unless current_user.site
|
2
2
|
%label{:for=>'snippet_site_id', :Class => 'admin_only'}
|
3
3
|
Site
|
4
|
-
-
|
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
|
-
|
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('
|
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/
|
7
|
-
APP_PATH = File.expand_path('../
|
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__)
|
data/config/application.rb
CHANGED
@@ -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
data/config/locales/en.yml
CHANGED
@@ -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‑Type'
|
162
|
+
content_editor: 'Content Editor'
|
162
163
|
creating_status: 'Creating %{model}…'
|
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:
|
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:
|
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
|
-
|
282
|
+
# Warnings and info text:
|
281
283
|
testing: Testing
|
282
284
|
text:
|
283
285
|
layouts:
|
@@ -71,7 +71,7 @@ class ExtensionControllerGenerator < ControllerGenerator
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def extension_uses_rspec?
|
74
|
-
File.
|
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.
|
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.
|
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
|
-
|
124
|
-
|
125
|
-
|
123
|
+
def banner
|
124
|
+
"Usage: #{$0} /path/to/trusty_cms/app [options]"
|
125
|
+
end
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
+
def radiant_root(filename = '')
|
149
|
+
File.join("..", "..", "..", "..", filename)
|
150
|
+
end
|
151
|
+
end
|
data/lib/login_system.rb
CHANGED
@@ -40,27 +40,27 @@ module LoginSystem
|
|
40
40
|
true
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
data/lib/tasks/framework.rake
CHANGED
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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
|
|
data/lib/trusty_cms/version.rb
CHANGED
@@ -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
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -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:
|
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"
|
data/spec/factories/user.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :user do
|
3
|
-
|
3
|
+
first_name { 'FirstName' }
|
4
|
+
last_name { 'LastName' }
|
4
5
|
email { 'email@test.com' }
|
5
|
-
|
6
|
-
password { 'password' }
|
6
|
+
password { 'ComplexPass1!' }
|
7
7
|
password_confirmation { password }
|
8
8
|
|
9
9
|
factory :admin do
|
10
|
-
|
11
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
31
|
-
|
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.
|
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'
|