Module: RubyCms

Extended by:
CommandsRegistry
Defined in:
lib/ruby_cms/dashboard_blocks.rb,
lib/ruby_cms.rb,
lib/ruby_cms/cli.rb,
lib/ruby_cms/icons.rb,
lib/ruby_cms/engine.rb,
lib/ruby_cms/version.rb,
lib/ruby_cms/settings.rb,
lib/ruby_cms/engine/css.rb,
lib/ruby_cms/css_compiler.rb,
lib/ruby_cms/app_integration.rb,
app/models/ruby_cms/permission.rb,
app/models/ruby_cms/preference.rb,
lib/ruby_cms/commands_registry.rb,
lib/ruby_cms/settings_registry.rb,
app/models/ruby_cms/permittable.rb,
lib/ruby_cms/content_blocks_sync.rb,
app/models/ruby_cms/content_block.rb,
app/models/ruby_cms/visitor_error.rb,
app/models/ruby_cms/user_permission.rb,
app/helpers/ruby_cms/settings_helper.rb,
app/services/ruby_cms/command_runner.rb,
lib/ruby_cms/content_blocks_grouping.rb,
lib/ruby_cms/engine/admin_permissions.rb,
app/services/ruby_cms/analytics/report.rb,
app/services/ruby_cms/security_tracker.rb,
app/helpers/ruby_cms/application_helper.rb,
app/components/ruby_cms/admin/admin_page.rb,
lib/ruby_cms/engine/content_blocks_tasks.rb,
lib/generators/ruby_cms/install_generator.rb,
app/controllers/ruby_cms/errors_controller.rb,
app/helpers/ruby_cms/content_blocks_helper.rb,
lib/ruby_cms/engine/dashboard_registration.rb,
app/helpers/ruby_cms/admin/dashboard_helper.rb,
lib/ruby_cms/engine/navigation_registration.rb,
app/components/ruby_cms/admin/base_component.rb,
app/helpers/ruby_cms/admin/admin_page_helper.rb,
lib/generators/ruby_cms/admin_page_generator.rb,
app/helpers/ruby_cms/bulk_action_table_helper.rb,
app/controllers/ruby_cms/admin/base_controller.rb,
app/components/ruby_cms/admin/admin_page_header.rb,
app/controllers/concerns/ruby_cms/page_tracking.rb,
app/controllers/ruby_cms/admin/users_controller.rb,
app/controllers/ruby_cms/admin/locale_controller.rb,
app/components/ruby_cms/admin/admin_resource_card.rb,
app/controllers/concerns/ruby_cms/admin_pagination.rb,
app/controllers/ruby_cms/admin/commands_controller.rb,
app/controllers/ruby_cms/admin/settings_controller.rb,
app/controllers/concerns/ruby_cms/admin_turbo_table.rb,
app/controllers/ruby_cms/admin/analytics_controller.rb,
app/controllers/ruby_cms/admin/dashboard_controller.rb,
app/helpers/ruby_cms/admin/bulk_action_table_helper.rb,
app/controllers/ruby_cms/admin/permissions_controller.rb,
app/controllers/concerns/ruby_cms/visitor_error_capture.rb,
app/controllers/ruby_cms/admin/visual_editor_controller.rb,
app/controllers/ruby_cms/admin/content_blocks_controller.rb,
app/controllers/ruby_cms/admin/visitor_errors_controller.rb,
app/controllers/ruby_cms/admin/user_permissions_controller.rb,
app/components/ruby_cms/admin/admin_page/admin_table_content.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb,
app/controllers/ruby_cms/admin/content_block_versions_controller.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb,
app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb

Overview

Loaded from lib/ruby_cms.rb and from lib/ruby_cms/engine.rb so dashboard API exists even when the host only requires “ruby_cms/engine” (without loading lib/ruby_cms.rb first).

Defined Under Namespace

Modules: Admin, AdminPagination, AdminTurboTable, Analytics, AppIntegration, ApplicationHelper, BulkActionTableHelper, CommandsRegistry, ContentBlocksGrouping, ContentBlocksHelper, CssCompiler, EngineAdminPermissions, EngineContentBlocksTasks, EngineCss, EngineDashboardRegistration, EngineNavigationRegistration, Generators, Icons, Nav, PageTracking, Permittable, Settings, SettingsHelper, SettingsRegistry, VisitorErrorCapture Classes: CLI, CommandRunner, ContentBlock, ContentBlocksSync, Engine, Error, ErrorsController, Permission, Preference, RunSetupAdmin, SecurityTracker, UserPermission, VisitorError

Constant Summary collapse

DEFAULT_PERMISSION_KEYS =

Permission configuration (available at boot, before models load)

%w[
  manage_admin
  manage_permissions
  manage_content_blocks
  manage_visitor_errors
  manage_analytics
].freeze
"main"
"Settings"
VALID_PAGE_SECTIONS =
%i[main settings].freeze
VERSION =
"0.2.1.1"

Class Method Summary collapse

Methods included from CommandsRegistry

find_command, register_command, registered_commands, registered_commands=

Class Method Details

.app_routesObject

Get registered routes



68
69
70
# File 'lib/ruby_cms/app_integration.rb', line 68

def app_routes
  AppIntegration::LinkApp.registered_routes
end

.configure {|Rails.application.config.ruby_cms| ... } ⇒ Object

Yields:

  • (Rails.application.config.ruby_cms)


51
52
53
# File 'lib/ruby_cms.rb', line 51

def self.configure
  yield(Rails.application.config.ruby_cms)
end

.dashboard_register(key:, label:, section:, order:, partial: nil, render: nil, permission: nil, enabled: true, default_visible: true, span: :single, data: nil) ⇒ Object

Register a dashboard block (stats row or main row). Host apps can add blocks or replace defaults by key.

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ruby_cms/dashboard_blocks.rb', line 10

def self.dashboard_register(
  key:, label:, section:, order:, partial: nil, render: nil, permission: nil,
  enabled: true, default_visible: true, span: :single, data: nil
)
  normalized_key = key.to_sym
  normalized_section = section.to_sym
  raise ArgumentError, "section must be :stats or :main" unless %i[stats main].include?(normalized_section)

  raise ArgumentError, "partial or render is required" if partial.blank? && !render.respond_to?(:call)

  entry = {
    key: normalized_key,
    label: label.to_s,
    section: normalized_section,
    order: order.to_i,
    partial: partial,
    render: render,
    permission: permission&.to_sym,
    enabled: enabled ? true : false,
    default_visible: default_visible ? true : false,
    span: span.to_sym == :double ? :double : :single,
    data: data
  }

  self.dashboard_registry = dashboard_registry.reject {|e| e[:key] == normalized_key }
  self.dashboard_registry += [entry]

  register_dashboard_setting!(entry)
  entry
end

.load_app_settings(view_context) ⇒ Object

Load app settings for view context



78
79
80
# File 'lib/ruby_cms/app_integration.rb', line 78

def load_app_settings(view_context)
  AppIntegration::AppSettings.load_settings(view_context)
end

Register a navigation group (accordion) in the sidebar.

A group can optionally have its own page (‘path:`) and can contain child pages (registered via `register_page` / `nav_register`) referenced by their keys.

Notes:

  • ‘children:` is an array of nav keys (Symbol/String) that will be rendered under the group.

  • Children are filtered by the same visibility rules as regular nav links.

  • A group is hidden if it has no visible children and no ‘path`.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ruby_cms.rb', line 107

def self.nav_group(
  key:, label:, children:,
  path: nil, icon: nil, section: nil, order: nil,
  permission: nil, default_visible: true, default_open: true, **options
)
  normalized_key = key.to_sym
  normalized_section = section.presence || NAV_SECTION_MAIN
  resolved_path = path.nil? ? nil : (path.kind_of?(Symbol) ? ->(v) { v.main_app.send(path) } : path)
  resolved_icon = icon.nil? ? nil : RubyCms::Icons.resolve(icon)
  normalized_children = Array(children).map(&:to_s).map(&:strip).reject(&:blank?).map(&:to_sym)

  entry = {
    type: :group,
    key: normalized_key,
    label: label.to_s,
    path: resolved_path,
    icon: resolved_icon,
    section: normalized_section,
    order: order,
    permission: permission&.to_s,
    default_visible: default_visible ? true : false,
    default_open: default_open ? true : false,
    children: normalized_children,
    if: options[:if]
  }

  self.nav_registry = nav_registry.reject {|e| e[:key] == normalized_key }
  self.nav_registry += [entry]

  register_navigation_setting!(entry)

  entry
end

Register a link for admin navigation. Sections: main (top), Settings (bottom). Add to main with section: “main”, order: 10+; add to bottom with section: “Settings”, order: 10+. Order can be changed via Settings → Navigation drag-and-drop. Options:

  • key: required Symbol/String

  • label: required String

  • path: String, Symbol (route helper name, auto-wrapped via main_app), or callable(view_context) -> path

  • icon: Symbol (named icon from RubyCms::Icons) or raw SVG path fragment string

  • section: “main” | “Settings” (or nil => main)

  • order: optional Integer for sorting within section

  • permission: optional permission key (e.g., :manage_analytics)

  • default_visible: optional Boolean (default true)

  • if: optional callable for custom visibility gate



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ruby_cms.rb', line 72

def self.nav_register(key:, label:, path:, icon: nil, section: nil, order: nil, permission: nil, default_visible: true, **options)
  normalized_key = key.to_sym
  normalized_section = section.presence || NAV_SECTION_MAIN
  resolved_path = path.kind_of?(Symbol) ? ->(v) { v.main_app.send(path) } : path
  resolved_icon = icon.nil? ? nil : RubyCms::Icons.resolve(icon)
  entry = {
    type: :link,
    key: normalized_key,
    label: label.to_s,
    path: resolved_path,
    icon: resolved_icon,
    section: normalized_section,
    order: order,
    permission: permission&.to_s,
    default_visible: default_visible ? true : false,
    if: options[:if]
  }

  self.nav_registry = nav_registry.reject {|e| e[:key] == normalized_key }
  self.nav_registry += [entry]

  register_navigation_setting!(entry)

  entry
end

.register_app_routeObject

Register an app route



63
64
65
# File 'lib/ruby_cms/app_integration.rb', line 63

def register_app_route(**)
  AppIntegration::LinkApp.register_route(**)
end

.register_app_settingObject

Register an app setting



73
74
75
# File 'lib/ruby_cms/app_integration.rb', line 73

def register_app_setting(**)
  AppIntegration::AppSettings.register_setting(**)
end

.register_page(key:, label:, path:, icon: nil, section: :main, order: nil, permission: nil, default_visible: true) ⇒ Object

Unified API to register an admin page: nav item + permission key in one call. Accepts :main or :settings for section (resolved to NAV_SECTION_MAIN / NAV_SECTION_BOTTOM). Automatically registers the permission key if provided.

Raises:

  • (ArgumentError)


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/ruby_cms.rb', line 146

def self.register_page(
  key:, label:, path:, icon: nil, section: :main, order: nil,
  permission: nil, default_visible: true, **
)
  raise ArgumentError, "register_page section must be :main or :settings, got #{section.inspect}" unless VALID_PAGE_SECTIONS.include?(section.to_s.to_sym)

  register_permission_keys(permission) if permission.present?
  resolved_section = section.to_s == "settings" ? NAV_SECTION_BOTTOM : NAV_SECTION_MAIN
  nav_register(
    key: key,
    label: label,
    path: path,
    icon: icon,
    section: resolved_section,
    order: order,
    permission: permission,
    default_visible: default_visible,
    **
  )
end

.register_permission_keys(*keys) ⇒ Object



33
34
35
# File 'lib/ruby_cms.rb', line 33

def self.register_permission_keys(*keys)
  self.extra_permission_keys = (extra_permission_keys + keys.flatten.map(&:to_s)).uniq
end

.register_permission_template(name, label:, keys:, description: nil) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/ruby_cms.rb', line 37

def self.register_permission_template(name, label:, keys:, description: nil)
  permission_templates[name.to_sym] = {
    label: label,
    keys: keys.map(&:to_s),
    description: description
  }
end

.setting(key, default: nil) ⇒ Object



55
56
57
# File 'lib/ruby_cms.rb', line 55

def self.setting(key, default: nil)
  RubyCms::Settings.get(key, default:)
end

.visible_dashboard_blocks(user: nil) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/ruby_cms/dashboard_blocks.rb', line 41

def self.visible_dashboard_blocks(user: nil)
  dashboard_registry
    .select {|e| e[:enabled] }
    .select {|e| dashboard_block_visible?(e, user:) }
    .sort_by {|e| [e[:section] == :stats ? 0 : 1, e[:order], e[:label]] }
rescue StandardError => e
  Rails.logger.error("[RubyCMS] Error filtering dashboard blocks: #{e.message}") if defined?(Rails.logger)
  []
end

.visible_nav_registry(view_context: nil, user: nil) ⇒ Object

Returns only entries that pass settings + permissions + conditional checks, sorted by saved nav_order (Settings → Navigation drag-and-drop) when set, else by section + order. Fail-closed: returns empty array on error (never exposes unfiltered items).



170
171
172
173
174
175
176
177
178
# File 'lib/ruby_cms.rb', line 170

def self.visible_nav_registry(view_context: nil, user: nil)
  list = nav_registry
         .select {|item| nav_entry_visible?(item, view_context:, user:) }
         .sort_by {|item| nav_sort_tuple(item) }
  localize_nav_labels(apply_nav_order(list))
rescue StandardError => e
  Rails.logger.error("[RubyCMS] Error filtering navigation: #{e.message}") if defined?(Rails.logger)
  []
end

.visible_nav_sidebar_rows(section:, view_context: nil, user: nil) ⇒ Object

Sidebar rows for a given section. Includes groups (accordions) and top-level links. Returns an array of:

  • { type: :link, entry: <nav entry hash> }

  • { type: :group, group: <group entry hash>, children: [<nav entry hash>…] }



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/ruby_cms.rb', line 184

def self.visible_nav_sidebar_rows(section:, view_context: nil, user: nil)
  visible = visible_nav_registry(view_context:, user:)
  links = visible.select {|e| e[:type].to_s == "link" }
  groups = visible.select {|e| e[:type].to_s == "group" }

  links_by_key = links.index_by {|e| e[:key].to_sym }
  nested_keys = groups.flat_map {|g| Array(g[:children]) }.map(&:to_sym).to_set

  section_key = section.to_s
  section_name = (section_key == "settings" ? NAV_SECTION_BOTTOM : NAV_SECTION_MAIN)

  # groups first; children order uses their own order/label.
  group_rows = groups
    .select {|g| g[:section].to_s == section_name }
    .sort_by {|g| nav_sort_tuple(g) }
    .filter_map do |g|
      child_entries = Array(g[:children]).map {|k| links_by_key[k.to_sym] }.compact
      child_entries = child_entries.sort_by {|c| [c[:order] || 1000, c[:label].to_s] }
      next if child_entries.empty? && g[:path].blank?
      { type: :group, group: g, children: child_entries }
    end

  link_rows = links
    .select {|e| e[:section].to_s == section_name }
    .reject {|e| nested_keys.include?(e[:key].to_sym) }
    .sort_by {|e| nav_sort_tuple(e) }
    .map {|e| { type: :link, entry: e } }

  merged = (group_rows.map {|r| [r, nav_sort_tuple(r[:group])] } +
            link_rows.map {|r| [r, nav_sort_tuple(r[:entry])] })
           .sort_by {|(_, tuple)| tuple }
           .map(&:first)
  merged
end