panda-cms 0.7.4 → 0.8.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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -2
  3. data/Rakefile +2 -0
  4. data/app/components/panda/cms/admin/statistics_component.rb +1 -2
  5. data/app/components/panda/cms/admin/user_activity_component.html.erb +3 -1
  6. data/app/components/panda/cms/admin/user_activity_component.rb +3 -5
  7. data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
  8. data/app/components/panda/cms/admin/user_display_component.rb +2 -2
  9. data/app/components/panda/cms/code_component.rb +8 -4
  10. data/app/components/panda/cms/menu_component.rb +7 -6
  11. data/app/components/panda/cms/page_menu_component.rb +15 -17
  12. data/app/components/panda/cms/rich_text_component.rb +10 -11
  13. data/app/components/panda/cms/text_component.rb +6 -7
  14. data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
  15. data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
  16. data/app/controllers/panda/cms/admin/dashboard_controller.rb +14 -9
  17. data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
  18. data/app/controllers/panda/cms/admin/forms_controller.rb +3 -6
  19. data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
  20. data/app/controllers/panda/cms/admin/pages_controller.rb +9 -8
  21. data/app/controllers/panda/cms/admin/posts_controller.rb +9 -11
  22. data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +32 -25
  23. data/app/controllers/panda/cms/admin/settings_controller.rb +13 -10
  24. data/app/controllers/panda/cms/application_controller.rb +19 -6
  25. data/app/controllers/panda/cms/errors_controller.rb +5 -2
  26. data/app/controllers/panda/cms/form_submissions_controller.rb +2 -0
  27. data/app/controllers/panda/cms/pages_controller.rb +34 -31
  28. data/app/controllers/panda/cms/posts_controller.rb +2 -0
  29. data/app/helpers/panda/cms/admin/files_helper.rb +5 -1
  30. data/app/helpers/panda/cms/admin/pages_helper.rb +5 -1
  31. data/app/helpers/panda/cms/application_helper.rb +3 -3
  32. data/app/helpers/panda/cms/asset_helper.rb +195 -0
  33. data/app/helpers/panda/cms/pages_helper.rb +2 -0
  34. data/app/helpers/panda/cms/posts_helper.rb +2 -0
  35. data/app/helpers/panda/cms/theme_helper.rb +2 -0
  36. data/app/javascript/panda/cms/application_panda_cms.js +2 -34
  37. data/app/javascript/panda/cms/controllers/editor_form_controller.js +59 -6
  38. data/app/javascript/panda/cms/controllers/index.js +8 -24
  39. data/app/javascript/panda/cms/stimulus-loading.js +39 -0
  40. data/app/javascript/panda_cms/stimulus-loading.js +39 -0
  41. data/app/jobs/panda/cms/application_job.rb +2 -0
  42. data/app/jobs/panda/cms/record_visit_job.rb +2 -0
  43. data/app/mailers/panda/cms/application_mailer.rb +2 -0
  44. data/app/mailers/panda/cms/form_mailer.rb +3 -1
  45. data/app/models/panda/cms/application_record.rb +2 -0
  46. data/app/models/panda/cms/block.rb +4 -1
  47. data/app/models/panda/cms/block_content.rb +3 -1
  48. data/app/models/panda/cms/current.rb +5 -12
  49. data/app/models/panda/cms/form.rb +2 -0
  50. data/app/models/panda/cms/form_submission.rb +2 -0
  51. data/app/models/panda/cms/menu.rb +12 -9
  52. data/app/models/panda/cms/menu_item.rb +10 -6
  53. data/app/models/panda/cms/page.rb +14 -12
  54. data/app/models/panda/cms/post.rb +12 -8
  55. data/app/models/panda/cms/redirect.rb +6 -3
  56. data/app/models/panda/cms/template.rb +12 -7
  57. data/app/models/panda/cms/visit.rb +3 -1
  58. data/app/models/panda/social/instagram_post.rb +2 -0
  59. data/app/services/panda/social/instagram_feed_service.rb +3 -1
  60. data/app/views/layouts/different_page.html.erb +6 -0
  61. data/app/views/layouts/homepage.html.erb +37 -0
  62. data/app/views/layouts/page.html.erb +18 -0
  63. data/app/views/layouts/panda/cms/application.html.erb +2 -1
  64. data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
  65. data/app/views/panda/cms/admin/files/index.html.erb +1 -1
  66. data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
  67. data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
  68. data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
  69. data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
  70. data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
  71. data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
  72. data/app/views/panda/cms/admin/pages/new.html.erb +16 -10
  73. data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
  74. data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
  75. data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
  76. data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
  77. data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
  78. data/app/views/panda/cms/admin/settings/index.html.erb +4 -4
  79. data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
  80. data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
  81. data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
  82. data/app/views/panda/cms/shared/_header.html.erb +10 -2
  83. data/app/views/panda/cms/shared/_importmap.html.erb +1 -1
  84. data/app/views/shared/_footer.html.erb +3 -0
  85. data/app/views/shared/_header.html.erb +11 -0
  86. data/config/importmap.rb +2 -0
  87. data/config/initializers/inflections.rb +2 -0
  88. data/config/initializers/panda/cms/form_errors.rb +20 -21
  89. data/config/initializers/panda/cms/healthcheck_log_silencer.rb +2 -0
  90. data/config/initializers/panda/cms.rb +8 -3
  91. data/config/initializers/zeitwork.rb +2 -0
  92. data/config/puma/test.rb +3 -1
  93. data/config/routes.rb +11 -19
  94. data/db/migrate/20240205223709_create_panda_cms_pages.rb +2 -0
  95. data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +2 -0
  96. data/db/migrate/20240303002805_create_panda_cms_templates.rb +4 -1
  97. data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +2 -0
  98. data/db/migrate/20240303022441_create_panda_cms_blocks.rb +4 -1
  99. data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +2 -0
  100. data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +2 -0
  101. data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +2 -0
  102. data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +2 -0
  103. data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +2 -0
  104. data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +7 -5
  105. data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +2 -0
  106. data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +3 -1
  107. data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +2 -0
  108. data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +2 -0
  109. data/db/migrate/20240317010532_create_panda_cms_users.rb +2 -0
  110. data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +2 -0
  111. data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +2 -0
  112. data/db/migrate/20240317214827_create_panda_cms_redirects.rb +2 -0
  113. data/db/migrate/20240317230622_create_panda_cms_visits.rb +2 -0
  114. data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +5 -2
  115. data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +2 -0
  116. data/db/migrate/20240701225422_add_service_name_to_active_storage_blobs.active_storage.rb +8 -6
  117. data/db/migrate/20240701225423_create_active_storage_variant_records.active_storage.rb +2 -0
  118. data/db/migrate/20240701225424_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +2 -0
  119. data/db/migrate/20240804235210_create_panda_cms_forms.rb +2 -0
  120. data/db/migrate/20240805013612_create_panda_cms_form_submissions.rb +2 -0
  121. data/db/migrate/20240805121123_create_panda_cms_posts.rb +3 -1
  122. data/db/migrate/20240805123104_create_panda_cms_post_versions.rb +2 -0
  123. data/db/migrate/20240806112735_fix_panda_cms_visits_column_names.rb +2 -0
  124. data/db/migrate/20240806204412_add_completion_path_to_panda_cms_forms.rb +2 -0
  125. data/db/migrate/20240820081917_change_form_submissions_to_submission_count.rb +2 -0
  126. data/db/migrate/20240923234535_add_depth_to_panda_cms_menus.rb +6 -4
  127. data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +2 -0
  128. data/db/migrate/20241119214548_convert_post_content_to_editor_js.rb +2 -0
  129. data/db/migrate/20241120000419_remove_post_tag_references.rb +2 -0
  130. data/db/migrate/20241120110943_add_editor_js_to_posts.rb +2 -0
  131. data/db/migrate/20241120113859_add_cached_content_to_panda_cms_posts.rb +2 -0
  132. data/db/migrate/20241123234140_remove_post_tag_id_from_posts.rb +2 -0
  133. data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +5 -1
  134. data/db/migrate/20250120235542_remove_paper_trail.rb +5 -4
  135. data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +4 -0
  136. data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
  137. data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -0
  138. data/db/seeds.rb +2 -0
  139. data/lib/generators/panda/cms/install_generator.rb +2 -0
  140. data/lib/panda/cms/asset_loader.rb +390 -0
  141. data/lib/panda/cms/bulk_editor.rb +7 -3
  142. data/lib/panda/cms/demo_site_generator.rb +2 -0
  143. data/lib/panda/cms/engine.rb +57 -116
  144. data/lib/panda/cms/exceptions_app.rb +2 -0
  145. data/lib/panda/cms/railtie.rb +2 -0
  146. data/lib/panda/cms/slug.rb +3 -1
  147. data/lib/panda-cms/version.rb +3 -1
  148. data/lib/panda-cms.rb +54 -42
  149. data/lib/tasks/assets.rake +587 -0
  150. data/lib/tasks/panda/cms/install.rake +2 -0
  151. data/lib/tasks/panda/cms/migrations.rake +13 -0
  152. data/lib/tasks/panda/social/instagram.rake +2 -0
  153. data/lib/tasks/panda_cms.rake +3 -30
  154. data/public/panda-cms-assets/manifest.json +20 -0
  155. data/public/panda-cms-assets/panda-cms-0.7.4.css +26 -0
  156. data/public/panda-cms-assets/panda-cms-0.7.4.js +150 -0
  157. metadata +186 -49
  158. data/app/builders/panda/cms/form_builder.rb +0 -217
  159. data/app/components/panda/cms/admin/button_component.rb +0 -70
  160. data/app/components/panda/cms/admin/container_component.rb +0 -13
  161. data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
  162. data/app/components/panda/cms/admin/heading_component.rb +0 -45
  163. data/app/components/panda/cms/admin/panel_component.rb +0 -13
  164. data/app/components/panda/cms/admin/table_component.rb +0 -46
  165. data/app/components/panda/cms/admin/tag_component.rb +0 -35
  166. data/app/constraints/panda/cms/admin_constraint.rb +0 -18
  167. data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -43
  168. data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -94
  169. data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -9
  170. data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
  171. data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
  172. data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
  173. data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
  174. data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
  175. data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
  176. data/app/models/panda/cms/breadcrumb.rb +0 -12
  177. data/app/models/panda/cms/user.rb +0 -31
  178. data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -193
  179. data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
  180. data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
  181. data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -5
  182. data/lib/panda/cms/editor_js/blocks/alert.rb +0 -34
  183. data/lib/panda/cms/editor_js/blocks/base.rb +0 -33
  184. data/lib/panda/cms/editor_js/blocks/header.rb +0 -15
  185. data/lib/panda/cms/editor_js/blocks/image.rb +0 -36
  186. data/lib/panda/cms/editor_js/blocks/list.rb +0 -32
  187. data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -15
  188. data/lib/panda/cms/editor_js/blocks/quote.rb +0 -41
  189. data/lib/panda/cms/editor_js/blocks/table.rb +0 -50
  190. data/lib/panda/cms/editor_js/renderer.rb +0 -124
  191. data/lib/panda/cms/editor_js.rb +0 -16
  192. data/lib/panda/cms/editor_js_content.rb +0 -55
@@ -1,36 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
- class Admin::Settings::BulkEditorController < ApplicationController
4
- before_action :set_initial_breadcrumb, only: %i[new]
5
+ module Admin
6
+ module Settings
7
+ class BulkEditorController < ::Panda::CMS::Admin::BaseController
8
+ before_action :set_initial_breadcrumb, only: %i[new]
5
9
 
6
- def new
7
- @json_data = BulkEditor.export
8
- end
10
+ def new
11
+ @json_data = BulkEditor.export
12
+ end
9
13
 
10
- def create
11
- begin
12
- debug_output = BulkEditor.import(params[:site_content])
13
- rescue JSON::ParserError
14
- redirect_to admin_settings_bulk_editor_path, flash: {error: "Error parsing content; are you sure this update is valid? Reverting..."}
15
- return
16
- end
14
+ def create
15
+ begin
16
+ debug_output = BulkEditor.import(params[:site_content])
17
+ rescue JSON::ParserError
18
+ redirect_to admin_cms_settings_bulk_editor_path,
19
+ flash: {error: "Error parsing content; are you sure this update is valid? Reverting..."}
20
+ return
21
+ end
17
22
 
18
- # Grab the latest content back so it's all formatted properly
19
- @json_data = BulkEditor.export
23
+ # Grab the latest content back so it's all formatted properly
24
+ @json_data = BulkEditor.export
20
25
 
21
- if debug_output[:error].empty? && debug_output[:warning].empty? && debug_output[:success].empty?
22
- redirect_to admin_settings_bulk_editor_path, flash: {success: "No changes were found!"}
23
- else
24
- @debug = debug_output
25
- render :new, flash: {warning: "Please review the output below for more information."}
26
- end
27
- end
26
+ if debug_output[:error].empty? && debug_output[:warning].empty? && debug_output[:success].empty?
27
+ redirect_to admin_cms_settings_bulk_editor_path, flash: {success: "No changes were found!"}
28
+ else
29
+ @debug = debug_output
30
+ render :new, flash: {warning: "Please review the output below for more information."}
31
+ end
32
+ end
28
33
 
29
- private
34
+ private
30
35
 
31
- def set_initial_breadcrumb
32
- add_breadcrumb "Settings", admin_settings_path
33
- add_breadcrumb "Bulk Editor", "#"
36
+ def set_initial_breadcrumb
37
+ add_breadcrumb "Settings", admin_cms_settings_path
38
+ add_breadcrumb "Bulk Editor", "#"
39
+ end
40
+ end
34
41
  end
35
42
  end
36
43
  end
@@ -1,19 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
- class Admin::SettingsController < ApplicationController
4
- before_action :set_initial_breadcrumb, only: %i[index show]
5
- before_action :authenticate_admin_user!
5
+ module Admin
6
+ class SettingsController < ::Panda::CMS::Admin::BaseController
7
+ before_action :set_initial_breadcrumb, only: %i[index show]
6
8
 
7
- def index
8
- end
9
+ def index
10
+ end
9
11
 
10
- def show
11
- end
12
+ def show
13
+ end
12
14
 
13
- private
15
+ private
14
16
 
15
- def set_initial_breadcrumb
16
- add_breadcrumb "Settings", admin_settings_path
17
+ def set_initial_breadcrumb
18
+ add_breadcrumb "Settings", admin_cms_settings_path
19
+ end
17
20
  end
18
21
  end
19
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  class ApplicationController < ::ActionController::Base
@@ -26,31 +28,42 @@ module Panda
26
28
  # Set the current request details
27
29
  # @return [void]
28
30
  def set_current_request_details
31
+ # Set Core current attributes
32
+ Panda::Core::Current.request_id = request.uuid
33
+ Panda::Core::Current.user_agent = request.user_agent
34
+ Panda::Core::Current.ip_address = request.ip
35
+ Panda::Core::Current.root = request.base_url
36
+ Panda::Core::Current.user ||= Panda::Core::User.find_by(id: session[:user_id]) if session[:user_id]
37
+
38
+ # Set CMS current attributes (inherits from Core so has access to all Core attributes)
29
39
  Panda::CMS::Current.request_id = request.uuid
30
40
  Panda::CMS::Current.user_agent = request.user_agent
31
41
  Panda::CMS::Current.ip_address = request.ip
32
42
  Panda::CMS::Current.root = request.base_url
43
+ Panda::CMS::Current.user = Panda::Core::Current.user
33
44
  Panda::CMS::Current.page = nil
34
- Panda::CMS::Current.user ||= User.find_by(id: session[:user_id]) if session[:user_id]
35
45
 
36
- Panda::CMS.config.url ||= Panda::CMS::Current.root
46
+ Panda::CMS.config.url ||= Panda::Core::Current.root
37
47
  end
38
48
 
39
49
  def authenticate_user!
40
- redirect_to root_path, flash: {error: "Please login to view this!"} unless user_signed_in?
50
+ redirect_to main_app.root_path, flash: {error: "Please login to view this!"} unless user_signed_in?
41
51
  end
42
52
 
43
53
  def authenticate_admin_user!
44
- redirect_to root_path, flash: {error: "Please login to view this!"} unless user_signed_in? && current_user.admin?
54
+ return if user_signed_in? && current_user.admin?
55
+
56
+ redirect_to panda_core.admin_login_path,
57
+ flash: {error: "Please login to view this!"}
45
58
  end
46
59
 
47
60
  # Required for paper_trail and seems as good as convention these days
48
61
  def current_user
49
- Panda::CMS::Current.user
62
+ Panda::Core::Current.user
50
63
  end
51
64
 
52
65
  def user_signed_in?
53
- !!Panda::CMS::Current.user
66
+ !!Panda::Core::Current.user
54
67
  end
55
68
  end
56
69
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  class ErrorsController < ApplicationController
@@ -5,7 +7,8 @@ module Panda
5
7
 
6
8
  def show
7
9
  exception = request.env["action_dispatch.exception"]
8
- status_code = exception.try(:status_code) || ActionDispatch::ExceptionWrapper.new(request.env, exception).status_code
10
+ status_code = exception.try(:status_code) || ActionDispatch::ExceptionWrapper.new(request.env,
11
+ exception).status_code
9
12
 
10
13
  render view_for_code(status_code), status: status_code
11
14
  end
@@ -17,7 +20,7 @@ module Panda
17
20
  private
18
21
 
19
22
  def view_for_code(code)
20
- supported_error_codes.fetch(code) { "404" }
23
+ supported_error_codes.fetch(code, "404")
21
24
  end
22
25
 
23
26
  def supported_error_codes
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  class FormSubmissionsController < ApplicationController
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  class PagesController < ApplicationController
4
6
  include ActionView::Helpers::TagHelper
5
7
 
6
- before_action :check_login_required, only: [:root, :show]
7
- before_action :handle_redirects, only: [:root, :show]
8
- after_action :record_visit, only: [:root, :show], unless: :ignore_visit?
8
+ before_action :check_login_required, only: %i[root show]
9
+ before_action :handle_redirects, only: %i[root show]
10
+ after_action :record_visit, only: %i[root show], unless: :ignore_visit?
9
11
 
10
12
  def root
11
13
  params[:path] = ""
@@ -14,15 +16,13 @@ module Panda
14
16
 
15
17
  def show
16
18
  page = if @overrides&.dig(:page_path_match)
17
- Panda::CMS::Page.find_by(path: @overrides.dig(:page_path_match))
19
+ Panda::CMS::Page.find_by(path: @overrides[:page_path_match])
18
20
  else
19
- Panda::CMS::Page.find_by(path: "/" + params[:path].to_s)
21
+ Panda::CMS::Page.find_by(path: "/#{params[:path]}")
20
22
  end
21
23
 
22
24
  Panda::CMS::Current.page = page || Panda::CMS::Page.find_by(path: "/404")
23
- if @overrides
24
- Panda::CMS::Current.page.title = @overrides&.dig(:title) || page.title
25
- end
25
+ Panda::CMS::Current.page.title = @overrides&.dig(:title) || page.title if @overrides
26
26
 
27
27
  layout = page&.template&.file_path
28
28
 
@@ -42,36 +42,39 @@ module Panda
42
42
  private
43
43
 
44
44
  def handle_redirects
45
- current_path = "/" + params[:path].to_s
45
+ current_path = "/#{params[:path]}"
46
46
  redirect = Panda::CMS::Redirect.find_by(origin_path: current_path)
47
47
 
48
- if redirect
49
- redirect.increment!(:visits)
48
+ return unless redirect
50
49
 
51
- # Check if the destination is also a redirect
52
- next_redirect = Panda::CMS::Redirect.find_by(origin_path: redirect.destination_path)
53
- if next_redirect
54
- next_redirect.increment!(:visits)
55
- redirect_to next_redirect.destination_path, status: redirect.status_code and return
56
- end
50
+ redirect.increment!(:visits)
57
51
 
58
- redirect_to redirect.destination_path, status: redirect.status_code and return
52
+ # Check if the destination is also a redirect
53
+ next_redirect = Panda::CMS::Redirect.find_by(origin_path: redirect.destination_path)
54
+ if next_redirect
55
+ next_redirect.increment!(:visits)
56
+ redirect_to next_redirect.destination_path, status: redirect.status_code and return
59
57
  end
58
+
59
+ redirect_to redirect.destination_path, status: redirect.status_code and return
60
60
  end
61
61
 
62
62
  def check_login_required
63
- if Panda::CMS.config.require_login_to_view && !user_signed_in?
64
- redirect_to panda_cms_maintenance_path and return
65
- end
63
+ return unless Panda::CMS.config.require_login_to_view && !user_signed_in?
64
+
65
+ redirect_to panda_cms_maintenance_path and return
66
66
  end
67
67
 
68
68
  def ignore_visit?
69
69
  # Ignore visits from bots (TODO: make this configurable)
70
70
  return true if /bot/i.match?(request.user_agent)
71
71
  # Ignore visits from Honeybadger
72
- return true if request.headers.to_h.key?("Honeybadger-Token") || request.user_agent == "Honeybadger Uptime Check"
72
+ if request.headers.to_h.key?("Honeybadger-Token") || request.user_agent == "Honeybadger Uptime Check"
73
+ return true
74
+ end
73
75
  # Ignore visits where we're asking for PHP files
74
76
  return true if request.path.ends_with?(".php")
77
+
75
78
  # Otherwise, record the visit
76
79
  false
77
80
  end
@@ -79,9 +82,9 @@ module Panda
79
82
  def record_visit
80
83
  RecordVisitJob.perform_later(
81
84
  path: request.path,
82
- user_id: Current.user&.id,
85
+ user_id: Panda::Core::Current.user&.id,
83
86
  redirect_id: @redirect&.id,
84
- page_id: Current.page&.id,
87
+ page_id: Panda::CMS::Current.page&.id,
85
88
  user_agent: request.user_agent,
86
89
  ip_address: request.remote_ip,
87
90
  referer: request.referer, # TODO: Fix the naming of this column
@@ -90,13 +93,13 @@ module Panda
90
93
  end
91
94
 
92
95
  def create_redirect_if_path_changed
93
- if path_changed? && path_was.present?
94
- Panda::CMS::Redirect.create!(
95
- origin_path: path_was,
96
- destination_path: path,
97
- status_code: 301
98
- )
99
- end
96
+ return unless path_changed? && path_was.present?
97
+
98
+ Panda::CMS::Redirect.create!(
99
+ origin_path: path_was,
100
+ destination_path: path,
101
+ status_code: 301
102
+ )
100
103
  end
101
104
  end
102
105
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  class PostsController < ApplicationController
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
- module Admin::FilesHelper
5
+ module Admin
6
+ module FilesHelper
7
+ end
4
8
  end
5
9
  end
6
10
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
- module Admin::PagesHelper
5
+ module Admin
6
+ module PagesHelper
7
+ end
4
8
  end
5
9
  end
6
10
  end
@@ -24,8 +24,8 @@ module Panda
24
24
  end
25
25
 
26
26
  def panda_cms_editor
27
- if Current.user&.admin
28
- content_tag(:a, "🐼", href: edit_admin_page_url(Current.page), class: "text-3xl inline absolute right-2 top-2")
27
+ if Panda::Core::Current.user&.admin
28
+ content_tag(:a, "🐼", href: edit_admin_cms_page_url(Panda::CMS::Current.page), class: "text-3xl inline absolute right-2 top-2")
29
29
  end
30
30
  end
31
31
 
@@ -45,7 +45,7 @@ module Panda
45
45
  end
46
46
 
47
47
  def panda_cms_form_with(**options, &)
48
- options[:builder] = Panda::CMS::FormBuilder
48
+ options[:builder] = Panda::Core::FormBuilder
49
49
  options[:class] = ["block visible p-6 bg-mid/5 rounded-lg border-mid border", options[:class]].compact.join(" ")
50
50
  form_with(**options, &)
51
51
  end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "panda/core"
4
+
5
+ module Panda
6
+ module CMS
7
+ module AssetHelper
8
+ include Panda::Core::AssetHelper if defined?(Panda::Core::AssetHelper)
9
+ # Include Panda CMS JavaScript and CSS assets
10
+ # Automatically chooses between GitHub-hosted assets (production)
11
+ # and local development assets
12
+ def panda_cms_assets
13
+ tags = []
14
+
15
+ # Include Core assets first (if Core is available)
16
+ if defined?(Panda::Core::AssetHelper)
17
+ tags << panda_core_assets
18
+ end
19
+
20
+ # Then include CMS-specific assets
21
+ tags << Panda::CMS::AssetLoader.asset_tags
22
+
23
+ tags.join("\n").html_safe
24
+ end
25
+
26
+ # Include only Panda CMS JavaScript
27
+ def panda_cms_javascript
28
+ js_url = Panda::CMS::AssetLoader.javascript_url
29
+ return "" unless js_url
30
+
31
+ if Panda::CMS::AssetLoader.use_github_assets?
32
+ # GitHub-hosted assets with integrity check
33
+ version = Panda::CMS::AssetLoader.send(:asset_version)
34
+ integrity = asset_integrity(version, "panda-cms-#{version}.js")
35
+
36
+ tag_options = {
37
+ src: js_url
38
+ }
39
+ # In CI environment, don't use defer to ensure immediate execution
40
+ tag_options[:defer] = true unless ENV["GITHUB_ACTIONS"] == "true"
41
+ # Standalone bundles should NOT use type: "module" - they're regular scripts
42
+ # Only use type: "module" for importmap/ES module assets
43
+ if !js_url.include?("panda-cms-assets")
44
+ tag_options[:type] = "module"
45
+ end
46
+ tag_options[:integrity] = integrity if integrity
47
+ tag_options[:crossorigin] = "anonymous" if integrity
48
+
49
+ content_tag(:script, "", tag_options)
50
+ elsif js_url.include?("panda-cms-assets")
51
+ # Development assets - check if it's a standalone bundle or importmap
52
+ defer_option = (ENV["GITHUB_ACTIONS"] == "true") ? {} : {defer: true}
53
+ javascript_include_tag(js_url, **defer_option)
54
+ # Standalone bundle - don't use type: "module"
55
+ else
56
+ # Importmap asset - use type: "module"
57
+ defer_option = (ENV["GITHUB_ACTIONS"] == "true") ? {} : {defer: true}
58
+ javascript_include_tag(js_url, type: "module", **defer_option)
59
+ end
60
+ end
61
+
62
+ # Include only Panda CMS CSS
63
+ def panda_cms_stylesheet
64
+ css_url = Panda::CMS::AssetLoader.css_url
65
+ return "" unless css_url
66
+
67
+ if Panda::CMS::AssetLoader.use_github_assets?
68
+ # GitHub-hosted assets with integrity check
69
+ version = Panda::CMS::VERSION
70
+ integrity = asset_integrity(version, "panda-cms-#{version}.css")
71
+
72
+ tag_options = {
73
+ rel: "stylesheet",
74
+ href: css_url
75
+ }
76
+ tag_options[:integrity] = integrity if integrity
77
+ tag_options[:crossorigin] = "anonymous" if integrity
78
+
79
+ tag(:link, tag_options)
80
+ else
81
+ # Development assets
82
+ stylesheet_link_tag(css_url)
83
+ end
84
+ end
85
+
86
+ # Get the current Panda CMS version
87
+ def panda_cms_version
88
+ Panda::CMS::VERSION
89
+ end
90
+
91
+ # Check if using GitHub-hosted assets
92
+ def using_github_assets?
93
+ Panda::CMS::AssetLoader.use_github_assets?
94
+ end
95
+
96
+ # Download and cache assets if needed
97
+ # Call this in an initializer or controller to pre-cache assets
98
+ def ensure_panda_cms_assets!
99
+ Panda::CMS::AssetLoader.ensure_assets_available!
100
+ end
101
+
102
+ # Debug information about asset loading
103
+ def panda_cms_asset_debug
104
+ return "" unless Rails.env.development? || Rails.env.test?
105
+
106
+ version = Panda::CMS::VERSION
107
+ js_url = Panda::CMS::AssetLoader.javascript_url
108
+ css_url = Panda::CMS::AssetLoader.css_url
109
+ using_github = Panda::CMS::AssetLoader.use_github_assets?
110
+ compiled_available = Panda::CMS::AssetLoader.send(:compiled_assets_available?)
111
+
112
+ # Additional CI debugging
113
+ asset_file_exists = js_url && File.exist?(Rails.root.join("public#{js_url}"))
114
+ ci_env = ENV["GITHUB_ACTIONS"] == "true"
115
+
116
+ # Check what script tag will be generated
117
+ script_tag_preview = if using_github
118
+ tag_options = {src: js_url}
119
+ tag_options[:defer] = true unless ci_env
120
+ if !js_url.include?("panda-cms-assets")
121
+ tag_options[:type] = "module"
122
+ end
123
+ "Script tag: <script#{tag_options.map { |k, v| (v == true) ? " #{k}" : " #{k}=\"#{v}\"" }.join}></script>"
124
+ else
125
+ "Using development assets"
126
+ end
127
+
128
+ debug_info = [
129
+ "<!-- Panda CMS Asset Debug Info -->",
130
+ "<!-- Version: #{version} -->",
131
+ "<!-- Using GitHub assets: #{using_github} -->",
132
+ "<!-- Compiled assets available: #{compiled_available} -->",
133
+ "<!-- JavaScript URL: #{js_url} -->",
134
+ "<!-- CSS URL: #{css_url || "none"} -->",
135
+ "<!-- Rails environment: #{Rails.env} -->",
136
+ "<!-- Asset file exists: #{asset_file_exists} -->",
137
+ "<!-- Rails root: #{Rails.root} -->",
138
+ "<!-- CI environment: #{ci_env} -->",
139
+ "<!-- #{script_tag_preview} -->",
140
+ "<!-- Params embed_id: #{params[:embed_id] if respond_to?(:params)} -->",
141
+ "<!-- Compiled at: #{Time.now.utc.iso8601} -->"
142
+ ]
143
+
144
+ debug_info.join("\n").html_safe
145
+ end
146
+
147
+ # Initialize Panda CMS Stimulus application
148
+ # Call this after the asset tags to ensure proper initialization
149
+ def panda_cms_stimulus_init
150
+ javascript_tag(<<~JS, type: "module")
151
+ // Initialize Panda CMS Stimulus application
152
+ document.addEventListener('DOMContentLoaded', function() {
153
+ if (window.pandaCmsStimulus) {
154
+ console.debug('[Panda CMS] Stimulus application initialized');
155
+
156
+ // Set debug mode based on Rails environment
157
+ const railsEnv = document.body?.dataset?.environment || 'production';
158
+ window.pandaCmsStimulus.debug = (railsEnv === 'development');
159
+
160
+ // Trigger a custom event to signal Panda CMS is ready
161
+ document.dispatchEvent(new CustomEvent('panda-cms:ready', {
162
+ detail: {
163
+ version: '#{Panda::CMS::VERSION}',
164
+ usingGitHubAssets: #{Panda::CMS::AssetLoader.use_github_assets?}
165
+ }
166
+ }));
167
+ } else {
168
+ console.warn('[Panda CMS] Stimulus application not found. Assets may not have loaded properly.');
169
+ }
170
+ });
171
+ JS
172
+ end
173
+
174
+ # Complete asset loading with initialization
175
+ # This is the recommended way to include all Panda CMS assets
176
+ def panda_cms_complete_assets
177
+ [
178
+ panda_cms_asset_debug,
179
+ panda_cms_assets,
180
+ panda_cms_stimulus_init,
181
+ # Add immediate JavaScript execution test for CI debugging
182
+ (Rails.env.test? ? javascript_tag("window.pandaCmsInlineTest = true; console.log('[Panda CMS] Inline script executed');") : "")
183
+ ].join("\n").html_safe
184
+ end
185
+
186
+ private
187
+
188
+ def asset_integrity(version, filename)
189
+ Panda::CMS::AssetLoader.send(:asset_integrity, version, filename)
190
+ rescue
191
+ nil
192
+ end
193
+ end
194
+ end
195
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  module PagesHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  module PostsHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Panda
2
4
  module CMS
3
5
  module ThemeHelper
@@ -3,37 +3,5 @@ console.debug("[Panda CMS] Controllers loading...");
3
3
  import "controllers"
4
4
  console.debug("[Panda CMS] Controllers loaded...");
5
5
 
6
- // Import editor resources
7
- import { EDITOR_JS_RESOURCES, EDITOR_JS_CSS } from "panda/cms/editor/editor_js_config"
8
- import { ResourceLoader } from "panda/cms/editor/resource_loader"
9
-
10
- // Function to load editor resources
11
- async function loadEditorResources() {
12
- console.debug("[Panda CMS] Loading editor resources...");
13
- try {
14
- // First load EditorJS core
15
- const editorCore = EDITOR_JS_RESOURCES[0]
16
- await ResourceLoader.loadScript(document, document.head, editorCore)
17
-
18
- // Then load all tools in parallel
19
- const toolLoads = EDITOR_JS_RESOURCES.slice(1).map(async (resource) => {
20
- await ResourceLoader.loadScript(document, document.head, resource)
21
- })
22
-
23
- // Load CSS directly since it's a string, not an array
24
- await ResourceLoader.embedCSS(document, document.head, EDITOR_JS_CSS)
25
-
26
- // Wait for all resources to load
27
- await Promise.all(toolLoads)
28
- console.debug("[Panda CMS] Editor resources loaded");
29
-
30
- // Dispatch a custom event when resources are loaded
31
- document.dispatchEvent(new CustomEvent('editorjs:loaded'))
32
- } catch (error) {
33
- console.error("[Panda CMS] Error loading editor resources:", error);
34
- }
35
- }
36
-
37
- // Load resources on both initial page load and Turbo cache restore
38
- document.addEventListener('turbo:load', loadEditorResources);
39
- document.addEventListener('turbo:render', loadEditorResources);
6
+ // Editor resources are now handled by panda-editor gem
7
+ // The panda-editor gem will load its own resources when needed