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.
- 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'
|