panda-cms 0.8.0 → 0.10.0
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/README.md +83 -4
- data/app/components/panda/cms/code_component.rb +117 -39
- data/app/components/panda/cms/grid_component.rb +26 -6
- data/app/components/panda/cms/menu_component.rb +66 -34
- data/app/components/panda/cms/page_menu_component.rb +94 -13
- data/app/components/panda/cms/rich_text_component.rb +198 -140
- data/app/components/panda/cms/text_component.rb +77 -44
- data/app/controllers/panda/cms/admin/base_controller.rb +19 -3
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +3 -3
- data/app/controllers/panda/cms/admin/files_controller.rb +7 -0
- data/app/controllers/panda/cms/admin/menus_controller.rb +47 -3
- data/app/controllers/panda/cms/admin/pages_controller.rb +6 -1
- data/app/controllers/panda/cms/pages_controller.rb +2 -2
- data/app/helpers/panda/cms/application_helper.rb +15 -1
- data/app/helpers/panda/cms/asset_helper.rb +14 -3
- data/app/javascript/panda/cms/application_panda_cms.js +1 -1
- data/app/javascript/panda/cms/controllers/code_editor_controller.js +95 -0
- data/app/javascript/panda/cms/controllers/file_gallery_controller.js +128 -0
- data/app/javascript/panda/cms/controllers/index.js +48 -13
- data/app/javascript/panda/cms/controllers/inline_code_editor_controller.js +96 -0
- data/app/javascript/panda/cms/controllers/menu_form_controller.js +40 -0
- data/app/javascript/panda/cms/controllers/nested_form_controller.js +35 -0
- data/app/javascript/panda/cms/controllers/tree_controller.js +214 -0
- data/app/javascript/panda/cms/stimulus-loading.js +5 -7
- data/app/models/panda/cms/block_content.rb +9 -0
- data/app/models/panda/cms/page.rb +41 -0
- data/app/models/panda/cms/post.rb +1 -0
- data/app/views/panda/cms/admin/dashboard/show.html.erb +5 -5
- data/app/views/panda/cms/admin/files/_file_details.html.erb +45 -0
- data/app/views/panda/cms/admin/files/index.html.erb +11 -118
- data/app/views/panda/cms/admin/forms/index.html.erb +2 -2
- data/app/views/panda/cms/admin/forms/new.html.erb +1 -2
- data/app/views/panda/cms/admin/forms/show.html.erb +15 -30
- data/app/views/panda/cms/admin/menus/_menu_item_fields.html.erb +11 -0
- data/app/views/panda/cms/admin/menus/edit.html.erb +64 -0
- data/app/views/panda/cms/admin/menus/index.html.erb +3 -2
- data/app/views/panda/cms/admin/menus/new.html.erb +40 -0
- data/app/views/panda/cms/admin/pages/edit.html.erb +15 -9
- data/app/views/panda/cms/admin/pages/index.html.erb +49 -11
- data/app/views/panda/cms/admin/pages/new.html.erb +3 -11
- data/app/views/panda/cms/admin/posts/_form.html.erb +4 -14
- data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +3 -3
- data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/index.html.erb +3 -3
- data/config/importmap.rb +4 -6
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb.disabled +31 -0
- data/config/initializers/panda/cms.rb +52 -10
- data/config/routes.rb +4 -2
- data/db/migrate/20240305000000_convert_html_content_to_editor_js.rb +9 -2
- data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +6 -1
- data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +23 -21
- data/db/migrate/20251104150640_add_cached_last_updated_at_to_panda_cms_pages.rb +22 -0
- data/db/migrate/20251104172242_add_page_type_to_panda_cms_pages.rb +6 -0
- data/db/migrate/20251104172638_set_page_types_for_existing_pages.rb +27 -0
- data/db/migrate/20251105000001_add_pending_review_status_to_pages_and_posts.panda_cms.rb +21 -0
- data/lib/generators/panda/cms/install_generator.rb +2 -5
- data/lib/panda/cms/asset_loader.rb +36 -16
- data/lib/panda/cms/debug.rb +29 -0
- data/lib/panda/cms/engine.rb +107 -48
- data/lib/panda/cms/features.rb +52 -0
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +5 -6
- data/lib/tasks/assets.rake +5 -52
- data/lib/tasks/panda_cms_tasks.rake +16 -0
- metadata +22 -29
- data/app/components/panda/cms/admin/container_component.html.erb +0 -13
- data/app/components/panda/cms/admin/flash_message_component.html.erb +0 -31
- data/app/components/panda/cms/admin/panel_component.html.erb +0 -7
- data/app/components/panda/cms/admin/slideover_component.html.erb +0 -9
- data/app/components/panda/cms/admin/slideover_component.rb +0 -15
- data/app/components/panda/cms/admin/statistics_component.html.erb +0 -4
- data/app/components/panda/cms/admin/statistics_component.rb +0 -16
- data/app/components/panda/cms/admin/tab_bar_component.html.erb +0 -35
- data/app/components/panda/cms/admin/tab_bar_component.rb +0 -15
- data/app/components/panda/cms/admin/table_component.html.erb +0 -29
- data/app/components/panda/cms/admin/user_activity_component.html.erb +0 -7
- data/app/components/panda/cms/admin/user_activity_component.rb +0 -20
- data/app/components/panda/cms/admin/user_display_component.html.erb +0 -17
- data/app/components/panda/cms/admin/user_display_component.rb +0 -21
- data/app/components/panda/cms/grid_component.html.erb +0 -6
- data/app/components/panda/cms/menu_component.html.erb +0 -6
- data/app/components/panda/cms/page_menu_component.html.erb +0 -21
- data/app/components/panda/cms/rich_text_component.html.erb +0 -90
- data/app/views/layouts/panda/cms/application.html.erb +0 -42
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +0 -28
- data/app/views/panda/cms/admin/shared/_flash.html.erb +0 -5
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +0 -41
- data/app/views/panda/cms/shared/_footer.html.erb +0 -2
- data/app/views/panda/cms/shared/_header.html.erb +0 -25
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb +0 -13
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class SetPageTypesForExistingPages < ActiveRecord::Migration[8.0]
|
|
2
|
+
def up
|
|
3
|
+
# Set system type for error pages
|
|
4
|
+
execute <<-SQL
|
|
5
|
+
UPDATE panda_cms_pages
|
|
6
|
+
SET page_type = 'system'
|
|
7
|
+
WHERE path IN ('/404', '/500')
|
|
8
|
+
SQL
|
|
9
|
+
|
|
10
|
+
# Set posts type for news/blog pages
|
|
11
|
+
execute <<-SQL
|
|
12
|
+
UPDATE panda_cms_pages
|
|
13
|
+
SET page_type = 'posts'
|
|
14
|
+
WHERE path LIKE '%news%' OR path LIKE '%blog%' OR path LIKE '%updates%'
|
|
15
|
+
SQL
|
|
16
|
+
|
|
17
|
+
# All other pages remain as 'standard' (the default)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def down
|
|
21
|
+
# Reset all to standard
|
|
22
|
+
execute <<-SQL
|
|
23
|
+
UPDATE panda_cms_pages
|
|
24
|
+
SET page_type = 'standard'
|
|
25
|
+
SQL
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddPendingReviewStatusToPagesAndPosts < ActiveRecord::Migration[8.0]
|
|
4
|
+
def up
|
|
5
|
+
# Add pending_review status to pages and posts for content approval workflow
|
|
6
|
+
# This allows content creators to submit drafts for editorial review
|
|
7
|
+
|
|
8
|
+
# Note: We're not modifying the enum directly in the database
|
|
9
|
+
# Rails enums are string-based, so we just document the new allowed value
|
|
10
|
+
# The application code will handle the new status value
|
|
11
|
+
|
|
12
|
+
# Update any existing pages/posts that might benefit from this status
|
|
13
|
+
# (In a real migration, you'd add logic here if needed)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def down
|
|
17
|
+
# Convert any pending_review items back to draft
|
|
18
|
+
Panda::CMS::Page.where(status: "pending_review").update_all(status: "draft")
|
|
19
|
+
Panda::CMS::Post.where(status: "pending_review").update_all(status: "draft")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -10,11 +10,8 @@ module Generators
|
|
|
10
10
|
desc "Adds the basic configuration for Panda CMS to your Rails app."
|
|
11
11
|
|
|
12
12
|
def create_initializer_file
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
unless File.exist?("#{::Rails.root}/#{initializer_path}")
|
|
16
|
-
FileUtils.cp "#{::Panda::CMS::Engine.root}/#{initializer_path}", "#{::Rails.root}/#{initializer_path}"
|
|
17
|
-
end
|
|
13
|
+
# Skip creating initializer - Panda::Core already creates config/initializers/panda.rb
|
|
14
|
+
# See config/initializers/panda/cms.rb in the gem for an example configuration
|
|
18
15
|
|
|
19
16
|
# Add the seed loader to the seeds.rb file
|
|
20
17
|
unless File.read("#{::Rails.root}/db/seeds.rb")&.include?("Panda::CMS::Engine.load_seed")
|
|
@@ -161,19 +161,24 @@ module Panda
|
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def development_javascript_url
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
versioned_path = "/panda-cms-assets/#{version}/panda-cms-#{version}.js"
|
|
169
|
-
|
|
170
|
-
if cached_asset_exists?(root_path)
|
|
171
|
-
root_path
|
|
172
|
-
elsif cached_asset_exists?(versioned_path)
|
|
173
|
-
versioned_path
|
|
164
|
+
# In development, use importmap for live-reloading
|
|
165
|
+
# Only use compiled assets in test/CI environments
|
|
166
|
+
if Rails.env.development?
|
|
167
|
+
"/panda/cms/application_panda_cms.js"
|
|
174
168
|
else
|
|
175
|
-
#
|
|
176
|
-
|
|
169
|
+
# Try cached assets for test environment
|
|
170
|
+
version = asset_version
|
|
171
|
+
root_path = "/panda-cms-assets/panda-cms-#{version}.js"
|
|
172
|
+
versioned_path = "/panda-cms-assets/#{version}/panda-cms-#{version}.js"
|
|
173
|
+
|
|
174
|
+
if cached_asset_exists?(root_path)
|
|
175
|
+
root_path
|
|
176
|
+
elsif cached_asset_exists?(versioned_path)
|
|
177
|
+
versioned_path
|
|
178
|
+
else
|
|
179
|
+
# Fallback to importmap
|
|
180
|
+
"/panda/cms/application_panda_cms.js"
|
|
181
|
+
end
|
|
177
182
|
end
|
|
178
183
|
end
|
|
179
184
|
|
|
@@ -251,7 +256,7 @@ module Panda
|
|
|
251
256
|
|
|
252
257
|
def engine_assets_available?
|
|
253
258
|
# Check if engine's JavaScript files are available
|
|
254
|
-
engine_js_path =
|
|
259
|
+
engine_js_path = Panda::CMS::Engine.root.join("app", "javascript", "panda", "cms", "controllers", "index.js")
|
|
255
260
|
File.exist?(engine_js_path)
|
|
256
261
|
end
|
|
257
262
|
|
|
@@ -312,6 +317,21 @@ module Panda
|
|
|
312
317
|
end
|
|
313
318
|
end
|
|
314
319
|
|
|
320
|
+
# Simple response object for HTTP requests
|
|
321
|
+
class Response
|
|
322
|
+
attr_reader :code, :body
|
|
323
|
+
|
|
324
|
+
def initialize(success:, code:, body:)
|
|
325
|
+
@success = success
|
|
326
|
+
@code = code
|
|
327
|
+
@body = body
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def success?
|
|
331
|
+
@success
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
315
335
|
def fetch_url(url)
|
|
316
336
|
uri = URI(url)
|
|
317
337
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
@@ -324,14 +344,14 @@ module Panda
|
|
|
324
344
|
|
|
325
345
|
response = http.request(request)
|
|
326
346
|
|
|
327
|
-
|
|
328
|
-
success
|
|
347
|
+
Response.new(
|
|
348
|
+
success: response.code.to_i == 200,
|
|
329
349
|
code: response.code,
|
|
330
350
|
body: response.body
|
|
331
351
|
)
|
|
332
352
|
rescue => e
|
|
333
353
|
Rails.logger.error "[Panda CMS] Network error: #{e.message}"
|
|
334
|
-
|
|
354
|
+
Response.new(success: false, code: "error", body: nil)
|
|
335
355
|
end
|
|
336
356
|
|
|
337
357
|
def asset_integrity(version, filename)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module CMS
|
|
5
|
+
module Debug
|
|
6
|
+
class << self
|
|
7
|
+
# Check if debug mode is enabled via PANDA_DEBUG environment variable
|
|
8
|
+
def enabled?
|
|
9
|
+
ENV["PANDA_DEBUG"].to_s.downcase == "true" || ENV["PANDA_DEBUG"] == "1"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Log a debug message if debug mode is enabled
|
|
13
|
+
def log(message)
|
|
14
|
+
Panda::Core::Debug.log(message, prefix: "PANDA CMS")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Log an object with pretty printing (using awesome_print if available)
|
|
18
|
+
def inspect(object, label: nil)
|
|
19
|
+
Panda::Core::Debug.inspect(object, label: label, prefix: "PANDA CMS")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Enable HTTP debugging for Net::HTTP requests (delegates to Core)
|
|
23
|
+
def enable_http_debug!
|
|
24
|
+
Panda::Core::Debug.enable_http_debug!
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/panda/cms/engine.rb
CHANGED
|
@@ -19,14 +19,8 @@ module Panda
|
|
|
19
19
|
#{root}/app/services
|
|
20
20
|
]
|
|
21
21
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
app.config.middleware = app.config.middleware.dup if app.config.middleware.frozen?
|
|
25
|
-
|
|
26
|
-
app.config.session_store :cookie_store, key: "_panda_cms_session"
|
|
27
|
-
app.config.middleware.use ActionDispatch::Cookies
|
|
28
|
-
app.config.middleware.use ActionDispatch::Session::CookieStore, app.config.session_options
|
|
29
|
-
end
|
|
22
|
+
# Session configuration is left to the consuming application
|
|
23
|
+
# The CMS engine does not impose session store requirements
|
|
30
24
|
|
|
31
25
|
config.to_prepare do
|
|
32
26
|
ApplicationController.helper(::ApplicationHelper)
|
|
@@ -43,11 +37,19 @@ module Panda
|
|
|
43
37
|
end
|
|
44
38
|
|
|
45
39
|
# Make files in public available to the main app (e.g. /panda_cms-assets/favicon.ico)
|
|
46
|
-
config.
|
|
47
|
-
Rack::Static,
|
|
40
|
+
config.middleware.use Rack::Static,
|
|
48
41
|
urls: ["/panda-cms-assets"],
|
|
49
42
|
root: Panda::CMS::Engine.root.join("public")
|
|
50
|
-
|
|
43
|
+
|
|
44
|
+
# Make JavaScript files available for importmap
|
|
45
|
+
# Serve from app/javascript with proper MIME types
|
|
46
|
+
config.middleware.use Rack::Static,
|
|
47
|
+
urls: ["/panda/cms"],
|
|
48
|
+
root: Panda::CMS::Engine.root.join("app/javascript"),
|
|
49
|
+
header_rules: [
|
|
50
|
+
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000",
|
|
51
|
+
"Content-Type" => "text/javascript; charset=utf-8"}]
|
|
52
|
+
]
|
|
51
53
|
|
|
52
54
|
# Custom error handling
|
|
53
55
|
# config.exceptions_app = Panda::CMS::ExceptionsApp.new(exceptions_app: routes)
|
|
@@ -86,13 +88,22 @@ module Panda
|
|
|
86
88
|
end
|
|
87
89
|
end
|
|
88
90
|
|
|
91
|
+
# Auto-mount CMS routes
|
|
89
92
|
config.after_initialize do |app|
|
|
90
93
|
# Append routes to the routes file
|
|
91
94
|
app.routes.append do
|
|
92
95
|
mount Panda::CMS::Engine => "/", :as => "panda_cms"
|
|
93
96
|
post "/_forms/:id", to: "panda/cms/form_submissions#create", as: :panda_cms_form_submit
|
|
94
97
|
get "/_maintenance", to: "panda/cms/errors#error_503", as: :panda_cms_maintenance
|
|
95
|
-
|
|
98
|
+
|
|
99
|
+
# Catch-all route for CMS pages, but exclude admin paths and assets
|
|
100
|
+
admin_path = Panda::Core.config.admin_path.delete_prefix("/")
|
|
101
|
+
constraints = ->(request) {
|
|
102
|
+
!request.path.start_with?("/#{admin_path}") &&
|
|
103
|
+
!request.path.start_with?("/panda-cms-assets/")
|
|
104
|
+
}
|
|
105
|
+
get "/*path", to: "panda/cms/pages#show", as: :panda_cms_page, constraints: constraints
|
|
106
|
+
|
|
96
107
|
root to: "panda/cms/pages#root"
|
|
97
108
|
end
|
|
98
109
|
end
|
|
@@ -131,63 +142,111 @@ module Panda
|
|
|
131
142
|
app.config.view_component.preview_paths << root.join("spec/components/previews")
|
|
132
143
|
app.config.view_component.generate.sidecar = true
|
|
133
144
|
app.config.view_component.generate.preview = true
|
|
145
|
+
|
|
146
|
+
# Add preview directories to autoload paths in development
|
|
147
|
+
if Rails.env.development?
|
|
148
|
+
# Handle frozen autoload_paths array
|
|
149
|
+
if app.config.autoload_paths.frozen?
|
|
150
|
+
app.config.autoload_paths = app.config.autoload_paths.dup
|
|
151
|
+
end
|
|
152
|
+
app.config.autoload_paths << root.join("spec/components/previews")
|
|
153
|
+
end
|
|
134
154
|
end
|
|
135
155
|
|
|
136
156
|
# Authentication is now handled by Panda::Core::Engine
|
|
137
157
|
|
|
138
|
-
# Configure Core for CMS
|
|
139
|
-
initializer "panda.cms.configure_core" do |app|
|
|
158
|
+
# Configure Core for CMS (runs before app initializers so apps can override)
|
|
159
|
+
initializer "panda.cms.configure_core", before: :load_config_initializers do |app|
|
|
140
160
|
Panda::Core.configure do |config|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
# Core now provides the admin interface foundation
|
|
162
|
+
# Apps using CMS can customize login_logo_path, login_page_title, etc. in their own initializers
|
|
163
|
+
|
|
164
|
+
# Register CMS navigation items
|
|
165
|
+
config.admin_navigation_items = ->(user) {
|
|
166
|
+
items = []
|
|
167
|
+
|
|
168
|
+
# Dashboard
|
|
169
|
+
items << {
|
|
170
|
+
path: "#{config.admin_path}/cms",
|
|
171
|
+
label: "Dashboard",
|
|
172
|
+
icon: "fa-solid fa-house"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Pages
|
|
176
|
+
items << {
|
|
177
|
+
path: "#{config.admin_path}/cms/pages",
|
|
178
|
+
label: "Pages",
|
|
179
|
+
icon: "fa-solid fa-file"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Collections (if enabled)
|
|
183
|
+
if Panda::CMS::Features.enabled?(:collections)
|
|
184
|
+
items << {
|
|
185
|
+
path: "#{config.admin_path}/cms/collections",
|
|
186
|
+
label: "Collections",
|
|
187
|
+
icon: "fa-solid fa-table-cells"
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Posts
|
|
192
|
+
items << {
|
|
193
|
+
path: "#{config.admin_path}/cms/posts",
|
|
194
|
+
label: "Posts",
|
|
195
|
+
icon: "fa-solid fa-newspaper"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# Forms
|
|
199
|
+
items << {
|
|
200
|
+
path: "#{config.admin_path}/cms/forms",
|
|
201
|
+
label: "Forms",
|
|
202
|
+
icon: "fa-solid fa-inbox"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Files
|
|
206
|
+
items << {
|
|
207
|
+
path: "#{config.admin_path}/cms/files",
|
|
208
|
+
label: "Files",
|
|
209
|
+
icon: "fa-solid fa-image"
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Menus
|
|
213
|
+
items << {
|
|
214
|
+
path: "#{config.admin_path}/cms/menus",
|
|
215
|
+
label: "Menus",
|
|
216
|
+
icon: "fa-solid fa-bars"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Settings
|
|
220
|
+
items << {
|
|
221
|
+
path: "#{config.admin_path}/cms/settings",
|
|
222
|
+
label: "Settings",
|
|
223
|
+
icon: "fa-solid fa-gear"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
items
|
|
227
|
+
}
|
|
144
228
|
|
|
145
|
-
#
|
|
146
|
-
|
|
229
|
+
# Redirect to CMS dashboard after login
|
|
230
|
+
# Apps can override this if they want different behavior
|
|
231
|
+
config.dashboard_redirect_path = -> { "#{Panda::Core.config.admin_path}/cms" }
|
|
147
232
|
|
|
148
233
|
# Customize initial breadcrumb
|
|
149
234
|
config.initial_admin_breadcrumb = ->(controller) {
|
|
150
235
|
# Use CMS dashboard path - just use the string path
|
|
151
|
-
["Admin", "#{
|
|
236
|
+
["Admin", "#{config.admin_path}/cms"]
|
|
152
237
|
}
|
|
153
238
|
|
|
154
239
|
# Dashboard widgets
|
|
155
240
|
config.admin_dashboard_widgets = ->(user) {
|
|
156
241
|
widgets = []
|
|
157
242
|
|
|
158
|
-
# Add CMS statistics widgets
|
|
159
|
-
|
|
160
|
-
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
|
161
|
-
metric: "Views Today",
|
|
162
|
-
value: Panda::CMS::Visit.group_by_day(:visited_at, last: 1).count.values.first || 0
|
|
163
|
-
)
|
|
164
|
-
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
|
165
|
-
metric: "Views Last Week",
|
|
166
|
-
value: Panda::CMS::Visit.group_by_week(:visited_at, last: 1).count.values.first || 0
|
|
167
|
-
)
|
|
168
|
-
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
|
169
|
-
metric: "Views Last Month",
|
|
170
|
-
value: Panda::CMS::Visit.group_by_month(:visited_at, last: 1).count.values.first || 0
|
|
171
|
-
)
|
|
172
|
-
end
|
|
243
|
+
# TODO: Add CMS statistics widgets when StatisticsComponent is implemented
|
|
244
|
+
# This was removed along with Pro code migration
|
|
173
245
|
|
|
174
246
|
widgets
|
|
175
247
|
}
|
|
176
248
|
end
|
|
177
249
|
end
|
|
178
|
-
|
|
179
|
-
config.before_initialize do |_app|
|
|
180
|
-
# Default configuration
|
|
181
|
-
Panda::CMS.configure do |config|
|
|
182
|
-
# Array of additional EditorJS tools to load
|
|
183
|
-
# Example: [{ url: "https://cdn.jsdelivr.net/npm/@editorjs/image@latest" }]
|
|
184
|
-
config.editor_js_tools ||= []
|
|
185
|
-
|
|
186
|
-
# Hash of EditorJS tool configurations
|
|
187
|
-
# Example: { image: { class: 'ImageTool', config: { ... } } }
|
|
188
|
-
config.editor_js_tool_config ||= {}
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
250
|
end
|
|
192
251
|
|
|
193
252
|
class MissingBlockError < StandardError; end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module CMS
|
|
5
|
+
# Lightweight feature flag registry so open-source core can gate functionality
|
|
6
|
+
# that is only available when panda-cms-pro is installed.
|
|
7
|
+
module Features
|
|
8
|
+
Feature = Struct.new(:name, :provider, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
class MissingFeatureError < StandardError; end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def register(name, provider:)
|
|
14
|
+
registry[name.to_sym] = Feature.new(name: name.to_sym, provider: provider)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def unregister(name)
|
|
18
|
+
registry.delete(name.to_sym)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset!
|
|
22
|
+
registry.clear
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def enabled?(name)
|
|
26
|
+
registry.key?(name.to_sym)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def require!(name)
|
|
30
|
+
return if enabled?(name)
|
|
31
|
+
|
|
32
|
+
raise MissingFeatureError,
|
|
33
|
+
"The #{name} feature is only available with panda-cms-pro."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def provider_for(name)
|
|
37
|
+
registry[name.to_sym]&.provider
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def enabled_features
|
|
41
|
+
registry.keys
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def registry
|
|
47
|
+
@registry ||= {}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/panda-cms/version.rb
CHANGED
data/lib/panda-cms.rb
CHANGED
|
@@ -9,8 +9,7 @@ module Panda
|
|
|
9
9
|
module CMS
|
|
10
10
|
class Configuration
|
|
11
11
|
attr_accessor :title, :require_login_to_view, :authentication,
|
|
12
|
-
:posts, :url, :
|
|
13
|
-
:editor_js_tool_config, :instagram, :analytics
|
|
12
|
+
:posts, :url, :instagram, :analytics
|
|
14
13
|
|
|
15
14
|
def initialize
|
|
16
15
|
@title = "Demo Site"
|
|
@@ -18,8 +17,6 @@ module Panda
|
|
|
18
17
|
@authentication = {}
|
|
19
18
|
@posts = {enabled: true, prefix: "blog"}
|
|
20
19
|
@url = nil
|
|
21
|
-
@editor_js_tools = []
|
|
22
|
-
@editor_js_tool_config = {}
|
|
23
20
|
@instagram = {
|
|
24
21
|
enabled: false,
|
|
25
22
|
username: nil,
|
|
@@ -56,7 +53,7 @@ module Panda
|
|
|
56
53
|
|
|
57
54
|
def self.root_path
|
|
58
55
|
# Delegate to Panda::Core's admin_path configuration
|
|
59
|
-
Panda::Core.
|
|
56
|
+
Panda::Core.config.admin_path
|
|
60
57
|
end
|
|
61
58
|
|
|
62
59
|
class << self
|
|
@@ -64,7 +61,7 @@ module Panda
|
|
|
64
61
|
|
|
65
62
|
def route_namespace
|
|
66
63
|
# Delegate to Panda::Core's admin_path configuration
|
|
67
|
-
Panda::Core.
|
|
64
|
+
Panda::Core.config.admin_path
|
|
68
65
|
end
|
|
69
66
|
end
|
|
70
67
|
end
|
|
@@ -91,10 +88,12 @@ Panda::CMS.loader.inflector.inflect(
|
|
|
91
88
|
|
|
92
89
|
# Manually require files from panda-cms directory
|
|
93
90
|
require_relative "panda-cms/version"
|
|
91
|
+
require_relative "panda/cms/debug"
|
|
94
92
|
require_relative "panda/cms/exceptions_app"
|
|
95
93
|
require_relative "panda/cms/engine"
|
|
96
94
|
require_relative "panda/cms/demo_site_generator"
|
|
97
95
|
require_relative "panda/cms/asset_loader"
|
|
96
|
+
require_relative "panda/cms/features"
|
|
98
97
|
require_relative "panda/cms/slug"
|
|
99
98
|
|
|
100
99
|
Panda::CMS.loader.setup
|
data/lib/tasks/assets.rake
CHANGED
|
@@ -23,13 +23,8 @@ namespace :panda do
|
|
|
23
23
|
File.write(js_file, js_bundle)
|
|
24
24
|
puts "✅ JavaScript compiled: #{js_file} (#{File.size(js_file)} bytes)"
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
if css_bundle && !css_bundle.strip.empty?
|
|
29
|
-
css_file = output_dir.join("panda-cms-#{version}.css")
|
|
30
|
-
File.write(css_file, css_bundle)
|
|
31
|
-
puts "✅ CSS compiled: #{css_file} (#{File.size(css_file)} bytes)"
|
|
32
|
-
end
|
|
26
|
+
# CSS is now provided by Panda Core
|
|
27
|
+
puts "ℹ️ CSS is provided by Panda Core at /panda-core-assets/panda-core.css"
|
|
33
28
|
|
|
34
29
|
# Create manifest file
|
|
35
30
|
manifest = create_asset_manifest(version)
|
|
@@ -43,7 +38,6 @@ namespace :panda do
|
|
|
43
38
|
FileUtils.mkdir_p(test_asset_dir)
|
|
44
39
|
|
|
45
40
|
js_file_name = "panda-cms-#{version}.js"
|
|
46
|
-
css_file_name = "panda-cms-#{version}.css"
|
|
47
41
|
|
|
48
42
|
# Copy JavaScript file
|
|
49
43
|
if File.exist?(output_dir.join(js_file_name))
|
|
@@ -51,12 +45,6 @@ namespace :panda do
|
|
|
51
45
|
puts "✅ Copied JavaScript to test location: #{test_asset_dir.join(js_file_name)}"
|
|
52
46
|
end
|
|
53
47
|
|
|
54
|
-
# Copy CSS file
|
|
55
|
-
if File.exist?(output_dir.join(css_file_name))
|
|
56
|
-
FileUtils.cp(output_dir.join(css_file_name), test_asset_dir.join(css_file_name))
|
|
57
|
-
puts "✅ Copied CSS to test location: #{test_asset_dir.join(css_file_name)}"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
48
|
# Copy manifest
|
|
61
49
|
if File.exist?(output_dir.join("manifest.json"))
|
|
62
50
|
FileUtils.cp(output_dir.join("manifest.json"), test_asset_dir.join("manifest.json"))
|
|
@@ -186,44 +174,9 @@ def compile_javascript_bundle(version)
|
|
|
186
174
|
bundle.join("\n")
|
|
187
175
|
end
|
|
188
176
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
# Create a minimal CSS bundle with basic styles
|
|
193
|
-
bundle = []
|
|
194
|
-
bundle << "/* Panda CMS CSS Bundle v#{version} */"
|
|
195
|
-
bundle << "/* Compiled: #{Time.now.utc.iso8601} */"
|
|
196
|
-
bundle << "/* This is a simplified bundle for CI testing purposes */"
|
|
197
|
-
bundle << ""
|
|
198
|
-
|
|
199
|
-
# Add some basic styles that might be expected
|
|
200
|
-
bundle << "/* Basic Panda CMS Styles */"
|
|
201
|
-
bundle << ".panda-cms-admin {"
|
|
202
|
-
bundle << " font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"
|
|
203
|
-
bundle << " line-height: 1.5;"
|
|
204
|
-
bundle << "}"
|
|
205
|
-
bundle << ""
|
|
206
|
-
bundle << ".panda-cms-editor {"
|
|
207
|
-
bundle << " min-height: 200px;"
|
|
208
|
-
bundle << " border: 1px solid #e5e7eb;"
|
|
209
|
-
bundle << " border-radius: 0.375rem;"
|
|
210
|
-
bundle << " padding: 1rem;"
|
|
211
|
-
bundle << "}"
|
|
212
|
-
bundle << ""
|
|
213
|
-
bundle << ".panda-cms-hidden {"
|
|
214
|
-
bundle << " display: none !important;"
|
|
215
|
-
bundle << "}"
|
|
216
|
-
bundle << ""
|
|
217
|
-
bundle << "/* Editor ready state */"
|
|
218
|
-
bundle << ".editor-ready {"
|
|
219
|
-
bundle << " opacity: 1;"
|
|
220
|
-
bundle << " transition: opacity 0.3s ease-in-out;"
|
|
221
|
-
bundle << "}"
|
|
222
|
-
bundle << ""
|
|
223
|
-
|
|
224
|
-
puts "✅ Created simplified CSS bundle (#{bundle.join("\n").length} chars)"
|
|
225
|
-
bundle.join("\n")
|
|
226
|
-
end
|
|
177
|
+
# CSS compilation removed - all CSS is now provided by Panda Core
|
|
178
|
+
# The panda-core.css file includes all admin interface styling including
|
|
179
|
+
# EditorJS styles, theme variables, and component styles
|
|
227
180
|
|
|
228
181
|
def create_asset_manifest(version)
|
|
229
182
|
output_dir = Rails.root.join("tmp", "panda_cms_assets")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Provide consistent namespacing for Panda tasks
|
|
4
|
+
# Rails auto-generates panda_cms:* tasks from the module name,
|
|
5
|
+
# but we want to use panda:cms:* for consistency across all Panda gems
|
|
6
|
+
|
|
7
|
+
namespace :panda do
|
|
8
|
+
namespace :cms do
|
|
9
|
+
namespace :install do
|
|
10
|
+
desc "Copy migrations from panda-cms to application"
|
|
11
|
+
task :migrations do
|
|
12
|
+
Rake::Task["panda_cms:install:migrations"].invoke
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|