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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +68 -164
  4. data/app/components/ruby_cms/admin/admin_page.rb +19 -19
  5. data/app/components/ruby_cms/admin/admin_page_header.rb +79 -0
  6. data/app/components/ruby_cms/admin/admin_resource_card.rb +55 -0
  7. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +4 -4
  8. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +5 -5
  9. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +1 -1
  10. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +15 -13
  11. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +13 -11
  12. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +9 -9
  13. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +2 -2
  14. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +8 -8
  15. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +9 -9
  16. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +3 -4
  17. data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +46 -32
  18. data/app/controllers/ruby_cms/admin/base_controller.rb +10 -4
  19. data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +4 -3
  20. data/app/controllers/ruby_cms/admin/locale_controller.rb +2 -1
  21. data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +25 -7
  22. data/app/helpers/ruby_cms/content_blocks_helper.rb +7 -3
  23. data/app/helpers/ruby_cms/settings_helper.rb +32 -20
  24. data/app/javascript/controllers/ruby_cms/bulk_action_table_controller.js +53 -12
  25. data/app/models/ruby_cms/permission.rb +38 -9
  26. data/app/models/ruby_cms/permittable.rb +0 -2
  27. data/app/services/ruby_cms/analytics/report.rb +37 -3
  28. data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +2 -2
  29. data/app/views/layouts/ruby_cms/admin.html.erb +13 -17
  30. data/app/views/ruby_cms/admin/analytics/index.html.erb +103 -108
  31. data/app/views/ruby_cms/admin/analytics/page_details.html.erb +28 -32
  32. data/app/views/ruby_cms/admin/analytics/partials/_back_button.html.erb +3 -2
  33. data/app/views/ruby_cms/admin/analytics/partials/_browser_device.html.erb +34 -20
  34. data/app/views/ruby_cms/admin/analytics/partials/_daily_activity_chart.html.erb +27 -27
  35. data/app/views/ruby_cms/admin/analytics/partials/_hourly_activity_chart.html.erb +19 -19
  36. data/app/views/ruby_cms/admin/analytics/partials/_landing_pages.html.erb +21 -0
  37. data/app/views/ruby_cms/admin/analytics/partials/_os_stats.html.erb +26 -0
  38. data/app/views/ruby_cms/admin/analytics/partials/_popular_pages.html.erb +34 -0
  39. data/app/views/ruby_cms/admin/analytics/partials/_recent_activity.html.erb +16 -14
  40. data/app/views/ruby_cms/admin/analytics/partials/_security_alert.html.erb +3 -3
  41. data/app/views/ruby_cms/admin/analytics/partials/_top_referrers.html.erb +14 -14
  42. data/app/views/ruby_cms/admin/analytics/partials/_top_visitors.html.erb +28 -0
  43. data/app/views/ruby_cms/admin/analytics/partials/_utm_sources.html.erb +21 -0
  44. data/app/views/ruby_cms/admin/analytics/visitor_details.html.erb +44 -48
  45. data/app/views/ruby_cms/admin/content_blocks/index.html.erb +0 -11
  46. data/app/views/ruby_cms/admin/content_blocks/show.html.erb +204 -85
  47. data/app/views/ruby_cms/admin/settings/index.html.erb +214 -175
  48. data/app/views/ruby_cms/admin/user_permissions/index.html.erb +32 -2
  49. data/app/views/ruby_cms/admin/users/_row.html.erb +4 -1
  50. data/config/locales/en.yml +4 -0
  51. data/lib/generators/ruby_cms/install_generator.rb +2 -1
  52. data/lib/ruby_cms/cli.rb +1 -1
  53. data/lib/ruby_cms/engine.rb +20 -12
  54. data/lib/ruby_cms/version.rb +1 -1
  55. data/lib/ruby_cms.rb +24 -0
  56. data/lib/tasks/admin.rake +120 -0
  57. data/log/test.log +7284 -0
  58. metadata +10 -4
  59. data/app/views/ruby_cms/admin/content_blocks/edit.html.erb +0 -17
  60. data/lib/tasks/ruby_cms.rake +0 -27
@@ -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 = %w[
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
- %w[
420
- manage_admin manage_permissions manage_content_blocks manage_visitor_errors
421
- manage_analytics
422
- ].each do |key|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyCms
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
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