ruby_cms 0.1.1 → 0.1.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/CHANGELOG.md +10 -0
- data/README.md +68 -164
- data/app/components/ruby_cms/admin/admin_page.rb +19 -19
- data/app/components/ruby_cms/admin/admin_page_header.rb +79 -0
- data/app/components/ruby_cms/admin/admin_resource_card.rb +55 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +4 -4
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +5 -5
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +1 -1
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +15 -13
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +13 -11
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +9 -9
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +2 -2
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +8 -8
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +9 -9
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +3 -4
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +46 -32
- data/app/controllers/ruby_cms/admin/base_controller.rb +10 -4
- data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +4 -3
- data/app/controllers/ruby_cms/admin/locale_controller.rb +2 -1
- data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +25 -7
- data/app/helpers/ruby_cms/content_blocks_helper.rb +7 -3
- data/app/helpers/ruby_cms/settings_helper.rb +32 -20
- data/app/javascript/controllers/ruby_cms/bulk_action_table_controller.js +53 -12
- data/app/models/ruby_cms/permission.rb +38 -9
- data/app/models/ruby_cms/permittable.rb +0 -2
- data/app/services/ruby_cms/analytics/report.rb +37 -3
- data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +2 -2
- data/app/views/layouts/ruby_cms/admin.html.erb +13 -17
- data/app/views/ruby_cms/admin/analytics/index.html.erb +103 -108
- data/app/views/ruby_cms/admin/analytics/page_details.html.erb +28 -32
- data/app/views/ruby_cms/admin/analytics/partials/_back_button.html.erb +3 -2
- data/app/views/ruby_cms/admin/analytics/partials/_browser_device.html.erb +34 -20
- data/app/views/ruby_cms/admin/analytics/partials/_daily_activity_chart.html.erb +27 -27
- data/app/views/ruby_cms/admin/analytics/partials/_hourly_activity_chart.html.erb +19 -19
- data/app/views/ruby_cms/admin/analytics/partials/_landing_pages.html.erb +21 -0
- data/app/views/ruby_cms/admin/analytics/partials/_os_stats.html.erb +26 -0
- data/app/views/ruby_cms/admin/analytics/partials/_popular_pages.html.erb +34 -0
- data/app/views/ruby_cms/admin/analytics/partials/_recent_activity.html.erb +16 -14
- data/app/views/ruby_cms/admin/analytics/partials/_security_alert.html.erb +3 -3
- data/app/views/ruby_cms/admin/analytics/partials/_top_referrers.html.erb +14 -14
- data/app/views/ruby_cms/admin/analytics/partials/_top_visitors.html.erb +28 -0
- data/app/views/ruby_cms/admin/analytics/partials/_utm_sources.html.erb +21 -0
- data/app/views/ruby_cms/admin/analytics/visitor_details.html.erb +44 -48
- data/app/views/ruby_cms/admin/content_blocks/index.html.erb +0 -11
- data/app/views/ruby_cms/admin/content_blocks/show.html.erb +204 -85
- data/app/views/ruby_cms/admin/settings/index.html.erb +214 -175
- data/app/views/ruby_cms/admin/user_permissions/index.html.erb +32 -2
- data/app/views/ruby_cms/admin/users/_row.html.erb +4 -1
- data/config/locales/en.yml +4 -0
- data/lib/generators/ruby_cms/install_generator.rb +2 -1
- data/lib/ruby_cms/cli.rb +1 -1
- data/lib/ruby_cms/engine.rb +20 -12
- data/lib/ruby_cms/version.rb +1 -1
- data/lib/ruby_cms.rb +24 -0
- data/lib/tasks/admin.rake +120 -0
- data/log/test.log +7284 -0
- metadata +10 -4
- data/app/views/ruby_cms/admin/content_blocks/edit.html.erb +0 -17
- data/lib/tasks/ruby_cms.rake +0 -27
data/lib/ruby_cms/engine.rb
CHANGED
|
@@ -98,6 +98,14 @@ module RubyCms
|
|
|
98
98
|
RubyCms::Settings.import_initializer_values!
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# After host initializers (e.g. register_permission_keys), ensure Permission rows exist
|
|
102
|
+
# so can?(:manage_backups) and other keys do not fail on Permission.exists? checks.
|
|
103
|
+
initializer "ruby_cms.ensure_permission_rows", after: :load_config_initializers do
|
|
104
|
+
RubyCms::Permission.ensure_defaults!
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
Rails.logger.warn("[RubyCMS] Permission.ensure_defaults! skipped: #{e.message}")
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
def self.register_main_nav_items
|
|
102
110
|
RubyCms.nav_register(
|
|
103
111
|
key: :dashboard,
|
|
@@ -105,6 +113,7 @@ module RubyCms
|
|
|
105
113
|
path: lambda(&:ruby_cms_admin_root_path),
|
|
106
114
|
icon: dashboard_icon_path,
|
|
107
115
|
section: RubyCms::NAV_SECTION_MAIN,
|
|
116
|
+
permission: :manage_admin,
|
|
108
117
|
order: 1
|
|
109
118
|
)
|
|
110
119
|
RubyCms.nav_register(
|
|
@@ -113,6 +122,7 @@ module RubyCms
|
|
|
113
122
|
path: lambda(&:ruby_cms_admin_visual_editor_path),
|
|
114
123
|
icon: visual_editor_icon_path,
|
|
115
124
|
section: RubyCms::NAV_SECTION_MAIN,
|
|
125
|
+
permission: :manage_content_blocks,
|
|
116
126
|
order: 2
|
|
117
127
|
)
|
|
118
128
|
RubyCms.nav_register(
|
|
@@ -121,6 +131,7 @@ module RubyCms
|
|
|
121
131
|
path: lambda(&:ruby_cms_admin_content_blocks_path),
|
|
122
132
|
icon: content_blocks_icon_path,
|
|
123
133
|
section: RubyCms::NAV_SECTION_MAIN,
|
|
134
|
+
permission: :manage_content_blocks,
|
|
124
135
|
order: 3
|
|
125
136
|
)
|
|
126
137
|
end
|
|
@@ -163,6 +174,7 @@ module RubyCms
|
|
|
163
174
|
path: lambda(&:ruby_cms_admin_permissions_path),
|
|
164
175
|
section: RubyCms::NAV_SECTION_BOTTOM,
|
|
165
176
|
icon: permissions_icon_path,
|
|
177
|
+
permission: :manage_permissions,
|
|
166
178
|
order: 2
|
|
167
179
|
)
|
|
168
180
|
RubyCms.nav_register(
|
|
@@ -171,6 +183,7 @@ module RubyCms
|
|
|
171
183
|
path: lambda(&:ruby_cms_admin_visitor_errors_path),
|
|
172
184
|
section: RubyCms::NAV_SECTION_BOTTOM,
|
|
173
185
|
icon: visitor_errors_icon_path,
|
|
186
|
+
permission: :manage_visitor_errors,
|
|
174
187
|
order: 3
|
|
175
188
|
)
|
|
176
189
|
RubyCms.nav_register(
|
|
@@ -179,6 +192,7 @@ module RubyCms
|
|
|
179
192
|
path: lambda(&:ruby_cms_admin_users_path),
|
|
180
193
|
section: RubyCms::NAV_SECTION_BOTTOM,
|
|
181
194
|
icon: users_icon_path,
|
|
195
|
+
permission: :manage_permissions,
|
|
182
196
|
order: 4
|
|
183
197
|
)
|
|
184
198
|
RubyCms.nav_register(
|
|
@@ -187,6 +201,7 @@ module RubyCms
|
|
|
187
201
|
path: lambda(&:ruby_cms_admin_settings_path),
|
|
188
202
|
section: RubyCms::NAV_SECTION_BOTTOM,
|
|
189
203
|
icon: settings_icon_path,
|
|
204
|
+
permission: :manage_admin,
|
|
190
205
|
order: 5
|
|
191
206
|
)
|
|
192
207
|
end
|
|
@@ -358,13 +373,7 @@ module RubyCms
|
|
|
358
373
|
def self.grant_admin_permissions_to_admin_users
|
|
359
374
|
return unless defined?(::User) && User.column_names.include?("admin")
|
|
360
375
|
|
|
361
|
-
permission_keys =
|
|
362
|
-
manage_admin
|
|
363
|
-
manage_permissions
|
|
364
|
-
manage_content_blocks
|
|
365
|
-
manage_visitor_errors
|
|
366
|
-
manage_analytics
|
|
367
|
-
]
|
|
376
|
+
permission_keys = RubyCms::Permission.all_keys
|
|
368
377
|
permissions = RubyCms::Permission.where(key: permission_keys).index_by(&:key)
|
|
369
378
|
User.where(admin: true).find_each do |u|
|
|
370
379
|
permission_keys.each do |key|
|
|
@@ -416,11 +425,10 @@ module RubyCms
|
|
|
416
425
|
|
|
417
426
|
def self.grant_manage_admin_permission(user, email)
|
|
418
427
|
RubyCms::Permission.ensure_defaults!
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
perm = RubyCms::Permission.find_by!(key:)
|
|
428
|
+
RubyCms::Permission.all_keys.each do |key|
|
|
429
|
+
perm = RubyCms::Permission.find_by(key:)
|
|
430
|
+
next unless perm
|
|
431
|
+
|
|
424
432
|
RubyCms::UserPermission.find_or_create_by!(user: user, permission: perm)
|
|
425
433
|
end
|
|
426
434
|
puts "Granted full admin permissions to #{email}" # rubocop:disable Rails/Output
|
data/lib/ruby_cms/version.rb
CHANGED
data/lib/ruby_cms.rb
CHANGED
|
@@ -15,6 +15,30 @@ module RubyCms
|
|
|
15
15
|
mattr_accessor :nav_registry
|
|
16
16
|
self.nav_registry = []
|
|
17
17
|
|
|
18
|
+
# Permission configuration (available at boot, before models load)
|
|
19
|
+
DEFAULT_PERMISSION_KEYS = %w[
|
|
20
|
+
manage_admin
|
|
21
|
+
manage_permissions
|
|
22
|
+
manage_content_blocks
|
|
23
|
+
manage_visitor_errors
|
|
24
|
+
manage_analytics
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
mattr_accessor :extra_permission_keys, default: []
|
|
28
|
+
mattr_accessor :permission_templates, default: {}
|
|
29
|
+
|
|
30
|
+
def self.register_permission_keys(*keys)
|
|
31
|
+
self.extra_permission_keys = (extra_permission_keys + keys.flatten.map(&:to_s)).uniq
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.register_permission_template(name, label:, keys:, description: nil)
|
|
35
|
+
permission_templates[name.to_sym] = {
|
|
36
|
+
label: label,
|
|
37
|
+
keys: keys.map(&:to_s),
|
|
38
|
+
description: description
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
18
42
|
# Navigation section keys. Order in sidebar: main, then Settings (bottom).
|
|
19
43
|
# User can add items to either via nav_register with section: NAV_SECTION_MAIN (order 10+)
|
|
20
44
|
# or section: NAV_SECTION_BOTTOM (order 10+).
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :admin do
|
|
4
|
+
def ruby_cms_user_class
|
|
5
|
+
Object.const_get(Rails.application.config.ruby_cms.user_class_name.presence ||
|
|
6
|
+
"User")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def ruby_cms_email_attr(user_class)
|
|
10
|
+
user_class.column_names.include?("email_address") ? :email_address : :email
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ruby_cms_find_user_by_email(email)
|
|
14
|
+
user_class = ruby_cms_user_class
|
|
15
|
+
email_attr = ruby_cms_email_attr(user_class)
|
|
16
|
+
user_class.find_by(email_attr => email)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ruby_cms_make_user_full_admin!(user)
|
|
20
|
+
# Prefer host app's make_admin! if it exists.
|
|
21
|
+
if user.respond_to?(:make_admin!) && user.respond_to?(:admin?) && !user.admin?
|
|
22
|
+
user.make_admin!
|
|
23
|
+
elsif user.class.column_names.include?("admin")
|
|
24
|
+
# Fallback if admin? / make_admin! aren't provided by host app.
|
|
25
|
+
user.update!(admin: true) unless user.respond_to?(:admin?) && user.admin?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ruby_cms_grant_all_permissions_to!(user, email:)
|
|
30
|
+
RubyCms::Permission.ensure_defaults!
|
|
31
|
+
RubyCms::UserPermission.where(user:).destroy_all
|
|
32
|
+
RubyCms::Engine.grant_manage_admin_permission(user, email)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "Make a user a full admin with all permissions (no templates). Usage: rails admin:make_admin email=user@example.com"
|
|
36
|
+
task make_admin: :environment do
|
|
37
|
+
email = ENV["email"] || ENV.fetch("EMAIL", nil)
|
|
38
|
+
abort "Usage: rails admin:make_admin email=user@example.com" if email.blank?
|
|
39
|
+
|
|
40
|
+
user = ruby_cms_find_user_by_email(email)
|
|
41
|
+
abort "User not found: #{email}" unless user
|
|
42
|
+
|
|
43
|
+
ruby_cms_make_user_full_admin!(user)
|
|
44
|
+
ruby_cms_grant_all_permissions_to!(user, email:)
|
|
45
|
+
|
|
46
|
+
keys = RubyCms::UserPermission.where(user:)
|
|
47
|
+
.joins(:permission)
|
|
48
|
+
.pluck("permissions.key")
|
|
49
|
+
.sort
|
|
50
|
+
.uniq
|
|
51
|
+
|
|
52
|
+
puts "#{email} is now admin with #{keys.size} permission(s):"
|
|
53
|
+
keys.each {|k| puts " - #{k}" }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc "List all users with their admin status and permission keys"
|
|
57
|
+
task list_users: :environment do
|
|
58
|
+
user_class = ruby_cms_user_class
|
|
59
|
+
email_attr = ruby_cms_email_attr(user_class)
|
|
60
|
+
|
|
61
|
+
users = user_class.order(email_attr)
|
|
62
|
+
|
|
63
|
+
if users.none?
|
|
64
|
+
puts "No users found."
|
|
65
|
+
next
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
users.each do |user|
|
|
69
|
+
keys = RubyCms::UserPermission.where(user:)
|
|
70
|
+
.joins(:permission)
|
|
71
|
+
.pluck("permissions.key")
|
|
72
|
+
.sort
|
|
73
|
+
|
|
74
|
+
admin_flag = user.respond_to?(:admin?) ? user.admin? : false
|
|
75
|
+
|
|
76
|
+
puts "#{user.public_send(email_attr)} admin=#{admin_flag} keys=#{keys.join(', ')}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
desc "Seed all permission keys into the database (no templates)."
|
|
81
|
+
task seed_permissions: :environment do
|
|
82
|
+
RubyCms::Permission.ensure_defaults!
|
|
83
|
+
RubyCms::Settings.ensure_defaults! if defined?(RubyCms::Settings)
|
|
84
|
+
RubyCms::Settings.import_initializer_values! if defined?(RubyCms::Settings)
|
|
85
|
+
|
|
86
|
+
puts "Permissions: #{RubyCms::Permission.pluck(:key).sort.join(', ')}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
desc "Delete admin user. Usage: rails admin:delete email=user@example.com"
|
|
90
|
+
task delete: :environment do
|
|
91
|
+
email = ENV["email"] || ENV.fetch("EMAIL", nil)
|
|
92
|
+
abort "Usage: rails admin:delete email=user@example.com" if email.blank?
|
|
93
|
+
|
|
94
|
+
user = ruby_cms_find_user_by_email(email)
|
|
95
|
+
abort "User not found: #{email}" unless user
|
|
96
|
+
|
|
97
|
+
print "Delete #{email}? (yes/no): "
|
|
98
|
+
abort "Cancelled." unless $stdin.gets.to_s.strip.downcase == "yes"
|
|
99
|
+
|
|
100
|
+
user.destroy!
|
|
101
|
+
puts "Deleted #{email}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
desc "Log out all users (delete all sessions)"
|
|
105
|
+
task logout_all: :environment do
|
|
106
|
+
abort "Session constant not found in host app. Ensure your auth generator created Session." unless defined?(Session)
|
|
107
|
+
|
|
108
|
+
count = Session.count
|
|
109
|
+
if count.zero?
|
|
110
|
+
puts "No active sessions."
|
|
111
|
+
next
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
print "Log out all #{count} session(s)? (yes/no): "
|
|
115
|
+
abort "Cancelled." unless $stdin.gets.to_s.strip.downcase == "yes"
|
|
116
|
+
|
|
117
|
+
Session.destroy_all
|
|
118
|
+
puts "Logged out #{count} session(s)"
|
|
119
|
+
end
|
|
120
|
+
end
|