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.
- checksums.yaml +4 -4
- data/README.md +37 -2
- data/Rakefile +2 -0
- 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 +3 -5
- data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
- data/app/components/panda/cms/admin/user_display_component.rb +2 -2
- 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 +10 -11
- data/app/components/panda/cms/text_component.rb +6 -7
- data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +14 -9
- data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
- data/app/controllers/panda/cms/admin/forms_controller.rb +3 -6
- data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
- data/app/controllers/panda/cms/admin/pages_controller.rb +9 -8
- data/app/controllers/panda/cms/admin/posts_controller.rb +9 -11
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +32 -25
- data/app/controllers/panda/cms/admin/settings_controller.rb +13 -10
- data/app/controllers/panda/cms/application_controller.rb +19 -6
- data/app/controllers/panda/cms/errors_controller.rb +5 -2
- data/app/controllers/panda/cms/form_submissions_controller.rb +2 -0
- data/app/controllers/panda/cms/pages_controller.rb +34 -31
- 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/application_helper.rb +3 -3
- data/app/helpers/panda/cms/asset_helper.rb +195 -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/application_panda_cms.js +2 -34
- data/app/javascript/panda/cms/controllers/editor_form_controller.js +59 -6
- data/app/javascript/panda/cms/controllers/index.js +8 -24
- 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 +2 -0
- 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 +2 -0
- data/app/models/panda/cms/block.rb +4 -1
- data/app/models/panda/cms/block_content.rb +3 -1
- data/app/models/panda/cms/current.rb +5 -12
- 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 +14 -12
- data/app/models/panda/cms/post.rb +12 -8
- data/app/models/panda/cms/redirect.rb +6 -3
- data/app/models/panda/cms/template.rb +12 -7
- data/app/models/panda/cms/visit.rb +3 -1
- data/app/models/panda/social/instagram_post.rb +2 -0
- data/app/services/panda/social/instagram_feed_service.rb +3 -1
- 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 +2 -1
- data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
- data/app/views/panda/cms/admin/files/index.html.erb +1 -1
- data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
- data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
- data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
- data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
- data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
- data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
- data/app/views/panda/cms/admin/pages/new.html.erb +16 -10
- data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
- data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
- data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/index.html.erb +4 -4
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
- data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
- data/app/views/panda/cms/shared/_header.html.erb +10 -2
- data/app/views/panda/cms/shared/_importmap.html.erb +1 -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 +8 -3
- data/config/initializers/zeitwork.rb +2 -0
- data/config/puma/test.rb +3 -1
- data/config/routes.rb +11 -19
- 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 +5 -1
- data/db/migrate/20250120235542_remove_paper_trail.rb +5 -4
- data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +4 -0
- data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
- data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -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 +2 -0
- data/lib/panda/cms/engine.rb +57 -116
- 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 +54 -42
- data/lib/tasks/assets.rake +587 -0
- data/lib/tasks/panda/cms/install.rake +2 -0
- data/lib/tasks/panda/cms/migrations.rake +13 -0
- data/lib/tasks/panda/social/instagram.rake +2 -0
- data/lib/tasks/panda_cms.rake +3 -30
- 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 +186 -49
- data/app/builders/panda/cms/form_builder.rb +0 -217
- data/app/components/panda/cms/admin/button_component.rb +0 -70
- data/app/components/panda/cms/admin/container_component.rb +0 -13
- data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
- data/app/components/panda/cms/admin/heading_component.rb +0 -45
- data/app/components/panda/cms/admin/panel_component.rb +0 -13
- data/app/components/panda/cms/admin/table_component.rb +0 -46
- data/app/components/panda/cms/admin/tag_component.rb +0 -35
- data/app/constraints/panda/cms/admin_constraint.rb +0 -18
- data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -43
- data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -94
- data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -9
- data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
- data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
- data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
- data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
- data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
- data/app/models/panda/cms/breadcrumb.rb +0 -12
- data/app/models/panda/cms/user.rb +0 -31
- data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -193
- data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
- data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
- data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -5
- data/lib/panda/cms/editor_js/blocks/alert.rb +0 -34
- data/lib/panda/cms/editor_js/blocks/base.rb +0 -33
- data/lib/panda/cms/editor_js/blocks/header.rb +0 -15
- data/lib/panda/cms/editor_js/blocks/image.rb +0 -36
- data/lib/panda/cms/editor_js/blocks/list.rb +0 -32
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -15
- data/lib/panda/cms/editor_js/blocks/quote.rb +0 -41
- data/lib/panda/cms/editor_js/blocks/table.rb +0 -50
- data/lib/panda/cms/editor_js/renderer.rb +0 -124
- data/lib/panda/cms/editor_js.rb +0 -16
- data/lib/panda/cms/editor_js_content.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a439141e4b2fd9c4167ea0bd872096a648fe1a9fcfc237e6a1c20e16d2a2562
|
4
|
+
data.tar.gz: fa28c3d1d698b9725140618603505a0120c0e58430ff4c923ccd986f3e27daa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1923aea63bd5a266487a4a25e852df4cc96e0cba408fd9cb9351a5a302a20ad7e07ef22254dbe01ef7751ca3b35d0a9cdfb941a821e56730455fc73f0223083b
|
7
|
+
data.tar.gz: 54291d3de7b95047412a2c6c9e50b94f187457803bfd1609f0f9a947348de612c2ca6480f33e8e5df5f0305c131d638e025f1d6e04cc30c2997ce4ccb2a9c56e
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Better websites, on Rails.
|
|
12
12
|
🐼 is grown from our work at [Otaina](https://www.otaina.co.uk), a small group of freelancers. We needed something that could handle websites large and small – but where we could expand it too. We sent our first websites live in March 2024.
|
13
13
|
|
14
14
|
 
|
15
|
-
 [](https://github.com/standardrb/standard)
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
@@ -66,8 +66,43 @@ We welcome contributions.
|
|
66
66
|
|
67
67
|
See our [Contributing Guidelines](https://docs.pandacms.io/developers/contributing/)
|
68
68
|
|
69
|
+
### Testing
|
70
|
+
|
71
|
+
Panda CMS uses RSpec for testing.
|
72
|
+
|
73
|
+
#### Using Fixtures
|
74
|
+
|
75
|
+
We encourage using fixtures for tests instead of factories for consistent test data:
|
76
|
+
|
77
|
+
1. Create fixture files in `spec/fixtures` named after the model's table (e.g., `panda_cms_pages.yml`)
|
78
|
+
2. Define records with unique names and their attributes
|
79
|
+
3. Use helper methods to create test templates with mocked file validation
|
80
|
+
|
81
|
+
Example fixture format:
|
82
|
+
|
83
|
+
```yaml
|
84
|
+
# spec/fixtures/panda_cms_pages.yml
|
85
|
+
home_page:
|
86
|
+
title: "Home"
|
87
|
+
path: "/"
|
88
|
+
panda_cms_template_id: <%= ActiveRecord::FixtureSet.identify(:page_template) %>
|
89
|
+
status: "active"
|
90
|
+
created_at: <%= Time.current %>
|
91
|
+
updated_at: <%= Time.current %>
|
92
|
+
```
|
93
|
+
|
94
|
+
Example test using fixtures:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
# Access fixture using table name and record name
|
98
|
+
page = panda_cms_pages(:home_page)
|
99
|
+
expect(page.title).to eq("Home")
|
100
|
+
```
|
101
|
+
|
102
|
+
When testing models with file validations or complex callbacks, use the helper methods in `spec/models/panda/cms/page_spec.rb` as a reference.
|
103
|
+
|
69
104
|
## License
|
70
105
|
|
71
106
|
The gem is available as open source under the terms of the [BSD-3-Clause License](https://opensource.org/licenses/bsd-3-clause).
|
72
107
|
|
73
|
-
Copyright © 2024 - 2025,
|
108
|
+
Copyright © 2024 - 2025, Otaina Limited.
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
<% if user.is_a?(Panda::
|
1
|
+
<% if user.is_a?(Panda::Core::User) && time %>
|
2
2
|
<%= render Panda::CMS::Admin::UserDisplayComponent.new(user: user, metadata: "#{time_ago_in_words(time)} ago") %>
|
3
|
+
<% elsif user.is_a?(Panda::Core::User) %>
|
4
|
+
<%= render Panda::CMS::Admin::UserDisplayComponent.new(user: user, metadata: "Not published") %>
|
3
5
|
<% elsif time %>
|
4
6
|
<div class="text-black/60"><%= time_ago_in_words(time) %> ago</div>
|
5
7
|
<% end %>
|
@@ -4,16 +4,14 @@ module Panda
|
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
6
|
class UserActivityComponent < ViewComponent::Base
|
7
|
-
attr_accessor :model
|
8
|
-
attr_accessor :time
|
9
|
-
attr_accessor :user
|
7
|
+
attr_accessor :model, :time, :user
|
10
8
|
|
11
9
|
# @param model [ActiveRecord::Base] Model instance to which the user activity is related
|
12
10
|
# @param at [ActiveSupport::TimeWithZone] Time of the activity
|
13
|
-
# @param user [Panda::
|
11
|
+
# @param user [Panda::Core::User] User who performed the activity
|
14
12
|
def initialize(model: nil, at: nil, user: nil)
|
15
13
|
@model = model
|
16
|
-
@user = user if user.is_a?(::Panda::
|
14
|
+
@user = user if user.is_a?(::Panda::Core::User)
|
17
15
|
@time = at if at.is_a?(::ActiveSupport::TimeWithZone)
|
18
16
|
end
|
19
17
|
end
|
@@ -10,7 +10,7 @@
|
|
10
10
|
</div>
|
11
11
|
<% end %>
|
12
12
|
<div class="ml-3">
|
13
|
-
<p class="text-sm text-black"><%= user.
|
13
|
+
<p class="text-sm text-black"><%= user.name %></p>
|
14
14
|
<% if metadata %><p class="text-sm text-black/60"><%= metadata %></p><% end %>
|
15
15
|
</div>
|
16
16
|
</div>
|
@@ -7,8 +7,8 @@ module Panda
|
|
7
7
|
attr_accessor :user_id, :user, :metadata
|
8
8
|
|
9
9
|
def initialize(user_id: nil, user: nil, metadata: "")
|
10
|
-
@user = if user.nil? && user_id.present? && Panda::
|
11
|
-
Panda::
|
10
|
+
@user = if user.nil? && user_id.present? && Panda::Core::User.find(user_id)
|
11
|
+
Panda::Core::User.find(user_id)
|
12
12
|
else
|
13
13
|
user
|
14
14
|
end
|
@@ -17,15 +17,19 @@ module Panda
|
|
17
17
|
@options[:id] ||= "code-#{key.to_s.dasherize}"
|
18
18
|
@editable = editable
|
19
19
|
|
20
|
-
raise BlockError
|
20
|
+
raise BlockError, "Key 'code' is not allowed for CodeComponent" if key == :code
|
21
21
|
end
|
22
22
|
|
23
23
|
def call
|
24
24
|
# TODO: For the non-editable version, grab this from a cache or similar?
|
25
|
-
block = Panda::CMS::Block.find_by(kind: KIND, key: @key,
|
25
|
+
block = Panda::CMS::Block.find_by(kind: KIND, key: @key,
|
26
|
+
panda_cms_template_id: Current.page.panda_cms_template_id)
|
26
27
|
|
27
28
|
if block.nil?
|
28
|
-
|
29
|
+
unless Rails.env.production?
|
30
|
+
raise Panda::CMS::MissingBlockError, "Block with key #{@key} not found for page #{Current.page.title}"
|
31
|
+
end
|
32
|
+
|
29
33
|
return false
|
30
34
|
end
|
31
35
|
|
@@ -58,7 +62,7 @@ module Panda
|
|
58
62
|
|
59
63
|
def is_embedded?
|
60
64
|
# TODO: Check security on this - embed_id should match something?
|
61
|
-
request.params
|
65
|
+
request.params[:embed_id].present?
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
@@ -23,24 +23,25 @@ module Panda
|
|
23
23
|
|
24
24
|
@menu_items = @menu_items.order(:lft).map do |menu_item|
|
25
25
|
if is_active?(menu_item)
|
26
|
-
menu_item.define_singleton_method(:css_classes) { styles[:default]
|
26
|
+
menu_item.define_singleton_method(:css_classes) { "#{styles[:default]} #{styles[:active]}" }
|
27
27
|
else
|
28
|
-
menu_item.define_singleton_method(:css_classes) { styles[:default]
|
28
|
+
menu_item.define_singleton_method(:css_classes) { "#{styles[:default]} #{styles[:inactive]}" }
|
29
29
|
end
|
30
30
|
|
31
31
|
menu_item
|
32
32
|
end
|
33
33
|
|
34
34
|
# TODO: Surely don't need this but Current.page isn't working in the component
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
return unless @render_page_menu
|
36
|
+
|
37
|
+
@current_page = Panda::CMS::Page.find_by(path: @current_path)
|
38
|
+
@page_menu_styles = page_menu_styles
|
39
39
|
end
|
40
40
|
|
41
41
|
def is_active?(menu_item)
|
42
42
|
return true if @current_path == "/" && active_link?(menu_item.page.path, match: :exact)
|
43
43
|
return true if menu_item.page.path != "/" && active_link?(menu_item.page.path, match: :starts_with)
|
44
|
+
|
44
45
|
false
|
45
46
|
end
|
46
47
|
|
@@ -3,31 +3,29 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
class PageMenuComponent < ViewComponent::Base
|
6
|
-
attr_accessor :page
|
7
|
-
attr_accessor :menu_item
|
8
|
-
attr_accessor :styles
|
6
|
+
attr_accessor :page, :menu_item, :styles
|
9
7
|
|
10
8
|
def initialize(page:, start_depth:, styles: {}, show_heading: true)
|
11
9
|
@page = page
|
12
10
|
|
13
|
-
|
14
|
-
start_page = if @page.depth == start_depth
|
15
|
-
@page
|
16
|
-
else
|
17
|
-
@page.ancestors.find { |anc| anc.depth == start_depth }
|
18
|
-
end
|
11
|
+
return if @page.nil?
|
19
12
|
|
20
|
-
|
21
|
-
|
13
|
+
start_page = if @page.depth == start_depth
|
14
|
+
@page
|
15
|
+
else
|
16
|
+
@page.ancestors.find { |anc| anc.depth == start_depth }
|
17
|
+
end
|
22
18
|
|
23
|
-
|
19
|
+
menu = start_page&.page_menu
|
20
|
+
return if menu.nil?
|
24
21
|
|
25
|
-
|
22
|
+
@menu_item = menu.menu_items.order(:lft)&.first
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
@show_heading = show_heading
|
25
|
+
|
26
|
+
# Set some default styles for sanity
|
27
|
+
@styles = styles
|
28
|
+
@styles[:indent_with] ||= "pl-2"
|
31
29
|
end
|
32
30
|
|
33
31
|
def render?
|
@@ -12,9 +12,7 @@ module Panda
|
|
12
12
|
|
13
13
|
KIND = "rich_text"
|
14
14
|
|
15
|
-
attr_accessor :editable
|
16
|
-
attr_accessor :content
|
17
|
-
attr_accessor :options
|
15
|
+
attr_accessor :editable, :content, :options
|
18
16
|
|
19
17
|
def initialize(key: :text_component, text: "Lorem ipsum...", editable: true, **options)
|
20
18
|
@key = key
|
@@ -27,7 +25,8 @@ module Panda
|
|
27
25
|
def before_render
|
28
26
|
@editable &&= params[:embed_id].present? && params[:embed_id] == Current.page.id && Current.user.admin?
|
29
27
|
|
30
|
-
block = Panda::CMS::Block.find_by(kind: "rich_text", key: @key,
|
28
|
+
block = Panda::CMS::Block.find_by(kind: "rich_text", key: @key,
|
29
|
+
panda_cms_template_id: Current.page.panda_cms_template_id)
|
31
30
|
raise ComponentError, "Block not found for key: #{@key}" unless block
|
32
31
|
|
33
32
|
block_content = block.block_contents.find_by(panda_cms_page_id: Current.page.id)
|
@@ -65,7 +64,7 @@ module Panda
|
|
65
64
|
# Ensure the content is properly structured
|
66
65
|
{
|
67
66
|
"time" => parsed["time"] || Time.current.to_i * 1000,
|
68
|
-
"blocks" => parsed["blocks"].map
|
67
|
+
"blocks" => parsed["blocks"].map do |block|
|
69
68
|
{
|
70
69
|
"type" => block["type"],
|
71
70
|
"data" => block["data"].merge(
|
@@ -73,19 +72,19 @@ module Panda
|
|
73
72
|
),
|
74
73
|
"tunes" => block["tunes"]
|
75
74
|
}.compact
|
76
|
-
|
75
|
+
end,
|
77
76
|
"version" => parsed["version"] || "2.28.2"
|
78
77
|
}
|
79
78
|
else
|
80
79
|
# If not valid EditorJS, try to convert from HTML
|
81
80
|
begin
|
82
|
-
editor_content = Panda::
|
81
|
+
editor_content = Panda::Editor::HtmlToEditorJsConverter.convert(@content)
|
83
82
|
if valid_editor_js_content?(editor_content)
|
84
83
|
editor_content
|
85
84
|
else
|
86
85
|
empty_editor_js_content
|
87
86
|
end
|
88
|
-
rescue Panda::
|
87
|
+
rescue Panda::Editor::HtmlToEditorJsConverter::ConversionError => e
|
89
88
|
Rails.logger.error("HTML conversion error: #{e.message}")
|
90
89
|
empty_editor_js_content
|
91
90
|
end
|
@@ -94,13 +93,13 @@ module Panda
|
|
94
93
|
Rails.logger.error("JSON parse error: #{e.message}")
|
95
94
|
# Try to convert from HTML
|
96
95
|
begin
|
97
|
-
editor_content = Panda::
|
96
|
+
editor_content = Panda::Editor::HtmlToEditorJsConverter.convert(@content)
|
98
97
|
if valid_editor_js_content?(editor_content)
|
99
98
|
editor_content
|
100
99
|
else
|
101
100
|
empty_editor_js_content
|
102
101
|
end
|
103
|
-
rescue Panda::
|
102
|
+
rescue Panda::Editor::HtmlToEditorJsConverter::ConversionError => e
|
104
103
|
Rails.logger.error("HTML conversion error: #{e.message}")
|
105
104
|
empty_editor_js_content
|
106
105
|
end
|
@@ -130,7 +129,7 @@ module Panda
|
|
130
129
|
parsed_content["blocks"][0]["data"]["text"].blank?
|
131
130
|
"<p></p>".html_safe
|
132
131
|
else
|
133
|
-
renderer = Panda::
|
132
|
+
renderer = Panda::Editor::Renderer.new(parsed_content)
|
134
133
|
rendered = renderer.render
|
135
134
|
rendered.presence&.html_safe || "<p></p>".html_safe
|
136
135
|
end
|
@@ -25,10 +25,10 @@ module Panda
|
|
25
25
|
content_tag(:span, @content, @options, false) # Don't escape the content
|
26
26
|
rescue
|
27
27
|
if !Rails.env.production? || is_defined?(Sentry)
|
28
|
-
raise Panda::CMS::MissingBlockError
|
29
|
-
else
|
30
|
-
false
|
28
|
+
raise Panda::CMS::MissingBlockError, "Block with key #{@key} not found for page #{Current.page.title}"
|
31
29
|
end
|
30
|
+
|
31
|
+
false
|
32
32
|
end
|
33
33
|
|
34
34
|
#
|
@@ -45,11 +45,10 @@ module Panda
|
|
45
45
|
def before_render
|
46
46
|
@editable &&= params[:embed_id].present? && params[:embed_id] == Current.page.id
|
47
47
|
|
48
|
-
block = Panda::CMS::Block.find_by(kind: KIND, key: @key,
|
48
|
+
block = Panda::CMS::Block.find_by(kind: KIND, key: @key,
|
49
|
+
panda_cms_template_id: Current.page.panda_cms_template_id)
|
49
50
|
|
50
|
-
if block.nil?
|
51
|
-
return false
|
52
|
-
end
|
51
|
+
return false if block.nil?
|
53
52
|
|
54
53
|
block_content = block.block_contents.find_by(panda_cms_page_id: Current.page.id)
|
55
54
|
plain_text = block_content&.content.to_s
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module CMS
|
5
|
+
module Admin
|
6
|
+
# Base controller for all CMS admin controllers
|
7
|
+
# Inherits from Core AdminController for authentication
|
8
|
+
# Adds CMS-specific helpers and functionality
|
9
|
+
class BaseController < ::Panda::Core::AdminController
|
10
|
+
# Include CMS helpers so views have access to panda_cms_form_with, etc.
|
11
|
+
helper Panda::CMS::ApplicationHelper
|
12
|
+
|
13
|
+
# Include the helper methods in the controller as well
|
14
|
+
include Panda::CMS::ApplicationHelper
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -3,10 +3,9 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
|
-
class BlockContentsController <
|
6
|
+
class BlockContentsController < ::Panda::CMS::Admin::BaseController
|
7
7
|
before_action :set_page, only: %i[update]
|
8
8
|
before_action :set_block_content, only: %i[update]
|
9
|
-
before_action :authenticate_admin_user!
|
10
9
|
|
11
10
|
# @type PATCH/PUT
|
12
11
|
# @return
|
@@ -1,19 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "groupdate"
|
2
4
|
|
3
5
|
module Panda
|
4
6
|
module CMS
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
module Admin
|
8
|
+
class DashboardController < ::Panda::Core::Admin::DashboardController
|
9
|
+
before_action :set_initial_breadcrumb, only: %i[show]
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
# Override the panda-core dashboard with CMS-specific dashboard
|
12
|
+
def show
|
13
|
+
# Render the CMS dashboard view
|
14
|
+
render "panda/cms/admin/dashboard/show"
|
15
|
+
end
|
12
16
|
|
13
|
-
|
17
|
+
private
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
def set_initial_breadcrumb
|
20
|
+
add_breadcrumb "Dashboard", admin_cms_dashboard_path
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -3,9 +3,7 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
|
-
class FilesController <
|
7
|
-
before_action :authenticate_admin_user!
|
8
|
-
|
6
|
+
class FilesController < ::Panda::CMS::Admin::BaseController
|
9
7
|
def create
|
10
8
|
file = params[:image]
|
11
9
|
return render json: {success: 0} unless file
|
@@ -3,9 +3,8 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
|
-
class FormsController <
|
6
|
+
class FormsController < ::Panda::CMS::Admin::BaseController
|
7
7
|
before_action :set_initial_breadcrumb, only: %i[index show]
|
8
|
-
before_action :authenticate_admin_user!
|
9
8
|
|
10
9
|
# Lists all forms
|
11
10
|
# @type GET
|
@@ -18,7 +17,7 @@ module Panda
|
|
18
17
|
def show
|
19
18
|
form = Panda::CMS::Form.find(params[:id])
|
20
19
|
|
21
|
-
add_breadcrumb form.name,
|
20
|
+
add_breadcrumb form.name, admin_cms_form_path(form)
|
22
21
|
submissions = form.form_submissions.order(created_at: :desc)
|
23
22
|
# TODO: Set a whitelist of fields we allow to be submitted for the form, shown in this view
|
24
23
|
# and a formatting array of how to display them... eventually?
|
@@ -35,11 +34,9 @@ module Panda
|
|
35
34
|
private
|
36
35
|
|
37
36
|
def set_initial_breadcrumb
|
38
|
-
add_breadcrumb "Forms",
|
37
|
+
add_breadcrumb "Forms", admin_cms_forms_path
|
39
38
|
end
|
40
39
|
|
41
|
-
private
|
42
|
-
|
43
40
|
# Only allow a list of trusted parameters through
|
44
41
|
# @type private
|
45
42
|
# @return ActionController::StrongParameters
|
@@ -3,9 +3,8 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
|
-
class MenusController <
|
6
|
+
class MenusController < ::Panda::CMS::Admin::BaseController
|
7
7
|
before_action :set_initial_breadcrumb, only: %i[index]
|
8
|
-
before_action :authenticate_admin_user!
|
9
8
|
|
10
9
|
# Lists all menus which can be managed by the administrator
|
11
10
|
# @type GET
|
@@ -22,7 +21,7 @@ module Panda
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def set_initial_breadcrumb
|
25
|
-
add_breadcrumb "Menus",
|
24
|
+
add_breadcrumb "Menus", admin_cms_menus_path
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
@@ -3,9 +3,9 @@
|
|
3
3
|
module Panda
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
|
-
class PagesController <
|
6
|
+
class PagesController < ::Panda::CMS::Admin::BaseController
|
7
7
|
before_action :set_initial_breadcrumb, only: %i[index edit new create update]
|
8
|
-
|
8
|
+
# Authentication is automatically enforced by AdminController
|
9
9
|
|
10
10
|
# Lists all pages which can be managed by the administrator
|
11
11
|
# @type GET
|
@@ -25,7 +25,7 @@ module Panda
|
|
25
25
|
# Loads the page editor
|
26
26
|
# @type GET
|
27
27
|
def edit
|
28
|
-
add_breadcrumb page.title,
|
28
|
+
add_breadcrumb page.title, edit_admin_cms_page_path(page)
|
29
29
|
|
30
30
|
render :edit, locals: {page: page, template: page.template}
|
31
31
|
end
|
@@ -38,12 +38,13 @@ module Panda
|
|
38
38
|
page.path = nil if page.path.blank?
|
39
39
|
|
40
40
|
# Set the full path before validation if we have a parent
|
41
|
-
if page.parent && page.parent.path != "/" && page.path.present?
|
41
|
+
if page.parent && page.parent.path != "/" && page.path.present? && !page.path.start_with?(page.parent.path)
|
42
|
+
# Only prepend parent path if it's not already included
|
42
43
|
page.path = page.parent.path + page.path
|
43
44
|
end
|
44
45
|
|
45
46
|
if page.save
|
46
|
-
redirect_to
|
47
|
+
redirect_to edit_admin_cms_page_path(page), notice: "The page was successfully created."
|
47
48
|
else
|
48
49
|
flash.now[:error] = page.errors.full_messages.to_sentence
|
49
50
|
locals = setup_new_page_form(page: page)
|
@@ -55,7 +56,7 @@ module Panda
|
|
55
56
|
# @return
|
56
57
|
def update
|
57
58
|
if page.update(page_params)
|
58
|
-
redirect_to
|
59
|
+
redirect_to edit_admin_cms_page_path(page),
|
59
60
|
status: :see_other,
|
60
61
|
flash: {success: "This page was successfully updated!"}
|
61
62
|
else
|
@@ -78,11 +79,11 @@ module Panda
|
|
78
79
|
end
|
79
80
|
|
80
81
|
def set_initial_breadcrumb
|
81
|
-
add_breadcrumb "Pages",
|
82
|
+
add_breadcrumb "Pages", admin_cms_pages_path
|
82
83
|
end
|
83
84
|
|
84
85
|
def setup_new_page_form(page:)
|
85
|
-
add_breadcrumb "Add Page",
|
86
|
+
add_breadcrumb "Add Page", new_admin_cms_page_path
|
86
87
|
{
|
87
88
|
page: page,
|
88
89
|
available_templates: Panda::CMS::Template.available
|
@@ -5,9 +5,8 @@ require "json"
|
|
5
5
|
module Panda
|
6
6
|
module CMS
|
7
7
|
module Admin
|
8
|
-
class PostsController <
|
8
|
+
class PostsController < ::Panda::CMS::Admin::BaseController
|
9
9
|
before_action :set_initial_breadcrumb, only: %i[index edit new create update]
|
10
|
-
before_action :authenticate_admin_user!
|
11
10
|
|
12
11
|
# Get all posts
|
13
12
|
# @type GET
|
@@ -27,7 +26,7 @@ module Panda
|
|
27
26
|
# Loads the post editor
|
28
27
|
# @type GET
|
29
28
|
def edit
|
30
|
-
add_breadcrumb post.title,
|
29
|
+
add_breadcrumb post.title, edit_admin_cms_post_path(post.admin_param)
|
31
30
|
render :edit, locals: {post: post}
|
32
31
|
end
|
33
32
|
|
@@ -39,8 +38,7 @@ module Panda
|
|
39
38
|
|
40
39
|
if @post.save
|
41
40
|
Rails.logger.debug "Post saved successfully"
|
42
|
-
|
43
|
-
redirect_to edit_admin_post_path(@post.admin_param), status: :see_other
|
41
|
+
redirect_to edit_admin_cms_post_path(@post.admin_param), notice: "The post was successfully created!"
|
44
42
|
else
|
45
43
|
Rails.logger.debug "Post save failed: #{@post.errors.full_messages.inspect}"
|
46
44
|
flash.now[:error] = @post.errors.full_messages.join(", ")
|
@@ -61,13 +59,13 @@ module Panda
|
|
61
59
|
update_params[:user_id] = current_user.id
|
62
60
|
if post.update(update_params)
|
63
61
|
Rails.logger.debug "Post updated successfully"
|
64
|
-
add_breadcrumb post.title,
|
62
|
+
add_breadcrumb post.title, edit_admin_cms_post_path(post.admin_param)
|
65
63
|
flash[:success] = "The post was successfully updated"
|
66
|
-
redirect_to
|
64
|
+
redirect_to edit_admin_cms_post_path(post.admin_param), status: :see_other
|
67
65
|
else
|
68
66
|
Rails.logger.debug "Post update failed: #{post.errors.full_messages.inspect}"
|
69
67
|
Rails.logger.debug "Preserving content: #{post_params[:content].inspect}"
|
70
|
-
add_breadcrumb post.title.presence || "Edit Post",
|
68
|
+
add_breadcrumb post.title.presence || "Edit Post", edit_admin_cms_post_path(post.admin_param)
|
71
69
|
flash.now[:error] = post.errors.full_messages.join(", ")
|
72
70
|
render :edit, locals: {
|
73
71
|
post: post,
|
@@ -93,11 +91,11 @@ module Panda
|
|
93
91
|
end
|
94
92
|
|
95
93
|
def set_initial_breadcrumb
|
96
|
-
add_breadcrumb "Posts",
|
94
|
+
add_breadcrumb "Posts", admin_cms_posts_path
|
97
95
|
end
|
98
96
|
|
99
97
|
def setup_new_post_form(post: nil, preserved_content: nil)
|
100
|
-
add_breadcrumb "Add Post",
|
98
|
+
add_breadcrumb "Add Post", new_admin_cms_post_path
|
101
99
|
|
102
100
|
post ||= Panda::CMS::Post.new(
|
103
101
|
status: "active",
|
@@ -106,7 +104,7 @@ module Panda
|
|
106
104
|
|
107
105
|
{
|
108
106
|
post: post,
|
109
|
-
url:
|
107
|
+
url: admin_cms_posts_path,
|
110
108
|
preserved_content: preserved_content
|
111
109
|
}
|
112
110
|
end
|