panda-cms 0.7.3 → 0.7.5
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 +40 -5
- data/Rakefile +2 -0
- data/app/assets/builds/panda.cms.css +2 -6
- data/app/assets/tailwind/application.css +178 -0
- data/app/assets/tailwind/tailwind.config.js +15 -0
- data/app/builders/panda/cms/form_builder.rb +27 -36
- data/app/components/panda/cms/admin/flash_message_component.html.erb +2 -2
- data/app/components/panda/cms/admin/heading_component.rb +5 -4
- data/app/components/panda/cms/admin/panel_component.rb +2 -2
- data/app/components/panda/cms/admin/statistics_component.rb +1 -2
- data/app/components/panda/cms/admin/user_activity_component.html.erb +3 -1
- data/app/components/panda/cms/admin/user_activity_component.rb +8 -21
- data/app/components/panda/cms/code_component.rb +8 -4
- data/app/components/panda/cms/menu_component.rb +7 -6
- data/app/components/panda/cms/page_menu_component.rb +15 -17
- data/app/components/panda/cms/rich_text_component.rb +5 -6
- data/app/components/panda/cms/text_component.rb +6 -7
- data/app/constraints/panda/cms/admin_constraint.rb +4 -1
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +0 -1
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +13 -9
- data/app/controllers/panda/cms/admin/forms_controller.rb +0 -3
- data/app/controllers/panda/cms/admin/my_profile_controller.rb +44 -0
- data/app/controllers/panda/cms/admin/pages_controller.rb +15 -4
- data/app/controllers/panda/cms/admin/posts_controller.rb +6 -22
- data/app/controllers/panda/cms/admin/sessions_controller.rb +3 -5
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +32 -25
- data/app/controllers/panda/cms/admin/settings_controller.rb +14 -10
- data/app/controllers/panda/cms/application_controller.rb +7 -2
- data/app/controllers/panda/cms/errors_controller.rb +5 -2
- data/app/controllers/panda/cms/form_submissions_controller.rb +4 -0
- data/app/controllers/panda/cms/pages_controller.rb +40 -35
- data/app/controllers/panda/cms/posts_controller.rb +2 -0
- data/app/helpers/panda/cms/admin/files_helper.rb +5 -1
- data/app/helpers/panda/cms/admin/pages_helper.rb +5 -1
- data/app/helpers/panda/cms/asset_helper.rb +182 -0
- data/app/helpers/panda/cms/pages_helper.rb +2 -0
- data/app/helpers/panda/cms/posts_helper.rb +2 -0
- data/app/helpers/panda/cms/theme_helper.rb +2 -0
- data/app/javascript/panda/cms/controllers/editor_form_controller.js +59 -6
- data/app/javascript/panda/cms/controllers/index.js +5 -9
- data/app/javascript/panda/cms/controllers/slug_controller.js +64 -31
- data/app/javascript/panda/cms/controllers/theme_form_controller.js +25 -0
- data/app/javascript/panda/cms/stimulus-loading.js +39 -0
- data/app/javascript/panda_cms/stimulus-loading.js +39 -0
- data/app/jobs/panda/cms/application_job.rb +2 -0
- data/app/jobs/panda/cms/record_visit_job.rb +14 -14
- data/app/mailers/panda/cms/application_mailer.rb +2 -0
- data/app/mailers/panda/cms/form_mailer.rb +3 -1
- data/app/models/panda/cms/application_record.rb +3 -0
- data/app/models/panda/cms/block.rb +12 -17
- data/app/models/panda/cms/block_content.rb +7 -6
- data/app/models/panda/cms/breadcrumb.rb +2 -0
- data/app/models/panda/cms/current.rb +2 -0
- data/app/models/panda/cms/form.rb +2 -0
- data/app/models/panda/cms/form_submission.rb +2 -0
- data/app/models/panda/cms/menu.rb +12 -9
- data/app/models/panda/cms/menu_item.rb +10 -6
- data/app/models/panda/cms/page.rb +31 -16
- data/app/models/panda/cms/post.rb +12 -10
- data/app/models/panda/cms/redirect.rb +9 -1
- data/app/models/panda/cms/template.rb +17 -13
- data/app/models/panda/cms/user.rb +2 -0
- data/app/models/panda/cms/visit.rb +3 -1
- data/app/models/panda/social/instagram_post.rb +17 -0
- data/app/services/panda/cms/html_to_editor_js_converter.rb +10 -15
- data/app/services/panda/social/instagram_feed_service.rb +63 -0
- data/app/views/layouts/different_page.html.erb +6 -0
- data/app/views/layouts/homepage.html.erb +37 -0
- data/app/views/layouts/page.html.erb +18 -0
- data/app/views/layouts/panda/cms/application.html.erb +1 -0
- data/app/views/panda/cms/admin/my_profile/edit.html.erb +35 -0
- data/app/views/panda/cms/admin/pages/index.html.erb +1 -1
- data/app/views/panda/cms/admin/pages/new.html.erb +14 -8
- data/app/views/panda/cms/admin/posts/_form.html.erb +10 -0
- data/app/views/panda/cms/admin/posts/edit.html.erb +3 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/index.html.erb +3 -1
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +1 -1
- data/app/views/panda/cms/shared/_header.html.erb +14 -4
- data/app/views/panda/cms/shared/_importmap.html.erb +2 -1
- data/app/views/shared/_footer.html.erb +3 -0
- data/app/views/shared/_header.html.erb +11 -0
- data/config/importmap.rb +2 -0
- data/config/initializers/inflections.rb +2 -0
- data/config/initializers/panda/cms/form_errors.rb +20 -21
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb +2 -0
- data/config/initializers/panda/cms.rb +2 -0
- data/config/initializers/zeitwork.rb +2 -0
- data/config/locales/en.yml +5 -0
- data/config/puma/test.rb +3 -1
- data/config/routes.rb +11 -8
- data/db/migrate/20240205223709_create_panda_cms_pages.rb +2 -0
- data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +2 -0
- data/db/migrate/20240303002805_create_panda_cms_templates.rb +4 -1
- data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +2 -0
- data/db/migrate/20240303022441_create_panda_cms_blocks.rb +4 -1
- data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +2 -0
- data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +2 -0
- data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +2 -0
- data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +2 -0
- data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +2 -0
- data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +7 -5
- data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +2 -0
- data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +3 -1
- data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +2 -0
- data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +2 -0
- data/db/migrate/20240317010532_create_panda_cms_users.rb +2 -0
- data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +2 -0
- data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +2 -0
- data/db/migrate/20240317214827_create_panda_cms_redirects.rb +2 -0
- data/db/migrate/20240317230622_create_panda_cms_visits.rb +2 -0
- data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +5 -2
- data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +2 -0
- data/db/migrate/20240701225422_add_service_name_to_active_storage_blobs.active_storage.rb +8 -6
- data/db/migrate/20240701225423_create_active_storage_variant_records.active_storage.rb +2 -0
- data/db/migrate/20240701225424_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +2 -0
- data/db/migrate/20240804235210_create_panda_cms_forms.rb +2 -0
- data/db/migrate/20240805013612_create_panda_cms_form_submissions.rb +2 -0
- data/db/migrate/20240805121123_create_panda_cms_posts.rb +3 -1
- data/db/migrate/20240805123104_create_panda_cms_post_versions.rb +2 -0
- data/db/migrate/20240806112735_fix_panda_cms_visits_column_names.rb +2 -0
- data/db/migrate/20240806204412_add_completion_path_to_panda_cms_forms.rb +2 -0
- data/db/migrate/20240820081917_change_form_submissions_to_submission_count.rb +2 -0
- data/db/migrate/20240923234535_add_depth_to_panda_cms_menus.rb +6 -4
- data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +2 -0
- data/db/migrate/20241119214548_convert_post_content_to_editor_js.rb +2 -0
- data/db/migrate/20241120000419_remove_post_tag_references.rb +2 -0
- data/db/migrate/20241120110943_add_editor_js_to_posts.rb +2 -0
- data/db/migrate/20241120113859_add_cached_content_to_panda_cms_posts.rb +2 -0
- data/db/migrate/20241123234140_remove_post_tag_id_from_posts.rb +2 -0
- data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +2 -0
- data/db/migrate/20250120235542_remove_paper_trail.rb +56 -0
- data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +16 -0
- data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +7 -0
- data/db/seeds.rb +2 -0
- data/lib/generators/panda/cms/install_generator.rb +2 -0
- data/lib/panda/cms/asset_loader.rb +390 -0
- data/lib/panda/cms/bulk_editor.rb +7 -3
- data/lib/panda/cms/demo_site_generator.rb +27 -4
- data/lib/panda/cms/editor_js/blocks/alert.rb +2 -0
- data/lib/panda/cms/editor_js/blocks/base.rb +2 -0
- data/lib/panda/cms/editor_js/blocks/header.rb +2 -0
- data/lib/panda/cms/editor_js/blocks/image.rb +3 -0
- data/lib/panda/cms/editor_js/blocks/list.rb +2 -0
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +3 -0
- data/lib/panda/cms/editor_js/blocks/quote.rb +3 -0
- data/lib/panda/cms/editor_js/blocks/table.rb +3 -1
- data/lib/panda/cms/editor_js/renderer.rb +3 -0
- data/lib/panda/cms/editor_js.rb +2 -0
- data/lib/panda/cms/editor_js_content.rb +50 -23
- data/lib/panda/cms/engine.rb +36 -37
- data/lib/panda/cms/exceptions_app.rb +2 -0
- data/lib/panda/cms/railtie.rb +2 -0
- data/lib/panda/cms/slug.rb +3 -1
- data/lib/panda-cms/version.rb +3 -1
- data/lib/panda-cms.rb +17 -2
- data/lib/tasks/assets.rake +547 -0
- data/lib/tasks/panda/cms/install.rake +25 -0
- data/lib/tasks/panda/social/instagram.rake +20 -0
- data/lib/tasks/panda_cms.rake +3 -30
- data/public/panda-cms-assets/editor-js/core/editorjs.min.js +83 -0
- data/public/panda-cms-assets/editor-js/plugins/embed.min.js +2 -0
- data/public/panda-cms-assets/editor-js/plugins/header.min.js +9 -0
- data/public/panda-cms-assets/editor-js/plugins/nested-list.min.js +2 -0
- data/public/panda-cms-assets/editor-js/plugins/paragraph.min.js +9 -0
- data/public/panda-cms-assets/editor-js/plugins/quote.min.js +2 -0
- data/public/panda-cms-assets/editor-js/plugins/simple-image.min.js +2 -0
- data/public/panda-cms-assets/editor-js/plugins/table.min.js +2 -0
- data/public/panda-cms-assets/manifest.json +20 -0
- data/public/panda-cms-assets/panda-cms-0.7.4.css +26 -0
- data/public/panda-cms-assets/panda-cms-0.7.4.js +150 -0
- metadata +71 -438
- data/app/models/action_text/rich_text_version.rb +0 -6
- data/app/models/panda/cms/block_content_version.rb +0 -8
- data/app/models/panda/cms/page_version.rb +0 -8
- data/app/models/panda/cms/post_version.rb +0 -8
- data/app/models/panda/cms/template_version.rb +0 -8
- data/app/models/panda/cms/version.rb +0 -8
- data/config/initializers/panda/cms/paper_trail.rb +0 -7
- data/db/migrate/20240904200605_create_action_text_tables.action_text.rb +0 -24
- data/db/migrate/20241119214549_remove_action_text_from_posts.rb +0 -9
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module CMS
|
5
|
+
module AssetHelper
|
6
|
+
# Include Panda CMS JavaScript and CSS assets
|
7
|
+
# Automatically chooses between GitHub-hosted assets (production)
|
8
|
+
# and local development assets
|
9
|
+
def panda_cms_assets
|
10
|
+
Panda::CMS::AssetLoader.asset_tags.html_safe
|
11
|
+
end
|
12
|
+
|
13
|
+
# Include only Panda CMS JavaScript
|
14
|
+
def panda_cms_javascript
|
15
|
+
js_url = Panda::CMS::AssetLoader.javascript_url
|
16
|
+
return "" unless js_url
|
17
|
+
|
18
|
+
if Panda::CMS::AssetLoader.use_github_assets?
|
19
|
+
# GitHub-hosted assets with integrity check
|
20
|
+
version = Panda::CMS::AssetLoader.send(:asset_version)
|
21
|
+
integrity = asset_integrity(version, "panda-cms-#{version}.js")
|
22
|
+
|
23
|
+
tag_options = {
|
24
|
+
src: js_url
|
25
|
+
}
|
26
|
+
# In CI environment, don't use defer to ensure immediate execution
|
27
|
+
tag_options[:defer] = true unless ENV["GITHUB_ACTIONS"] == "true"
|
28
|
+
# Standalone bundles should NOT use type: "module" - they're regular scripts
|
29
|
+
# Only use type: "module" for importmap/ES module assets
|
30
|
+
if !js_url.include?("panda-cms-assets")
|
31
|
+
tag_options[:type] = "module"
|
32
|
+
end
|
33
|
+
tag_options[:integrity] = integrity if integrity
|
34
|
+
tag_options[:crossorigin] = "anonymous" if integrity
|
35
|
+
|
36
|
+
content_tag(:script, "", tag_options)
|
37
|
+
elsif js_url.include?("panda-cms-assets")
|
38
|
+
# Development assets - check if it's a standalone bundle or importmap
|
39
|
+
defer_option = (ENV["GITHUB_ACTIONS"] == "true") ? {} : {defer: true}
|
40
|
+
javascript_include_tag(js_url, **defer_option)
|
41
|
+
# Standalone bundle - don't use type: "module"
|
42
|
+
else
|
43
|
+
# Importmap asset - use type: "module"
|
44
|
+
defer_option = (ENV["GITHUB_ACTIONS"] == "true") ? {} : {defer: true}
|
45
|
+
javascript_include_tag(js_url, type: "module", **defer_option)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Include only Panda CMS CSS
|
50
|
+
def panda_cms_stylesheet
|
51
|
+
css_url = Panda::CMS::AssetLoader.css_url
|
52
|
+
return "" unless css_url
|
53
|
+
|
54
|
+
if Panda::CMS::AssetLoader.use_github_assets?
|
55
|
+
# GitHub-hosted assets with integrity check
|
56
|
+
version = Panda::CMS::VERSION
|
57
|
+
integrity = asset_integrity(version, "panda-cms-#{version}.css")
|
58
|
+
|
59
|
+
tag_options = {
|
60
|
+
rel: "stylesheet",
|
61
|
+
href: css_url
|
62
|
+
}
|
63
|
+
tag_options[:integrity] = integrity if integrity
|
64
|
+
tag_options[:crossorigin] = "anonymous" if integrity
|
65
|
+
|
66
|
+
tag(:link, tag_options)
|
67
|
+
else
|
68
|
+
# Development assets
|
69
|
+
stylesheet_link_tag(css_url)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get the current Panda CMS version
|
74
|
+
def panda_cms_version
|
75
|
+
Panda::CMS::VERSION
|
76
|
+
end
|
77
|
+
|
78
|
+
# Check if using GitHub-hosted assets
|
79
|
+
def using_github_assets?
|
80
|
+
Panda::CMS::AssetLoader.use_github_assets?
|
81
|
+
end
|
82
|
+
|
83
|
+
# Download and cache assets if needed
|
84
|
+
# Call this in an initializer or controller to pre-cache assets
|
85
|
+
def ensure_panda_cms_assets!
|
86
|
+
Panda::CMS::AssetLoader.ensure_assets_available!
|
87
|
+
end
|
88
|
+
|
89
|
+
# Debug information about asset loading
|
90
|
+
def panda_cms_asset_debug
|
91
|
+
return "" unless Rails.env.development? || Rails.env.test?
|
92
|
+
|
93
|
+
version = Panda::CMS::VERSION
|
94
|
+
js_url = Panda::CMS::AssetLoader.javascript_url
|
95
|
+
css_url = Panda::CMS::AssetLoader.css_url
|
96
|
+
using_github = Panda::CMS::AssetLoader.use_github_assets?
|
97
|
+
compiled_available = Panda::CMS::AssetLoader.send(:compiled_assets_available?)
|
98
|
+
|
99
|
+
# Additional CI debugging
|
100
|
+
asset_file_exists = js_url && File.exist?(Rails.root.join("public#{js_url}"))
|
101
|
+
ci_env = ENV["GITHUB_ACTIONS"] == "true"
|
102
|
+
|
103
|
+
# Check what script tag will be generated
|
104
|
+
script_tag_preview = if using_github
|
105
|
+
tag_options = {src: js_url}
|
106
|
+
tag_options[:defer] = true unless ci_env
|
107
|
+
if !js_url.include?("panda-cms-assets")
|
108
|
+
tag_options[:type] = "module"
|
109
|
+
end
|
110
|
+
"Script tag: <script#{tag_options.map { |k, v| (v == true) ? " #{k}" : " #{k}=\"#{v}\"" }.join}></script>"
|
111
|
+
else
|
112
|
+
"Using development assets"
|
113
|
+
end
|
114
|
+
|
115
|
+
debug_info = [
|
116
|
+
"<!-- Panda CMS Asset Debug Info -->",
|
117
|
+
"<!-- Version: #{version} -->",
|
118
|
+
"<!-- Using GitHub assets: #{using_github} -->",
|
119
|
+
"<!-- Compiled assets available: #{compiled_available} -->",
|
120
|
+
"<!-- JavaScript URL: #{js_url} -->",
|
121
|
+
"<!-- CSS URL: #{css_url || "none"} -->",
|
122
|
+
"<!-- Rails environment: #{Rails.env} -->",
|
123
|
+
"<!-- Asset file exists: #{asset_file_exists} -->",
|
124
|
+
"<!-- Rails root: #{Rails.root} -->",
|
125
|
+
"<!-- CI environment: #{ci_env} -->",
|
126
|
+
"<!-- #{script_tag_preview} -->",
|
127
|
+
"<!-- Params embed_id: #{params[:embed_id] if respond_to?(:params)} -->",
|
128
|
+
"<!-- Compiled at: #{Time.now.utc.iso8601} -->"
|
129
|
+
]
|
130
|
+
|
131
|
+
debug_info.join("\n").html_safe
|
132
|
+
end
|
133
|
+
|
134
|
+
# Initialize Panda CMS Stimulus application
|
135
|
+
# Call this after the asset tags to ensure proper initialization
|
136
|
+
def panda_cms_stimulus_init
|
137
|
+
javascript_tag(<<~JS, type: "module")
|
138
|
+
// Initialize Panda CMS Stimulus application
|
139
|
+
document.addEventListener('DOMContentLoaded', function() {
|
140
|
+
if (window.pandaCmsStimulus) {
|
141
|
+
console.debug('[Panda CMS] Stimulus application initialized');
|
142
|
+
|
143
|
+
// Set debug mode based on Rails environment
|
144
|
+
const railsEnv = document.body?.dataset?.environment || 'production';
|
145
|
+
window.pandaCmsStimulus.debug = (railsEnv === 'development');
|
146
|
+
|
147
|
+
// Trigger a custom event to signal Panda CMS is ready
|
148
|
+
document.dispatchEvent(new CustomEvent('panda-cms:ready', {
|
149
|
+
detail: {
|
150
|
+
version: '#{Panda::CMS::VERSION}',
|
151
|
+
usingGitHubAssets: #{Panda::CMS::AssetLoader.use_github_assets?}
|
152
|
+
}
|
153
|
+
}));
|
154
|
+
} else {
|
155
|
+
console.warn('[Panda CMS] Stimulus application not found. Assets may not have loaded properly.');
|
156
|
+
}
|
157
|
+
});
|
158
|
+
JS
|
159
|
+
end
|
160
|
+
|
161
|
+
# Complete asset loading with initialization
|
162
|
+
# This is the recommended way to include all Panda CMS assets
|
163
|
+
def panda_cms_complete_assets
|
164
|
+
[
|
165
|
+
panda_cms_asset_debug,
|
166
|
+
panda_cms_assets,
|
167
|
+
panda_cms_stimulus_init,
|
168
|
+
# Add immediate JavaScript execution test for CI debugging
|
169
|
+
(Rails.env.test? ? javascript_tag("window.pandaCmsInlineTest = true; console.log('[Panda CMS] Inline script executed');") : "")
|
170
|
+
].join("\n").html_safe
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def asset_integrity(version, filename)
|
176
|
+
Panda::CMS::AssetLoader.send(:asset_integrity, version, filename)
|
177
|
+
rescue
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -10,6 +10,10 @@ export default class extends Controller {
|
|
10
10
|
|
11
11
|
connect() {
|
12
12
|
this.loadEditorResources();
|
13
|
+
// Enable submit button after a delay as fallback
|
14
|
+
setTimeout(() => {
|
15
|
+
this.enableSubmitButton();
|
16
|
+
}, 1000);
|
13
17
|
}
|
14
18
|
|
15
19
|
async loadEditorResources() {
|
@@ -77,6 +81,8 @@ export default class extends Controller {
|
|
77
81
|
holderDiv.dataset.editorInitialized = "true";
|
78
82
|
// Add a class to indicate the editor is ready
|
79
83
|
holderDiv.classList.add("editor-ready");
|
84
|
+
// Enable the submit button
|
85
|
+
this.enableSubmitButton();
|
80
86
|
// Dispatch an event when editor is ready
|
81
87
|
this.editorContainerTarget.dispatchEvent(new CustomEvent("editor:ready"));
|
82
88
|
},
|
@@ -119,12 +125,6 @@ export default class extends Controller {
|
|
119
125
|
// Wait for editor to be ready
|
120
126
|
await this.editor.isReady;
|
121
127
|
console.debug("[Panda CMS] Editor initialized successfully");
|
122
|
-
this.editorContainerTarget.dataset.editorInitialized = "true";
|
123
|
-
holderDiv.dataset.editorInitialized = "true";
|
124
|
-
// Add a class to indicate the editor is ready
|
125
|
-
holderDiv.classList.add("editor-ready");
|
126
|
-
// Dispatch an event when editor is ready
|
127
|
-
this.editorContainerTarget.dispatchEvent(new CustomEvent("editor:ready"));
|
128
128
|
|
129
129
|
} catch (error) {
|
130
130
|
console.error("[Panda CMS] Editor setup failed:", error);
|
@@ -133,6 +133,8 @@ export default class extends Controller {
|
|
133
133
|
holderDiv.dataset.editorInitialized = "false";
|
134
134
|
holderDiv.classList.remove("editor-ready");
|
135
135
|
}
|
136
|
+
// Still enable the submit button even if editor fails
|
137
|
+
this.enableSubmitButton();
|
136
138
|
}
|
137
139
|
}
|
138
140
|
|
@@ -190,6 +192,57 @@ export default class extends Controller {
|
|
190
192
|
source: "editorJS",
|
191
193
|
};
|
192
194
|
}
|
195
|
+
|
196
|
+
enableSubmitButton() {
|
197
|
+
// Find the submit button in the form and enable it
|
198
|
+
const form = this.element.closest('form');
|
199
|
+
if (form) {
|
200
|
+
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
|
201
|
+
if (submitButton) {
|
202
|
+
submitButton.disabled = false;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
async submit(event) {
|
208
|
+
// Prevent the default button click behavior temporarily
|
209
|
+
event.preventDefault();
|
210
|
+
|
211
|
+
const submitButton = event.target;
|
212
|
+
const form = submitButton.closest('form');
|
213
|
+
|
214
|
+
// Re-enable the button that was disabled by data-disable-with
|
215
|
+
submitButton.disabled = false;
|
216
|
+
|
217
|
+
// Ensure editor content is saved before form submission
|
218
|
+
if (this.editor) {
|
219
|
+
try {
|
220
|
+
const outputData = await this.editor.save();
|
221
|
+
outputData.source = "editorJS";
|
222
|
+
const jsonString = JSON.stringify(outputData);
|
223
|
+
this.hiddenFieldTarget.value = jsonString;
|
224
|
+
console.log("[Panda CMS] Editor content saved before submission");
|
225
|
+
} catch (error) {
|
226
|
+
console.error("[Panda CMS] Failed to save editor content:", error);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
// Now trigger the normal form submission (this will let Rails/Turbo handle it properly)
|
231
|
+
if (form) {
|
232
|
+
// Remove our custom action to prevent infinite loop
|
233
|
+
submitButton.removeAttribute('data-action');
|
234
|
+
|
235
|
+
// Create a new click event that will trigger the normal form submission
|
236
|
+
const clickEvent = new MouseEvent('click', {
|
237
|
+
bubbles: true,
|
238
|
+
cancelable: true,
|
239
|
+
view: window
|
240
|
+
});
|
241
|
+
|
242
|
+
// Dispatch the click event, which will trigger normal Rails form submission
|
243
|
+
submitButton.dispatchEvent(clickEvent);
|
244
|
+
}
|
245
|
+
}
|
193
246
|
|
194
247
|
disconnect() {
|
195
248
|
if (this.editor) {
|
@@ -1,16 +1,10 @@
|
|
1
1
|
console.debug("[Panda CMS] Importing Panda CMS Stimulus Controller...")
|
2
2
|
|
3
|
-
import {
|
3
|
+
import { application } from "@hotwired/stimulus-loading"
|
4
4
|
|
5
|
-
|
5
|
+
console.debug("[Panda CMS] Using shared Stimulus application...")
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
// Configure Stimulus development experience
|
10
|
-
pandaCmsApplication.debug = false
|
11
|
-
window.pandaCmsStimulus = pandaCmsApplication
|
12
|
-
|
13
|
-
console.debug("[Panda CMS] window.pandaCmsStimulus available...")
|
7
|
+
const pandaCmsApplication = application
|
14
8
|
|
15
9
|
console.debug("[Panda CMS] Registering controllers...")
|
16
10
|
|
@@ -28,6 +22,8 @@ import EditorIframeController from "panda/cms/controllers/editor_iframe_controll
|
|
28
22
|
pandaCmsApplication.register("editor-iframe", EditorIframeController)
|
29
23
|
|
30
24
|
console.debug("[Panda CMS] Registering components...")
|
25
|
+
import ThemeFormController from "panda/cms/controllers/theme_form_controller";
|
26
|
+
pandaCmsApplication.register("theme-form", ThemeFormController);
|
31
27
|
|
32
28
|
// Import and register all TailwindCSS Components or just the ones you need
|
33
29
|
import { Alert, Autosave, ColorPreview, Dropdown, Modal, Tabs, Popover, Toggle, Slideover } from "tailwindcss-stimulus-components"
|
@@ -14,57 +14,85 @@ export default class extends Controller {
|
|
14
14
|
|
15
15
|
connect() {
|
16
16
|
console.debug("[Panda CMS] Slug handler connected...");
|
17
|
-
//
|
18
|
-
if (this.input_textTarget.value) {
|
19
|
-
this.generatePath();
|
20
|
-
}
|
17
|
+
// Don't auto-generate on connect anymore
|
21
18
|
}
|
22
19
|
|
23
|
-
generatePath() {
|
24
|
-
|
25
|
-
|
20
|
+
generatePath(event) {
|
21
|
+
// Prevent event object from being used as input
|
22
|
+
const title = this.input_textTarget.value.trim();
|
23
|
+
console.debug("[Panda CMS] Generating path from title:", title);
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
if (!title) {
|
26
|
+
this.output_textTarget.value = "";
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
|
30
|
+
// Only generate path if output is empty OR user has not manually edited it
|
31
|
+
if (!this.output_textTarget.value || !this.output_textTarget.dataset.userEdited) {
|
32
|
+
// Convert title to slug format
|
33
|
+
const slug = this.createSlug(title);
|
34
|
+
console.debug("[Panda CMS] Generated slug:", slug);
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
// Only add year/month prefix for posts
|
37
|
+
if (this.addDatePrefixValue) {
|
38
|
+
// Get current date for year/month
|
39
|
+
const now = new Date();
|
40
|
+
const year = now.getFullYear();
|
41
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
42
|
+
|
43
|
+
// Add leading slash and use date format
|
44
|
+
this.output_textTarget.value = `/${year}/${month}/${slug}`;
|
45
|
+
} else {
|
46
|
+
// If we have a parent selected, let setPrePath handle it
|
47
|
+
if (this.input_selectTarget.value) {
|
48
|
+
this.setPrePath(slug);
|
49
|
+
} else {
|
50
|
+
// Add leading slash for regular pages
|
51
|
+
this.output_textTarget.value = `/${slug}`;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
console.debug("[Panda CMS] Final path value:", this.output_textTarget.value);
|
45
55
|
}
|
46
56
|
}
|
47
57
|
|
48
|
-
setPrePath() {
|
58
|
+
setPrePath(slug = null) {
|
49
59
|
try {
|
60
|
+
// Don't do anything if we're passed the event object
|
61
|
+
if (slug && typeof slug === 'object') {
|
62
|
+
slug = null;
|
63
|
+
}
|
64
|
+
|
50
65
|
const match = this.input_selectTarget.options[this.input_selectTarget.selectedIndex].text.match(/.*\((.*)\)$/);
|
51
66
|
if (match) {
|
52
|
-
|
53
|
-
|
54
|
-
//
|
55
|
-
|
56
|
-
|
67
|
+
const parentPath = match[1].replace(/\/$/, ""); // Remove trailing slash if present
|
68
|
+
|
69
|
+
// If we have a specific slug passed in, use it
|
70
|
+
// Otherwise only use the title-based slug if we have a title
|
71
|
+
const currentSlug = slug ||
|
72
|
+
(this.input_textTarget.value.trim() ? this.createSlug(this.input_textTarget.value.trim()) : "");
|
73
|
+
|
74
|
+
// Set the full path including parent path
|
75
|
+
this.output_textTarget.value = currentSlug
|
76
|
+
? `${parentPath}/${currentSlug}`
|
77
|
+
: `${parentPath}/`;
|
78
|
+
|
79
|
+
console.debug("[Panda CMS] Set path with parent:", this.output_textTarget.value);
|
57
80
|
}
|
58
81
|
} catch (e) {
|
59
82
|
console.error("[Panda CMS] Error setting pre-path:", e);
|
83
|
+
// Clear the output on error
|
84
|
+
this.output_textTarget.value = "";
|
60
85
|
}
|
61
86
|
}
|
62
87
|
|
63
88
|
createSlug(input) {
|
64
|
-
return
|
89
|
+
if (!input || typeof input !== 'string') return "";
|
90
|
+
const slug = input
|
65
91
|
.toLowerCase()
|
92
|
+
.trim()
|
66
93
|
.replace(/[^a-z0-9]+/g, "-")
|
67
94
|
.replace(/^-+|-+$/g, "");
|
95
|
+
return slug;
|
68
96
|
}
|
69
97
|
|
70
98
|
trimStartEnd(str, ch) {
|
@@ -74,4 +102,9 @@ export default class extends Controller {
|
|
74
102
|
while (end > start && str[end - 1] === ch) --end;
|
75
103
|
return start > 0 || end < str.length ? str.substring(start, end) : str;
|
76
104
|
}
|
105
|
+
|
106
|
+
// Add handler for manual path edits
|
107
|
+
handlePathInput() {
|
108
|
+
this.output_textTarget.dataset.userEdited = "true";
|
109
|
+
}
|
77
110
|
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
// Connects to data-controller="theme-form"
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
// Ensure submit button is enabled on connect
|
7
|
+
this.enableSubmitButton();
|
8
|
+
}
|
9
|
+
|
10
|
+
updateTheme(event) {
|
11
|
+
const newTheme = event.target.value;
|
12
|
+
document.documentElement.dataset.theme = newTheme;
|
13
|
+
}
|
14
|
+
|
15
|
+
enableSubmitButton() {
|
16
|
+
// Find the submit button in the form and ensure it's enabled
|
17
|
+
const form = this.element;
|
18
|
+
if (form) {
|
19
|
+
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
|
20
|
+
if (submitButton) {
|
21
|
+
submitButton.disabled = false;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// Stimulus loading utilities for Panda CMS
|
2
|
+
// This provides the loading functionality that would normally come from stimulus-rails
|
3
|
+
|
4
|
+
import { Application } from "@hotwired/stimulus"
|
5
|
+
|
6
|
+
const application = Application.start()
|
7
|
+
|
8
|
+
// Configure debug mode based on environment
|
9
|
+
const railsEnv = document.body?.dataset?.environment || "production";
|
10
|
+
application.debug = railsEnv === "development"
|
11
|
+
window.Stimulus = application
|
12
|
+
|
13
|
+
// Auto-registration functionality
|
14
|
+
function eagerLoadControllersFrom(context) {
|
15
|
+
const definitions = []
|
16
|
+
for (const path of context.keys()) {
|
17
|
+
const module = context(path)
|
18
|
+
const controller = module.default
|
19
|
+
if (controller && path.match(/[_-]controller\.(js|ts)$/)) {
|
20
|
+
const name = path
|
21
|
+
.replace(/^.*\//, "")
|
22
|
+
.replace(/[_-]controller\.(js|ts)$/, "")
|
23
|
+
.replace(/_/g, "-")
|
24
|
+
definitions.push({ name, module: controller, filename: path })
|
25
|
+
}
|
26
|
+
}
|
27
|
+
return definitions
|
28
|
+
}
|
29
|
+
|
30
|
+
function lazyLoadControllersFrom(context) {
|
31
|
+
return eagerLoadControllersFrom(context)
|
32
|
+
}
|
33
|
+
|
34
|
+
// Export the functions that stimulus-loading typically provides
|
35
|
+
export {
|
36
|
+
application,
|
37
|
+
eagerLoadControllersFrom,
|
38
|
+
lazyLoadControllersFrom
|
39
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// Stimulus loading utilities for Panda CMS
|
2
|
+
// This provides the loading functionality that would normally come from stimulus-rails
|
3
|
+
|
4
|
+
import { Application } from "@hotwired/stimulus"
|
5
|
+
|
6
|
+
const application = Application.start()
|
7
|
+
|
8
|
+
// Configure debug mode based on environment
|
9
|
+
const railsEnv = document.body?.dataset?.environment || "production";
|
10
|
+
application.debug = railsEnv === "development"
|
11
|
+
window.Stimulus = application
|
12
|
+
|
13
|
+
// Auto-registration functionality
|
14
|
+
function eagerLoadControllersFrom(context) {
|
15
|
+
const definitions = []
|
16
|
+
for (const path of context.keys()) {
|
17
|
+
const module = context(path)
|
18
|
+
const controller = module.default
|
19
|
+
if (controller && path.match(/[_-]controller\.(js|ts)$/)) {
|
20
|
+
const name = path
|
21
|
+
.replace(/^.*\//, "")
|
22
|
+
.replace(/[_-]controller\.(js|ts)$/, "")
|
23
|
+
.replace(/_/g, "-")
|
24
|
+
definitions.push({ name, module: controller, filename: path })
|
25
|
+
}
|
26
|
+
}
|
27
|
+
return definitions
|
28
|
+
}
|
29
|
+
|
30
|
+
function lazyLoadControllersFrom(context) {
|
31
|
+
return eagerLoadControllersFrom(context)
|
32
|
+
}
|
33
|
+
|
34
|
+
// Export the functions that stimulus-loading typically provides
|
35
|
+
export {
|
36
|
+
application,
|
37
|
+
eagerLoadControllersFrom,
|
38
|
+
lazyLoadControllersFrom
|
39
|
+
}
|
@@ -1,29 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
5
|
class RecordVisitJob < ApplicationJob
|
4
6
|
queue_as :default
|
5
7
|
|
6
8
|
def perform(
|
7
|
-
|
9
|
+
path:,
|
10
|
+
user_id: nil,
|
11
|
+
redirect_id: nil,
|
12
|
+
page_id: nil,
|
8
13
|
user_agent: nil,
|
9
|
-
referrer: nil,
|
10
14
|
ip_address: nil,
|
11
|
-
|
12
|
-
|
13
|
-
params: [],
|
14
|
-
visited_at: nil,
|
15
|
-
redirect_id: nil
|
15
|
+
referer: nil,
|
16
|
+
params: []
|
16
17
|
)
|
17
18
|
Panda::CMS::Visit.create!(
|
18
|
-
url:
|
19
|
+
url: path,
|
20
|
+
user_id: user_id,
|
21
|
+
redirect_id: redirect_id,
|
22
|
+
page_id: page_id,
|
19
23
|
user_agent: user_agent,
|
20
|
-
referrer: referrer,
|
21
24
|
ip_address: ip_address,
|
22
|
-
|
23
|
-
|
24
|
-
user_id: current_user_id,
|
25
|
-
params: params,
|
26
|
-
visited_at: visited_at
|
25
|
+
referrer: referer, # TODO: Fix the naming of this column
|
26
|
+
params: params
|
27
27
|
)
|
28
28
|
end
|
29
29
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
5
|
class FormMailer < Panda::CMS::ApplicationMailer
|
4
6
|
def notification_email(form:, form_submission:)
|
5
7
|
# TODO: Handle fields named just "name", and "email" better
|
6
8
|
@submission_data = form_submission.data
|
7
|
-
@sender_name = @submission_data["first_name"]
|
9
|
+
@sender_name = "#{@submission_data["first_name"]} #{@submission_data["last_name"]}"
|
8
10
|
@sender_email = @submission_data["email"].to_s
|
9
11
|
|
10
12
|
mail(
|