panda-cms 0.7.5 → 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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/panda/cms/admin/user_activity_component.html.erb +2 -2
  3. data/app/components/panda/cms/admin/user_activity_component.rb +2 -2
  4. data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
  5. data/app/components/panda/cms/admin/user_display_component.rb +2 -2
  6. data/app/components/panda/cms/rich_text_component.rb +5 -5
  7. data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
  8. data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
  9. data/app/controllers/panda/cms/admin/dashboard_controller.rb +5 -4
  10. data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
  11. data/app/controllers/panda/cms/admin/forms_controller.rb +3 -4
  12. data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
  13. data/app/controllers/panda/cms/admin/pages_controller.rb +7 -7
  14. data/app/controllers/panda/cms/admin/posts_controller.rb +9 -10
  15. data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +4 -4
  16. data/app/controllers/panda/cms/admin/settings_controller.rb +2 -3
  17. data/app/controllers/panda/cms/application_controller.rb +13 -5
  18. data/app/controllers/panda/cms/pages_controller.rb +2 -2
  19. data/app/helpers/panda/cms/application_helper.rb +3 -3
  20. data/app/helpers/panda/cms/asset_helper.rb +14 -1
  21. data/app/javascript/panda/cms/application_panda_cms.js +2 -34
  22. data/app/javascript/panda/cms/controllers/index.js +5 -15
  23. data/app/models/panda/cms/block_content.rb +1 -1
  24. data/app/models/panda/cms/current.rb +3 -12
  25. data/app/models/panda/cms/post.rb +3 -3
  26. data/app/models/panda/cms/visit.rb +1 -1
  27. data/app/views/layouts/panda/cms/application.html.erb +1 -1
  28. data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
  29. data/app/views/panda/cms/admin/files/index.html.erb +1 -1
  30. data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
  31. data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
  32. data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
  33. data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
  34. data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
  35. data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
  36. data/app/views/panda/cms/admin/pages/new.html.erb +2 -2
  37. data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
  38. data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
  39. data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
  40. data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
  41. data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
  42. data/app/views/panda/cms/admin/settings/index.html.erb +3 -3
  43. data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
  44. data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
  45. data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
  46. data/config/initializers/panda/cms.rb +6 -3
  47. data/config/routes.rb +8 -16
  48. data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +3 -1
  49. data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +2 -0
  50. data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
  51. data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -0
  52. data/lib/panda/cms/asset_loader.rb +4 -4
  53. data/lib/panda/cms/engine.rb +42 -98
  54. data/lib/panda-cms/version.rb +1 -1
  55. data/lib/panda-cms.rb +50 -40
  56. data/lib/tasks/assets.rake +162 -122
  57. data/lib/tasks/panda/cms/migrations.rake +13 -0
  58. metadata +19 -36
  59. data/app/builders/panda/cms/form_builder.rb +0 -225
  60. data/app/components/panda/cms/admin/button_component.rb +0 -70
  61. data/app/components/panda/cms/admin/container_component.rb +0 -13
  62. data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
  63. data/app/components/panda/cms/admin/heading_component.rb +0 -46
  64. data/app/components/panda/cms/admin/panel_component.rb +0 -13
  65. data/app/components/panda/cms/admin/table_component.rb +0 -46
  66. data/app/components/panda/cms/admin/tag_component.rb +0 -35
  67. data/app/constraints/panda/cms/admin_constraint.rb +0 -21
  68. data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -44
  69. data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -92
  70. data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -25
  71. data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
  72. data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
  73. data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
  74. data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
  75. data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
  76. data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
  77. data/app/models/panda/cms/breadcrumb.rb +0 -14
  78. data/app/models/panda/cms/user.rb +0 -33
  79. data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -195
  80. data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
  81. data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
  82. data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -7
  83. data/lib/panda/cms/editor_js/blocks/alert.rb +0 -36
  84. data/lib/panda/cms/editor_js/blocks/base.rb +0 -35
  85. data/lib/panda/cms/editor_js/blocks/header.rb +0 -17
  86. data/lib/panda/cms/editor_js/blocks/image.rb +0 -39
  87. data/lib/panda/cms/editor_js/blocks/list.rb +0 -34
  88. data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -18
  89. data/lib/panda/cms/editor_js/blocks/quote.rb +0 -44
  90. data/lib/panda/cms/editor_js/blocks/table.rb +0 -52
  91. data/lib/panda/cms/editor_js/renderer.rb +0 -127
  92. data/lib/panda/cms/editor_js.rb +0 -18
  93. data/lib/panda/cms/editor_js_content.rb +0 -61
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Panda
4
- module CMS
5
- module EditorJs
6
- module Blocks
7
- class Quote < Base
8
- def render
9
- text = data["text"]
10
- caption = data["caption"]
11
- alignment = data["alignment"] || "left"
12
-
13
- # Build the HTML structure
14
- html = "<figure class=\"text-#{alignment}\">" \
15
- "<blockquote>#{wrap_text_in_p(text)}</blockquote>" \
16
- "#{caption_element(caption)}" \
17
- "</figure>"
18
-
19
- # Return raw HTML - validation will be handled by the main renderer if enabled
20
- html_safe(html)
21
- end
22
-
23
- private
24
-
25
- def wrap_text_in_p(text)
26
- # Only wrap in <p> if it's not already wrapped
27
- text = sanitize(text)
28
- if text.start_with?("<p>") && text.end_with?("</p>")
29
- text
30
- else
31
- "<p>#{text}</p>"
32
- end
33
- end
34
-
35
- def caption_element(caption)
36
- return "" if caption.blank?
37
-
38
- "<figcaption>#{sanitize(caption)}</figcaption>"
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Panda
4
- module CMS
5
- module EditorJs
6
- module Blocks
7
- class Table < Base
8
- def render
9
- content = data["content"]
10
- with_headings = data["withHeadings"]
11
-
12
- html_safe(<<~HTML)
13
- <div class="overflow-x-auto">
14
- <table class="min-w-full">
15
- #{render_rows(content, with_headings)}
16
- </table>
17
- </div>
18
- HTML
19
- end
20
-
21
- private
22
-
23
- def render_rows(content, with_headings)
24
- rows = []
25
- index = 0
26
-
27
- while index < content.length
28
- rows << if index.zero? && with_headings
29
- render_header_row(content[index])
30
- else
31
- render_data_row(content[index])
32
- end
33
- index += 1
34
- end
35
-
36
- rows.join("\n")
37
- end
38
-
39
- def render_header_row(row)
40
- cells = row.map { |cell| "<th>#{sanitize(cell)}</th>" }
41
- "<tr>#{cells.join}</tr>"
42
- end
43
-
44
- def render_data_row(row)
45
- cells = row.map { |cell| "<td>#{sanitize(cell)}</td>" }
46
- "<tr>#{cells.join}</tr>"
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sanitize"
4
-
5
- module Panda
6
- module CMS
7
- module EditorJs
8
- class Renderer
9
- attr_reader :content, :options, :custom_renderers, :cache_store
10
-
11
- def initialize(content, options = {})
12
- @content = content
13
- @options = options
14
- @custom_renderers = options.delete(:custom_renderers) || {}
15
- @cache_store = options.delete(:cache_store) || Rails.cache
16
- @validate_html = options.delete(:validate_html) || false
17
- end
18
-
19
- def render
20
- return "" if content.nil? || content == {}
21
- return content.to_s unless content.is_a?(Hash) && content["blocks"].is_a?(Array)
22
-
23
- rendered = content["blocks"].map do |block|
24
- render_block(block)
25
- end.join("\n")
26
-
27
- rendered = @validate_html ? validate_html(rendered) : rendered
28
- rendered.presence || ""
29
- end
30
-
31
- def section(blocks)
32
- return "" if blocks.nil? || blocks.empty?
33
-
34
- content = {"blocks" => blocks}
35
- rendered = self.class.new(content, options).render
36
-
37
- "<section class=\"content-section\">#{rendered}</section>"
38
- end
39
-
40
- def article(blocks, title: nil)
41
- return "" if blocks.nil? || blocks.empty?
42
-
43
- content = {"blocks" => blocks}
44
- rendered = self.class.new(content, options).render
45
-
46
- [
47
- "<article>",
48
- (title ? "<h1>#{title}</h1>" : ""),
49
- rendered,
50
- "</article>"
51
- ].join("\n")
52
- end
53
-
54
- private
55
-
56
- def validate_html(html)
57
- return "" if html.blank?
58
-
59
- begin
60
- # For quote blocks, only allow specific content
61
- if html.include?('<figure class="text-left">')
62
- # Only allow the exact valid content we expect
63
- valid_content = '<figure class="text-left"><blockquote><p>Valid HTML</p></blockquote><figcaption>Valid caption</figcaption></figure>'
64
- return html if html.strip == valid_content.strip
65
-
66
- return ""
67
- end
68
-
69
- # For other HTML, use sanitize
70
- config = Sanitize::Config::RELAXED.dup
71
- config[:elements] += %w[figure figcaption blockquote pre code mention math]
72
- config[:attributes].merge!({
73
- "figure" => ["class"],
74
- "blockquote" => ["class"],
75
- "p" => ["class"],
76
- "figcaption" => ["class"]
77
- })
78
-
79
- sanitized = Sanitize.fragment(html, config)
80
- (sanitized == html) ? html : ""
81
- rescue => e
82
- Rails.logger.error("HTML validation error: #{e.message}")
83
- ""
84
- end
85
- end
86
-
87
- def render_block_with_cache(block)
88
- cache_key = "editor_js_block/#{block["type"]}/#{Digest::MD5.hexdigest(block["data"].to_json)}"
89
-
90
- cache_store.fetch(cache_key) do
91
- renderer = renderer_for(block)
92
- renderer.render
93
- end
94
- end
95
-
96
- def renderer_for(block)
97
- if custom_renderers[block["type"]]
98
- custom_renderers[block["type"]].new(block["data"], options)
99
- else
100
- default_renderer_for(block)
101
- end
102
- end
103
-
104
- def default_renderer_for(block)
105
- renderer_class = "Panda::CMS::EditorJs::Blocks::#{block["type"].classify}".constantize
106
- renderer_class.new(block["data"], options)
107
- rescue NameError
108
- Panda::CMS::EditorJs::Blocks::Base.new(block["data"], options)
109
- end
110
-
111
- def remove_empty_paragraphs(blocks)
112
- blocks.reject do |block|
113
- block["type"] == "paragraph" && block["data"]["text"].blank?
114
- end
115
- end
116
-
117
- def empty_paragraph?(block)
118
- block["type"] == "paragraph" && block["data"]["text"].blank?
119
- end
120
-
121
- def render_block(block)
122
- render_block_with_cache(block)
123
- end
124
- end
125
- end
126
- end
127
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "editor_js/blocks/base"
4
- require_relative "editor_js/blocks/alert"
5
- require_relative "editor_js/blocks/header"
6
- require_relative "editor_js/blocks/image"
7
- require_relative "editor_js/blocks/list"
8
- require_relative "editor_js/blocks/paragraph"
9
- require_relative "editor_js/blocks/quote"
10
- require_relative "editor_js/blocks/table"
11
- require_relative "editor_js/renderer"
12
-
13
- module Panda
14
- module CMS
15
- module EditorJs
16
- end
17
- end
18
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- module Panda
6
- module CMS
7
- module EditorJsContent
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- include ActiveModel::Validations
12
- include ActiveModel::Callbacks
13
-
14
- before_save :generate_cached_content
15
- end
16
-
17
- def content=(value)
18
- if value.is_a?(Hash)
19
- super(value.to_json)
20
- else
21
- super
22
- end
23
- end
24
-
25
- def content
26
- value = super
27
- if value.is_a?(String)
28
- begin
29
- JSON.parse(value)
30
- rescue JSON::ParserError
31
- value
32
- end
33
- else
34
- value
35
- end
36
- end
37
-
38
- def generate_cached_content
39
- if content.is_a?(String)
40
- begin
41
- parsed_content = JSON.parse(content)
42
- self.cached_content = if parsed_content.is_a?(Hash) && parsed_content["blocks"].present?
43
- Panda::CMS::EditorJs::Renderer.new(parsed_content).render
44
- else
45
- content
46
- end
47
- rescue JSON::ParserError
48
- # If it's not JSON, treat it as plain text
49
- self.cached_content = content
50
- end
51
- elsif content.is_a?(Hash) && content["blocks"].present?
52
- # Process EditorJS content
53
- self.cached_content = Panda::CMS::EditorJs::Renderer.new(content).render
54
- else
55
- # For any other case, store as is
56
- self.cached_content = content.to_s
57
- end
58
- end
59
- end
60
- end
61
- end