panda-cms 0.7.4 → 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 +37 -2
- data/Rakefile +2 -0
- data/app/builders/panda/cms/form_builder.rb +13 -5
- 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 +1 -3
- 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/dashboard_controller.rb +13 -9
- data/app/controllers/panda/cms/admin/forms_controller.rb +0 -2
- data/app/controllers/panda/cms/admin/my_profile_controller.rb +2 -1
- data/app/controllers/panda/cms/admin/pages_controller.rb +2 -1
- data/app/controllers/panda/cms/admin/posts_controller.rb +1 -2
- 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 +2 -0
- data/app/controllers/panda/cms/pages_controller.rb +32 -29
- 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 +3 -9
- data/app/javascript/panda/cms/controllers/theme_form_controller.js +16 -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 +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 +2 -0
- 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 +14 -12
- data/app/models/panda/cms/post.rb +9 -5
- data/app/models/panda/cms/redirect.rb +6 -3
- data/app/models/panda/cms/template.rb +12 -7
- data/app/models/panda/cms/user.rb +2 -0
- data/app/models/panda/cms/visit.rb +2 -0
- data/app/models/panda/social/instagram_post.rb +2 -0
- data/app/services/panda/cms/html_to_editor_js_converter.rb +4 -2
- 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 +1 -0
- data/app/views/panda/cms/admin/pages/new.html.erb +14 -8
- data/app/views/panda/cms/admin/settings/index.html.erb +1 -1
- 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 +2 -0
- data/config/initializers/zeitwork.rb +2 -0
- data/config/puma/test.rb +3 -1
- data/config/routes.rb +8 -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 +5 -4
- data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +2 -0
- data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +2 -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/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 +47 -41
- data/lib/panda/cms/engine.rb +29 -32
- 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 +4 -2
- data/lib/tasks/assets.rake +547 -0
- data/lib/tasks/panda/cms/install.rake +2 -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 +168 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a11a8249369ac564953d025d4887020ced356b6357a5d8c823580c27e39394cb
|
4
|
+
data.tar.gz: 04d6f8dab1ae48f77414ea10b515739f36a54c0f37b79e82f7d9282305af03a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb71f7f01ba585e142738e5d28510c5a0508b69843aab7c5b60fbf5cbaa7bb89419a916df923d0282cd2311a61614d836002635e67b9d6dfadd4bd6dba4e3f81
|
7
|
+
data.tar.gz: a795545501c2633b3e911b93ccd5c302adbb7d9c9f5a0ab6c363196ae4e0d18089a2b99cc3ada4b66e44902c8a5828212251b331e1b8b4d4762dfafef74dfe0f
|
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,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
5
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
4
6
|
include ActionView::Helpers::TagHelper
|
5
7
|
include ActionView::Helpers::FormTagHelper
|
6
8
|
|
7
|
-
def label(attribute, text = nil, options = {}
|
9
|
+
def label(attribute, text = nil, options = {})
|
8
10
|
super(attribute, text, options.reverse_merge(class: label_styles))
|
9
11
|
end
|
10
12
|
|
@@ -13,8 +15,11 @@ module Panda
|
|
13
15
|
content_tag :div, class: container_styles do
|
14
16
|
label(attribute) + meta_text(options) +
|
15
17
|
content_tag(:div, class: "flex flex-grow") do
|
16
|
-
content_tag(:span,
|
17
|
-
|
18
|
+
content_tag(:span,
|
19
|
+
class: "inline-flex items-center px-3 text-base border border-r-none rounded-s-md whitespace-nowrap break-keep") do
|
20
|
+
options.dig(:data, :prefix)
|
21
|
+
end +
|
22
|
+
super(attribute, options.reverse_merge(class: "#{input_styles_prefix} input-prefix rounded-l-none border-l-none"))
|
18
23
|
end + error_message(attribute)
|
19
24
|
end
|
20
25
|
else
|
@@ -48,7 +53,7 @@ module Panda
|
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
51
|
-
def select(method, choices = nil, options = {}, html_options = {}
|
56
|
+
def select(method, choices = nil, options = {}, html_options = {})
|
52
57
|
content_tag :div, class: container_styles do
|
53
58
|
label(method) + meta_text(options) + super(method, choices, options, html_options.reverse_merge(class: select_styles)) + select_svg + error_message(method)
|
54
59
|
end
|
@@ -147,6 +152,7 @@ module Panda
|
|
147
152
|
|
148
153
|
def meta_text(options)
|
149
154
|
return unless options[:meta]
|
155
|
+
|
150
156
|
@template.content_tag(:p, options[:meta], class: "block text-black/60 text-sm mb-2")
|
151
157
|
end
|
152
158
|
|
@@ -173,7 +179,8 @@ module Panda
|
|
173
179
|
end
|
174
180
|
|
175
181
|
def select_svg
|
176
|
-
@template.content_tag(:svg,
|
182
|
+
@template.content_tag(:svg,
|
183
|
+
class: "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400", aria_hidden: true) do
|
177
184
|
@template.content_tag(:path, d: "M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z")
|
178
185
|
end
|
179
186
|
end
|
@@ -208,6 +215,7 @@ module Panda
|
|
208
215
|
|
209
216
|
def error_message(attribute)
|
210
217
|
return unless object.respond_to?(:errors) && object.errors[attribute]&.any?
|
218
|
+
|
211
219
|
content_tag(:p, class: "mt-2 text-sm text-red-600") do
|
212
220
|
object.errors[attribute].join(", ")
|
213
221
|
end
|
@@ -29,13 +29,14 @@ module Panda
|
|
29
29
|
output = output.html_safe
|
30
30
|
base_heading_styles = "flex pt-1 text-black mb-5 -mt-1"
|
31
31
|
|
32
|
-
|
32
|
+
case level
|
33
|
+
when 1
|
33
34
|
content_tag(:h1, output, class: [base_heading_styles, "text-2xl font-medium", @additional_styles])
|
34
|
-
|
35
|
+
when 2
|
35
36
|
content_tag(:h2, output, class: [base_heading_styles, "text-xl font-medium", @additional_styles])
|
36
|
-
|
37
|
+
when 3
|
37
38
|
content_tag(:h3, output, class: [base_heading_styles, "text-xl", "font-light", @additional_styles])
|
38
|
-
|
39
|
+
when :panel
|
39
40
|
content_tag(:h3, output, class: ["text-base font-medium p-4 text-white"])
|
40
41
|
end
|
41
42
|
end
|
@@ -4,9 +4,9 @@ module Panda
|
|
4
4
|
module CMS
|
5
5
|
module Admin
|
6
6
|
class PanelComponent < ViewComponent::Base
|
7
|
-
renders_one :heading,
|
7
|
+
renders_one :heading, lambda { |text:, icon: "", level: :panel, additional_styles: ""|
|
8
8
|
Panda::CMS::Admin::HeadingComponent.new(text: text, icon: icon, level: level, additional_styles: additional_styles)
|
9
|
-
|
9
|
+
}
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
<% if user.is_a?(Panda::CMS::User) %>
|
1
|
+
<% if user.is_a?(Panda::CMS::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::CMS::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,9 +4,7 @@ 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
|
@@ -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,7 +72,7 @@ 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
|
@@ -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
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
5
|
class AdminConstraint
|
@@ -11,7 +13,8 @@ module Panda
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def current_user(request)
|
14
|
-
|
16
|
+
user_id = request.session[:user_id]
|
17
|
+
User.find_by(id: user_id)
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
@@ -1,19 +1,23 @@
|
|
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 < ApplicationController
|
9
|
+
before_action :set_initial_breadcrumb, only: %i[show]
|
10
|
+
before_action :authenticate_admin_user!
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
# GET /admin
|
13
|
+
def show
|
14
|
+
end
|
12
15
|
|
13
|
-
|
16
|
+
private
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
def set_initial_breadcrumb
|
19
|
+
add_breadcrumb "Dashboard", Panda::CMS.route_namespace
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
@@ -19,7 +19,8 @@ module Panda
|
|
19
19
|
# @return void
|
20
20
|
def update
|
21
21
|
if current_user.update(user_params)
|
22
|
-
|
22
|
+
flash[:success] = "Your profile has been updated successfully."
|
23
|
+
redirect_to edit_admin_my_profile_path
|
23
24
|
else
|
24
25
|
render :edit, locals: {user: current_user}, status: :unprocessable_entity
|
25
26
|
end
|
@@ -38,7 +38,8 @@ 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
|
|
@@ -39,8 +39,7 @@ module Panda
|
|
39
39
|
|
40
40
|
if @post.save
|
41
41
|
Rails.logger.debug "Post saved successfully"
|
42
|
-
|
43
|
-
redirect_to edit_admin_post_path(@post.admin_param), status: :see_other
|
42
|
+
redirect_to edit_admin_post_path(@post.admin_param), notice: "The post was successfully created!"
|
44
43
|
else
|
45
44
|
Rails.logger.debug "Post save failed: #{@post.errors.full_messages.inspect}"
|
46
45
|
flash.now[:error] = @post.errors.full_messages.join(", ")
|
@@ -27,9 +27,7 @@ module Panda
|
|
27
27
|
|
28
28
|
# Always create the first user as admin, regardless of what our settings look like
|
29
29
|
# else we can't ever really login. :)
|
30
|
-
if !create_as_admin
|
31
|
-
create_as_admin = true if !create_as_admin && Panda::CMS::User.count.zero?
|
32
|
-
end
|
30
|
+
create_as_admin = true if !create_as_admin && Panda::CMS::User.count.zero?
|
33
31
|
|
34
32
|
if user_info["first_name"] && user_info["last_name"]
|
35
33
|
firstname = user_info["first_name"]
|
@@ -55,9 +53,9 @@ module Panda
|
|
55
53
|
return
|
56
54
|
end
|
57
55
|
|
58
|
-
|
56
|
+
unless user.admin?
|
59
57
|
# User can't be found with this email address or can't login
|
60
|
-
Rails.logger.error "User ID #{user.id} attempted admin login, is not admin."
|
58
|
+
Rails.logger.error "User ID #{user.id} attempted admin login, is not admin."
|
61
59
|
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
62
60
|
return
|
63
61
|
end
|
@@ -1,36 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
|
-
|
4
|
-
|
5
|
+
module Admin
|
6
|
+
module Settings
|
7
|
+
class BulkEditorController < ApplicationController
|
8
|
+
before_action :set_initial_breadcrumb, only: %i[new]
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
def new
|
11
|
+
@json_data = BulkEditor.export
|
12
|
+
end
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def create
|
15
|
+
begin
|
16
|
+
debug_output = BulkEditor.import(params[:site_content])
|
17
|
+
rescue JSON::ParserError
|
18
|
+
redirect_to admin_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
|
-
|
19
|
-
|
23
|
+
# Grab the latest content back so it's all formatted properly
|
24
|
+
@json_data = BulkEditor.export
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
if debug_output[:error].empty? && debug_output[:warning].empty? && debug_output[:success].empty?
|
27
|
+
redirect_to admin_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
|
-
|
34
|
+
private
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
def set_initial_breadcrumb
|
37
|
+
add_breadcrumb "Settings", admin_settings_path
|
38
|
+
add_breadcrumb "Bulk Editor", "#"
|
39
|
+
end
|
40
|
+
end
|
34
41
|
end
|
35
42
|
end
|
36
43
|
end
|
@@ -1,19 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Panda
|
2
4
|
module CMS
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
module Admin
|
6
|
+
class SettingsController < ApplicationController
|
7
|
+
before_action :set_initial_breadcrumb, only: %i[index show]
|
8
|
+
before_action :authenticate_admin_user!
|
6
9
|
|
7
|
-
|
8
|
-
|
10
|
+
def index
|
11
|
+
end
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
def show
|
14
|
+
end
|
12
15
|
|
13
|
-
|
16
|
+
private
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
def set_initial_breadcrumb
|
19
|
+
add_breadcrumb "Settings", admin_settings_path
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
19
23
|
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
|
@@ -37,11 +39,14 @@ module Panda
|
|
37
39
|
end
|
38
40
|
|
39
41
|
def authenticate_user!
|
40
|
-
redirect_to root_path, flash: {error: "Please login to view this!"} unless user_signed_in?
|
42
|
+
redirect_to main_app.root_path, flash: {error: "Please login to view this!"} unless user_signed_in?
|
41
43
|
end
|
42
44
|
|
43
45
|
def authenticate_admin_user!
|
44
|
-
|
46
|
+
return if user_signed_in? && current_user.admin?
|
47
|
+
|
48
|
+
redirect_to admin_login_path,
|
49
|
+
flash: {error: "Please login to view this!"}
|
45
50
|
end
|
46
51
|
|
47
52
|
# Required for paper_trail and seems as good as convention these days
|
@@ -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,
|
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
|
23
|
+
supported_error_codes.fetch(code, "404")
|
21
24
|
end
|
22
25
|
|
23
26
|
def supported_error_codes
|