panda-cms 0.10.0 → 0.10.2
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 +2 -11
- data/app/components/panda/cms/code_component.rb +45 -8
- data/app/components/panda/cms/menu_component.rb +9 -3
- data/app/components/panda/cms/page_menu_component.rb +9 -1
- data/app/components/panda/cms/rich_text_component.rb +49 -17
- data/app/components/panda/cms/text_component.rb +46 -14
- data/app/controllers/panda/cms/admin/menus_controller.rb +2 -2
- data/app/controllers/panda/cms/admin/pages_controller.rb +6 -2
- data/app/controllers/panda/cms/admin/posts_controller.rb +3 -1
- data/app/controllers/panda/cms/form_submissions_controller.rb +134 -11
- data/app/controllers/panda/cms/pages_controller.rb +7 -2
- data/app/controllers/panda/cms/posts_controller.rb +16 -0
- data/app/helpers/panda/cms/application_helper.rb +2 -3
- data/app/helpers/panda/cms/asset_helper.rb +14 -72
- data/app/helpers/panda/cms/forms_helper.rb +60 -0
- data/app/helpers/panda/cms/seo_helper.rb +85 -0
- data/app/javascript/panda/cms/{application_panda_cms.js → application.js} +4 -0
- data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +31 -4
- data/app/javascript/panda/cms/controllers/file_upload_controller.js +165 -0
- data/app/javascript/panda/cms/controllers/index.js +6 -0
- data/app/javascript/panda/cms/controllers/menu_form_controller.js +14 -1
- data/app/javascript/panda/cms/controllers/page_form_controller.js +454 -0
- data/app/javascript/panda/cms/stimulus-loading.js +2 -1
- data/app/models/panda/cms/menu.rb +12 -0
- data/app/models/panda/cms/page.rb +106 -0
- data/app/models/panda/cms/post.rb +97 -0
- data/app/views/layouts/homepage.html.erb +1 -4
- data/app/views/layouts/page.html.erb +1 -4
- data/app/views/panda/cms/admin/dashboard/show.html.erb +1 -1
- data/app/views/panda/cms/admin/files/index.html.erb +1 -1
- data/app/views/panda/cms/admin/forms/show.html.erb +3 -3
- data/app/views/panda/cms/admin/menus/_menu_item_fields.html.erb +3 -3
- data/app/views/panda/cms/admin/menus/edit.html.erb +12 -14
- data/app/views/panda/cms/admin/menus/index.html.erb +1 -1
- data/app/views/panda/cms/admin/menus/new.html.erb +5 -7
- data/app/views/panda/cms/admin/pages/edit.html.erb +139 -20
- data/app/views/panda/cms/admin/pages/index.html.erb +6 -6
- data/app/views/panda/cms/admin/posts/_form.html.erb +41 -2
- data/app/views/panda/cms/admin/posts/edit.html.erb +1 -1
- data/app/views/panda/cms/admin/posts/index.html.erb +4 -4
- data/app/views/shared/_header.html.erb +1 -4
- data/config/brakeman.ignore +38 -0
- data/config/importmap.rb +8 -6
- data/config/locales/en.yml +41 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20251109131150_add_seo_fields_to_pages.rb +32 -0
- data/db/migrate/20251109131205_add_seo_fields_to_posts.rb +27 -0
- data/db/migrate/20251110114258_add_spam_tracking_to_form_submissions.rb +7 -0
- data/db/migrate/20251110122812_add_performance_indexes_to_pages_and_redirects.rb +13 -0
- data/lib/panda/cms/asset_loader.rb +27 -77
- data/lib/panda/cms/bulk_editor.rb +288 -12
- data/lib/panda/cms/engine/asset_config.rb +49 -0
- data/lib/panda/cms/engine/autoload_config.rb +19 -0
- data/lib/panda/cms/engine/backtrace_config.rb +42 -0
- data/lib/panda/cms/engine/core_config.rb +106 -0
- data/lib/panda/cms/engine/helper_config.rb +20 -0
- data/lib/panda/cms/engine/route_config.rb +34 -0
- data/lib/panda/cms/engine/view_component_config.rb +31 -0
- data/lib/panda/cms/engine.rb +44 -221
- data/lib/panda/cms.rb +10 -0
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +16 -2
- metadata +20 -22
- data/app/javascript/panda_cms/stimulus-loading.js +0 -39
- data/app/views/panda/cms/shared/_importmap.html.erb +0 -34
- data/config/initializers/inflections.rb +0 -5
- data/lib/tasks/assets.rake +0 -540
data/config/importmap.rb
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Base dependencies are now in panda-core (Stimulus, Turbo, Font Awesome, etc.)
|
|
4
4
|
# This file only contains CMS-specific pins
|
|
5
|
+
# NOTE: Paths must be absolute (starting with /) because Rack::Static serves
|
|
6
|
+
# from /panda/cms/, not from asset pipeline /assets/
|
|
5
7
|
|
|
6
|
-
pin "
|
|
7
|
-
pin "@hotwired/stimulus-loading", to: "panda/cms/stimulus-loading.js", preload: true
|
|
8
|
-
pin "@editorjs/editorjs", to: "panda/cms/editor/editorjs.js" # @2.30.6
|
|
8
|
+
pin "panda/cms/application", to: "/panda/cms/application.js", preload: true
|
|
9
|
+
pin "@hotwired/stimulus-loading", to: "/panda/cms/stimulus-loading.js", preload: true
|
|
10
|
+
pin "@editorjs/editorjs", to: "/panda/cms/editor/editorjs.js" # @2.30.6
|
|
9
11
|
|
|
10
12
|
# Pin the controllers directory
|
|
11
|
-
pin "controllers", to: "panda/cms/controllers/index.js"
|
|
12
|
-
pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/controllers"), under: "controllers"
|
|
13
|
-
pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/editor"), under: "editor"
|
|
13
|
+
pin "panda/cms/controllers/index", to: "/panda/cms/controllers/index.js"
|
|
14
|
+
pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/controllers"), under: "controllers", to: "/panda/cms/controllers"
|
|
15
|
+
pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/editor"), under: "editor", to: "/panda/cms/editor"
|
data/config/locales/en.yml
CHANGED
|
@@ -29,6 +29,17 @@ en:
|
|
|
29
29
|
title: Title
|
|
30
30
|
path: URL
|
|
31
31
|
panda_cms_template_id: Template
|
|
32
|
+
page_type: Page Type
|
|
33
|
+
seo_title: SEO Title
|
|
34
|
+
seo_description: SEO Description
|
|
35
|
+
seo_keywords: SEO Keywords
|
|
36
|
+
seo_index_mode: Search Engine Visibility
|
|
37
|
+
canonical_url: Canonical URL
|
|
38
|
+
og_title: OpenGraph Title
|
|
39
|
+
og_description: OpenGraph Description
|
|
40
|
+
og_type: OpenGraph Type
|
|
41
|
+
og_image: OpenGraph Image
|
|
42
|
+
inherit_seo: Inherit Settings
|
|
32
43
|
panda/cms/post:
|
|
33
44
|
title: Title
|
|
34
45
|
slug: URL
|
|
@@ -36,6 +47,15 @@ en:
|
|
|
36
47
|
user_id: Author
|
|
37
48
|
published_at: Published At
|
|
38
49
|
post_content: Content
|
|
50
|
+
seo_title: SEO Title
|
|
51
|
+
seo_description: SEO Description
|
|
52
|
+
seo_keywords: SEO Keywords
|
|
53
|
+
seo_index_mode: Search Engine Visibility
|
|
54
|
+
canonical_url: Canonical URL
|
|
55
|
+
og_title: OpenGraph Title
|
|
56
|
+
og_description: OpenGraph Description
|
|
57
|
+
og_type: OpenGraph Type
|
|
58
|
+
og_image: OpenGraph Image
|
|
39
59
|
statuses:
|
|
40
60
|
active: Active
|
|
41
61
|
draft: Draft
|
|
@@ -52,3 +72,24 @@ en:
|
|
|
52
72
|
lastname: Last Name
|
|
53
73
|
email: Email Address
|
|
54
74
|
current_theme: Theme
|
|
75
|
+
enums:
|
|
76
|
+
panda/cms/page:
|
|
77
|
+
seo_index_mode:
|
|
78
|
+
visible: Visible to search engines
|
|
79
|
+
invisible: Hidden from search engines
|
|
80
|
+
og_type:
|
|
81
|
+
website: Website
|
|
82
|
+
article: Article
|
|
83
|
+
profile: Profile
|
|
84
|
+
video: Video
|
|
85
|
+
book: Book
|
|
86
|
+
panda/cms/post:
|
|
87
|
+
seo_index_mode:
|
|
88
|
+
visible: Visible to search engines
|
|
89
|
+
invisible: Hidden from search engines
|
|
90
|
+
og_type:
|
|
91
|
+
website: Website
|
|
92
|
+
article: Article
|
|
93
|
+
profile: Profile
|
|
94
|
+
video: Video
|
|
95
|
+
book: Book
|
data/config/routes.rb
CHANGED
|
@@ -10,7 +10,7 @@ Panda::CMS::Engine.routes.draw do
|
|
|
10
10
|
|
|
11
11
|
namespace admin_path.delete_prefix("/").to_sym, path: "#{admin_path}/cms", as: :admin_cms, module: :admin do
|
|
12
12
|
resources :files
|
|
13
|
-
resources :forms
|
|
13
|
+
resources :forms
|
|
14
14
|
resources :menus
|
|
15
15
|
resources :pages do
|
|
16
16
|
resources :block_contents, only: %i[update]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class AddSEOFieldsToPages < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
# Create enum types for SEO fields
|
|
4
|
+
create_enum :panda_cms_seo_index_mode, ["visible", "invisible"]
|
|
5
|
+
create_enum :panda_cms_og_type, ["website", "article", "profile", "video", "book"]
|
|
6
|
+
|
|
7
|
+
# Add SEO basic fields
|
|
8
|
+
add_column :panda_cms_pages, :seo_title, :string
|
|
9
|
+
add_column :panda_cms_pages, :seo_description, :text
|
|
10
|
+
add_column :panda_cms_pages, :seo_keywords, :string
|
|
11
|
+
|
|
12
|
+
# Robots control (visible/invisible in base CMS)
|
|
13
|
+
add_column :panda_cms_pages, :seo_index_mode, :enum,
|
|
14
|
+
enum_type: "panda_cms_seo_index_mode",
|
|
15
|
+
default: "visible",
|
|
16
|
+
null: false
|
|
17
|
+
|
|
18
|
+
# Canonical URL
|
|
19
|
+
add_column :panda_cms_pages, :canonical_url, :string
|
|
20
|
+
|
|
21
|
+
# OpenGraph / Social Sharing
|
|
22
|
+
add_column :panda_cms_pages, :og_title, :string
|
|
23
|
+
add_column :panda_cms_pages, :og_description, :text
|
|
24
|
+
add_column :panda_cms_pages, :og_type, :enum,
|
|
25
|
+
enum_type: "panda_cms_og_type",
|
|
26
|
+
default: "website",
|
|
27
|
+
null: false
|
|
28
|
+
|
|
29
|
+
# Inheritance (pages only - they have hierarchy via nested sets)
|
|
30
|
+
add_column :panda_cms_pages, :inherit_seo, :boolean, default: true, null: false
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class AddSEOFieldsToPosts < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
# Add SEO basic fields
|
|
4
|
+
add_column :panda_cms_posts, :seo_title, :string
|
|
5
|
+
add_column :panda_cms_posts, :seo_description, :text
|
|
6
|
+
add_column :panda_cms_posts, :seo_keywords, :string
|
|
7
|
+
|
|
8
|
+
# Robots control (visible/invisible in base CMS)
|
|
9
|
+
add_column :panda_cms_posts, :seo_index_mode, :enum,
|
|
10
|
+
enum_type: "panda_cms_seo_index_mode",
|
|
11
|
+
default: "visible",
|
|
12
|
+
null: false
|
|
13
|
+
|
|
14
|
+
# Canonical URL
|
|
15
|
+
add_column :panda_cms_posts, :canonical_url, :string
|
|
16
|
+
|
|
17
|
+
# OpenGraph / Social Sharing
|
|
18
|
+
add_column :panda_cms_posts, :og_title, :string
|
|
19
|
+
add_column :panda_cms_posts, :og_description, :text
|
|
20
|
+
add_column :panda_cms_posts, :og_type, :enum,
|
|
21
|
+
enum_type: "panda_cms_og_type",
|
|
22
|
+
default: "article", # Posts default to 'article' type
|
|
23
|
+
null: false
|
|
24
|
+
|
|
25
|
+
# Note: No inherit_seo for posts - they don't have hierarchy
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class AddSpamTrackingToFormSubmissions < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :panda_cms_form_submissions, :ip_address, :string
|
|
4
|
+
add_column :panda_cms_form_submissions, :user_agent, :text
|
|
5
|
+
add_index :panda_cms_form_submissions, :ip_address
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddPerformanceIndexesToPagesAndRedirects < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
# Add index to pages.path for fast page lookups
|
|
6
|
+
# This is the most critical query path in the CMS
|
|
7
|
+
add_index :panda_cms_pages, :path, name: "index_panda_cms_pages_on_path"
|
|
8
|
+
|
|
9
|
+
# Add index to redirects.origin_path for fast redirect lookups
|
|
10
|
+
# Checked on every request in PagesController#handle_redirects
|
|
11
|
+
add_index :panda_cms_redirects, :origin_path, name: "index_panda_cms_redirects_on_origin_path"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -35,11 +35,9 @@ module Panda
|
|
|
35
35
|
|
|
36
36
|
# Check if GitHub-hosted assets should be used
|
|
37
37
|
def use_github_assets?
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
!development_assets_available? ||
|
|
42
|
-
((Rails.env.test? || in_test_environment?) && compiled_assets_available?)
|
|
38
|
+
# Panda CMS uses importmaps for JavaScript (no compilation needed)
|
|
39
|
+
# Only use GitHub assets in production or when explicitly enabled
|
|
40
|
+
false # Always use importmaps like panda-core
|
|
43
41
|
end
|
|
44
42
|
|
|
45
43
|
# Download assets from GitHub to local cache
|
|
@@ -105,39 +103,10 @@ module Panda
|
|
|
105
103
|
end
|
|
106
104
|
|
|
107
105
|
def development_asset_tags(options = {})
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
js_url = "/panda-cms-assets/panda-cms-#{version}.js"
|
|
113
|
-
css_url = "/panda-cms-assets/panda-cms-#{version}.css"
|
|
114
|
-
|
|
115
|
-
tags = []
|
|
116
|
-
|
|
117
|
-
# JavaScript tag
|
|
118
|
-
tags << content_tag(:script, "", {
|
|
119
|
-
src: js_url,
|
|
120
|
-
defer: true
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
# CSS tag if exists
|
|
124
|
-
if cached_asset_exists?(css_url)
|
|
125
|
-
tags << tag(:link, {
|
|
126
|
-
rel: "stylesheet",
|
|
127
|
-
href: css_url
|
|
128
|
-
})
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
tags.join("\n").html_safe
|
|
132
|
-
else
|
|
133
|
-
# In development, just use a simple script tag
|
|
134
|
-
# The view will handle importmap tags separately
|
|
135
|
-
content_tag(:script, "", {
|
|
136
|
-
src: development_javascript_url,
|
|
137
|
-
type: "module",
|
|
138
|
-
defer: true
|
|
139
|
-
})
|
|
140
|
-
end
|
|
106
|
+
# Panda CMS uses importmaps for JavaScript (no compiled bundles)
|
|
107
|
+
# The view will handle importmap tags via <%= javascript_importmap_tags %>
|
|
108
|
+
# We don't need to return anything here - CSS comes from panda-core
|
|
109
|
+
"".html_safe
|
|
141
110
|
end
|
|
142
111
|
|
|
143
112
|
def github_javascript_url
|
|
@@ -161,25 +130,8 @@ module Panda
|
|
|
161
130
|
end
|
|
162
131
|
|
|
163
132
|
def development_javascript_url
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
if Rails.env.development?
|
|
167
|
-
"/panda/cms/application_panda_cms.js"
|
|
168
|
-
else
|
|
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
|
|
182
|
-
end
|
|
133
|
+
# Always use importmap (no compiled bundles)
|
|
134
|
+
"/panda/cms/application.js"
|
|
183
135
|
end
|
|
184
136
|
|
|
185
137
|
def development_css_url
|
|
@@ -207,14 +159,7 @@ module Panda
|
|
|
207
159
|
end
|
|
208
160
|
|
|
209
161
|
def asset_version
|
|
210
|
-
|
|
211
|
-
# In other environments, use git SHA for dynamic versioning
|
|
212
|
-
# Also check for test environment indicators since Rails.env might be development in specs
|
|
213
|
-
if Rails.env.test? || ENV["CI"].present? || in_test_environment?
|
|
214
|
-
Panda::CMS::VERSION
|
|
215
|
-
else
|
|
216
|
-
`git rev-parse --short HEAD`.strip
|
|
217
|
-
end
|
|
162
|
+
Panda::CMS::VERSION
|
|
218
163
|
end
|
|
219
164
|
|
|
220
165
|
def in_test_environment?
|
|
@@ -222,14 +167,6 @@ module Panda
|
|
|
222
167
|
defined?(RSpec) && RSpec.respond_to?(:configuration)
|
|
223
168
|
end
|
|
224
169
|
|
|
225
|
-
def compiled_assets_available?
|
|
226
|
-
# Check if compiled assets exist in test location
|
|
227
|
-
version = asset_version
|
|
228
|
-
js_file = Rails.public_path.join("panda-cms-assets", "panda-cms-#{version}.js")
|
|
229
|
-
css_file = Rails.public_path.join("panda-cms-assets", "panda-cms-#{version}.css")
|
|
230
|
-
js_file.exist? && css_file.exist?
|
|
231
|
-
end
|
|
232
|
-
|
|
233
170
|
def development_assets_available?
|
|
234
171
|
# Check if local development assets exist (importmap, etc.)
|
|
235
172
|
importmap_available? || engine_assets_available?
|
|
@@ -239,12 +176,18 @@ module Panda
|
|
|
239
176
|
return false unless defined?(Rails.application.importmap)
|
|
240
177
|
|
|
241
178
|
begin
|
|
242
|
-
#
|
|
243
|
-
if Rails.application.importmap.respond_to?(:
|
|
244
|
-
|
|
245
|
-
|
|
179
|
+
# Try different API methods depending on importmap-rails version
|
|
180
|
+
if Rails.application.importmap.respond_to?(:packages)
|
|
181
|
+
# importmap-rails 2.x - check packages hash
|
|
182
|
+
Rails.application.importmap.packages.keys.any? { |name| name.to_s.include?("panda") }
|
|
246
183
|
elsif Rails.application.importmap.respond_to?(:entries)
|
|
184
|
+
# importmap-rails 1.x - check entries array
|
|
247
185
|
Rails.application.importmap.entries.any? { |entry| entry.name.include?("panda") }
|
|
186
|
+
elsif Rails.application.importmap.respond_to?(:to_json)
|
|
187
|
+
# Fallback with proper resolver
|
|
188
|
+
# Note: to_json requires a resolver in newer versions
|
|
189
|
+
# We'll just return false if we can't access packages/entries
|
|
190
|
+
false
|
|
248
191
|
else
|
|
249
192
|
false
|
|
250
193
|
end
|
|
@@ -260,6 +203,13 @@ module Panda
|
|
|
260
203
|
File.exist?(engine_js_path)
|
|
261
204
|
end
|
|
262
205
|
|
|
206
|
+
def compiled_assets_available?
|
|
207
|
+
# Check if compiled JavaScript bundle exists
|
|
208
|
+
version = asset_version
|
|
209
|
+
js_file = Rails.public_path.join("panda-cms-assets", "panda-cms-#{version}.js")
|
|
210
|
+
js_file.exist?
|
|
211
|
+
end
|
|
212
|
+
|
|
263
213
|
def cached_assets_exist?(version)
|
|
264
214
|
cache_dir = local_cache_directory.join(version)
|
|
265
215
|
cache_dir.exist? && cache_dir.join("panda-cms-#{version}.js").exist?
|