panda-cms 0.7.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 +7 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/app/assets/builds/panda.cms.css +2808 -0
- data/app/assets/config/panda_cms_manifest.js +4 -0
- data/app/assets/stylesheets/panda/cms/application.tailwind.css +162 -0
- data/app/assets/stylesheets/panda/cms/editor.css +120 -0
- data/app/builders/panda/cms/form_builder.rb +234 -0
- data/app/components/panda/cms/admin/button_component.rb +70 -0
- data/app/components/panda/cms/admin/container_component.html.erb +13 -0
- data/app/components/panda/cms/admin/container_component.rb +13 -0
- data/app/components/panda/cms/admin/flash_message_component.html.erb +31 -0
- data/app/components/panda/cms/admin/flash_message_component.rb +47 -0
- data/app/components/panda/cms/admin/heading_component.rb +45 -0
- data/app/components/panda/cms/admin/panel_component.html.erb +7 -0
- data/app/components/panda/cms/admin/panel_component.rb +13 -0
- data/app/components/panda/cms/admin/slideover_component.html.erb +9 -0
- data/app/components/panda/cms/admin/slideover_component.rb +15 -0
- data/app/components/panda/cms/admin/statistics_component.html.erb +4 -0
- data/app/components/panda/cms/admin/statistics_component.rb +17 -0
- data/app/components/panda/cms/admin/tab_bar_component.html.erb +35 -0
- data/app/components/panda/cms/admin/tab_bar_component.rb +15 -0
- data/app/components/panda/cms/admin/table_component.html.erb +29 -0
- data/app/components/panda/cms/admin/table_component.rb +46 -0
- data/app/components/panda/cms/admin/tag_component.rb +35 -0
- data/app/components/panda/cms/admin/user_activity_component.html.erb +5 -0
- data/app/components/panda/cms/admin/user_activity_component.rb +33 -0
- data/app/components/panda/cms/admin/user_display_component.html.erb +17 -0
- data/app/components/panda/cms/admin/user_display_component.rb +21 -0
- data/app/components/panda/cms/code_component.rb +64 -0
- data/app/components/panda/cms/grid_component.html.erb +6 -0
- data/app/components/panda/cms/grid_component.rb +15 -0
- data/app/components/panda/cms/menu_component.html.erb +6 -0
- data/app/components/panda/cms/menu_component.rb +58 -0
- data/app/components/panda/cms/page_menu_component.html.erb +21 -0
- data/app/components/panda/cms/page_menu_component.rb +38 -0
- data/app/components/panda/cms/rich_text_component.html.erb +6 -0
- data/app/components/panda/cms/rich_text_component.rb +84 -0
- data/app/components/panda/cms/text_component.rb +72 -0
- data/app/constraints/panda/cms/admin_constraint.rb +18 -0
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +52 -0
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +20 -0
- data/app/controllers/panda/cms/admin/files_controller.rb +21 -0
- data/app/controllers/panda/cms/admin/forms_controller.rb +53 -0
- data/app/controllers/panda/cms/admin/menus_controller.rb +30 -0
- data/app/controllers/panda/cms/admin/pages_controller.rb +91 -0
- data/app/controllers/panda/cms/admin/posts_controller.rb +146 -0
- data/app/controllers/panda/cms/admin/sessions_controller.rb +94 -0
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +37 -0
- data/app/controllers/panda/cms/admin/settings_controller.rb +20 -0
- data/app/controllers/panda/cms/application_controller.rb +57 -0
- data/app/controllers/panda/cms/errors_controller.rb +33 -0
- data/app/controllers/panda/cms/form_submissions_controller.rb +23 -0
- data/app/controllers/panda/cms/pages_controller.rb +72 -0
- data/app/controllers/panda/cms/posts_controller.rb +13 -0
- data/app/helpers/panda/cms/admin/files_helper.rb +6 -0
- data/app/helpers/panda/cms/admin/pages_helper.rb +6 -0
- data/app/helpers/panda/cms/admin/posts_helper.rb +48 -0
- data/app/helpers/panda/cms/application_helper.rb +120 -0
- data/app/helpers/panda/cms/pages_helper.rb +6 -0
- data/app/helpers/panda/cms/theme_helper.rb +18 -0
- data/app/javascript/panda/cms/@editorjs--editorjs.js +2577 -0
- data/app/javascript/panda/cms/@hotwired--stimulus.js +4 -0
- data/app/javascript/panda/cms/@hotwired--turbo.js +160 -0
- data/app/javascript/panda/cms/@rails--actioncable--src.js +4 -0
- data/app/javascript/panda/cms/application_panda_cms.js +39 -0
- data/app/javascript/panda/cms/controllers/dashboard_controller.js +7 -0
- data/app/javascript/panda/cms/controllers/editor_form_controller.js +77 -0
- data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +320 -0
- data/app/javascript/panda/cms/controllers/index.js +48 -0
- data/app/javascript/panda/cms/controllers/slug_controller.js +87 -0
- data/app/javascript/panda/cms/editor/css_extractor.js +80 -0
- data/app/javascript/panda/cms/editor/editor_js_config.js +177 -0
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +285 -0
- data/app/javascript/panda/cms/editor/plain_text_editor.js +110 -0
- data/app/javascript/panda/cms/editor/resource_loader.js +115 -0
- data/app/javascript/panda/cms/tailwindcss-stimulus-components.js +4 -0
- data/app/jobs/panda/cms/application_job.rb +6 -0
- data/app/jobs/panda/cms/record_visit_job.rb +31 -0
- data/app/mailers/panda/cms/application_mailer.rb +8 -0
- data/app/mailers/panda/cms/form_mailer.rb +21 -0
- data/app/models/action_text/rich_text_version.rb +6 -0
- data/app/models/panda/cms/application_record.rb +7 -0
- data/app/models/panda/cms/block.rb +34 -0
- data/app/models/panda/cms/block_content.rb +18 -0
- data/app/models/panda/cms/block_content_version.rb +8 -0
- data/app/models/panda/cms/breadcrumb.rb +12 -0
- data/app/models/panda/cms/current.rb +17 -0
- data/app/models/panda/cms/form.rb +9 -0
- data/app/models/panda/cms/form_submission.rb +7 -0
- data/app/models/panda/cms/menu.rb +52 -0
- data/app/models/panda/cms/menu_item.rb +58 -0
- data/app/models/panda/cms/page.rb +96 -0
- data/app/models/panda/cms/page_version.rb +8 -0
- data/app/models/panda/cms/post.rb +60 -0
- data/app/models/panda/cms/post_version.rb +8 -0
- data/app/models/panda/cms/redirect.rb +11 -0
- data/app/models/panda/cms/template.rb +124 -0
- data/app/models/panda/cms/template_version.rb +8 -0
- data/app/models/panda/cms/user.rb +31 -0
- data/app/models/panda/cms/version.rb +8 -0
- data/app/models/panda/cms/visit.rb +9 -0
- data/app/services/panda/cms/html_to_editor_js_converter.rb +200 -0
- data/app/views/active_storage/blobs/blobs/_blob.html.erb +14 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/panda/cms/application.html.erb +41 -0
- data/app/views/layouts/panda/cms/public.html.erb +3 -0
- data/app/views/panda/cms/admin/dashboard/show.html.erb +12 -0
- data/app/views/panda/cms/admin/files/index.html.erb +124 -0
- data/app/views/panda/cms/admin/files/show.html.erb +2 -0
- data/app/views/panda/cms/admin/forms/edit.html.erb +0 -0
- data/app/views/panda/cms/admin/forms/index.html.erb +13 -0
- data/app/views/panda/cms/admin/forms/new.html.erb +15 -0
- data/app/views/panda/cms/admin/forms/show.html.erb +35 -0
- data/app/views/panda/cms/admin/menus/index.html.erb +8 -0
- data/app/views/panda/cms/admin/pages/edit.html.erb +36 -0
- data/app/views/panda/cms/admin/pages/index.html.erb +22 -0
- data/app/views/panda/cms/admin/pages/new.html.erb +15 -0
- data/app/views/panda/cms/admin/pages/show.html.erb +1 -0
- data/app/views/panda/cms/admin/posts/_form.html.erb +29 -0
- data/app/views/panda/cms/admin/posts/edit.html.erb +6 -0
- data/app/views/panda/cms/admin/posts/index.html.erb +18 -0
- data/app/views/panda/cms/admin/posts/new.html.erb +6 -0
- data/app/views/panda/cms/admin/sessions/new.html.erb +17 -0
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +68 -0
- data/app/views/panda/cms/admin/settings/index.html.erb +21 -0
- data/app/views/panda/cms/admin/settings/insta.html +4 -0
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +28 -0
- data/app/views/panda/cms/admin/shared/_flash.html.erb +5 -0
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +41 -0
- data/app/views/panda/cms/form_mailer/notification_email.html.erb +11 -0
- data/app/views/panda/cms/shared/_editor.html.erb +0 -0
- data/app/views/panda/cms/shared/_favicons.html.erb +9 -0
- data/app/views/panda/cms/shared/_footer.html.erb +2 -0
- data/app/views/panda/cms/shared/_header.html.erb +15 -0
- data/app/views/panda/cms/shared/_importmap.html.erb +33 -0
- data/config/importmap.rb +13 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/initializers/panda/cms/form_errors.rb +38 -0
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb +11 -0
- data/config/initializers/panda/cms/paper_trail.rb +7 -0
- data/config/initializers/panda/cms.rb +10 -0
- data/config/initializers/zeitwork.rb +3 -0
- data/config/locales/en.yml +49 -0
- data/config/puma/test.rb +9 -0
- data/config/routes.rb +48 -0
- data/config/tailwind.config.js +37 -0
- data/db/migrate/20240205223709_create_panda_cms_pages.rb +9 -0
- data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +14 -0
- data/db/migrate/20240303002805_create_panda_cms_templates.rb +11 -0
- data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +14 -0
- data/db/migrate/20240303022441_create_panda_cms_blocks.rb +13 -0
- data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +10 -0
- data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +14 -0
- data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +10 -0
- data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +12 -0
- data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +5 -0
- data/db/migrate/20240305000000_convert_html_content_to_editor_js.rb +82 -0
- data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +9 -0
- data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +16 -0
- data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +6 -0
- data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +5 -0
- data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +24 -0
- data/db/migrate/20240317010532_create_panda_cms_users.rb +12 -0
- data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +7 -0
- data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +5 -0
- data/db/migrate/20240317214827_create_panda_cms_redirects.rb +14 -0
- data/db/migrate/20240317230622_create_panda_cms_visits.rb +13 -0
- data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +58 -0
- data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +5 -0
- data/db/migrate/20240701225422_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
- data/db/migrate/20240701225423_create_active_storage_variant_records.active_storage.rb +28 -0
- data/db/migrate/20240701225424_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +8 -0
- data/db/migrate/20240804235210_create_panda_cms_forms.rb +11 -0
- data/db/migrate/20240805013612_create_panda_cms_form_submissions.rb +9 -0
- data/db/migrate/20240805121123_create_panda_cms_posts.rb +27 -0
- data/db/migrate/20240805123104_create_panda_cms_post_versions.rb +14 -0
- data/db/migrate/20240806112735_fix_panda_cms_visits_column_names.rb +13 -0
- data/db/migrate/20240806204412_add_completion_path_to_panda_cms_forms.rb +5 -0
- data/db/migrate/20240820081917_change_form_submissions_to_submission_count.rb +5 -0
- data/db/migrate/20240904200605_create_action_text_tables.action_text.rb +24 -0
- data/db/migrate/20240923234535_add_depth_to_panda_cms_menus.rb +11 -0
- data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +5 -0
- data/db/migrate/20241119214548_convert_post_content_to_editor_js.rb +35 -0
- data/db/migrate/20241119214549_remove_action_text_from_posts.rb +9 -0
- data/db/migrate/20241120000419_remove_post_tag_references.rb +19 -0
- data/db/migrate/20241120110943_add_editor_js_to_posts.rb +27 -0
- data/db/migrate/20241120113859_add_cached_content_to_panda_cms_posts.rb +5 -0
- data/db/migrate/20241123234140_remove_post_tag_id_from_posts.rb +5 -0
- data/db/migrate/migrate +1 -0
- data/db/seeds.rb +5 -0
- data/lib/generators/panda/cms/install_generator.rb +29 -0
- data/lib/panda/cms/bulk_editor.rb +171 -0
- data/lib/panda/cms/demo_site_generator.rb +67 -0
- data/lib/panda/cms/editor_js/blocks/alert.rb +34 -0
- data/lib/panda/cms/editor_js/blocks/base.rb +33 -0
- data/lib/panda/cms/editor_js/blocks/header.rb +15 -0
- data/lib/panda/cms/editor_js/blocks/image.rb +36 -0
- data/lib/panda/cms/editor_js/blocks/list.rb +32 -0
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +15 -0
- data/lib/panda/cms/editor_js/blocks/quote.rb +41 -0
- data/lib/panda/cms/editor_js/blocks/table.rb +50 -0
- data/lib/panda/cms/editor_js/renderer.rb +124 -0
- data/lib/panda/cms/editor_js.rb +16 -0
- data/lib/panda/cms/editor_js_content.rb +21 -0
- data/lib/panda/cms/engine.rb +257 -0
- data/lib/panda/cms/exceptions_app.rb +26 -0
- data/lib/panda/cms/railtie.rb +11 -0
- data/lib/panda/cms/slug.rb +24 -0
- data/lib/panda/cms.rb +0 -0
- data/lib/panda-cms/version.rb +5 -0
- data/lib/panda-cms.rb +81 -0
- data/lib/tasks/panda_cms.rake +54 -0
- data/lib/templates/erb/scaffold/_form.html.erb.tt +43 -0
- data/lib/templates/erb/scaffold/edit.html.erb.tt +8 -0
- data/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
- data/lib/templates/erb/scaffold/new.html.erb.tt +7 -0
- data/lib/templates/erb/scaffold/partial.html.erb.tt +22 -0
- data/lib/templates/erb/scaffold/show.html.erb.tt +15 -0
- data/public/panda-cms-assets/favicons/android-chrome-192x192.png +0 -0
- data/public/panda-cms-assets/favicons/android-chrome-512x512.png +0 -0
- data/public/panda-cms-assets/favicons/apple-touch-icon.png +0 -0
- data/public/panda-cms-assets/favicons/browserconfig.xml +9 -0
- data/public/panda-cms-assets/favicons/favicon-16x16.png +0 -0
- data/public/panda-cms-assets/favicons/favicon-32x32.png +0 -0
- data/public/panda-cms-assets/favicons/favicon.ico +0 -0
- data/public/panda-cms-assets/favicons/mstile-150x150.png +0 -0
- data/public/panda-cms-assets/favicons/safari-pinned-tab.svg +61 -0
- data/public/panda-cms-assets/favicons/site.webmanifest +14 -0
- data/public/panda-cms-assets/panda-logo-screenprint.png +0 -0
- data/public/panda-cms-assets/panda-nav.png +0 -0
- data/public/panda-cms-assets/rich_text_editor.css +568 -0
- metadata +654 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class Admin::Settings::BulkEditorController < ApplicationController
|
|
4
|
+
before_action :set_initial_breadcrumb, only: %i[new]
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
@json_data = BulkEditor.export
|
|
8
|
+
end
|
|
9
|
+
|
|
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
|
|
17
|
+
|
|
18
|
+
# Grab the latest content back so it's all formatted properly
|
|
19
|
+
@json_data = BulkEditor.export
|
|
20
|
+
|
|
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
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def set_initial_breadcrumb
|
|
32
|
+
add_breadcrumb "Settings", admin_settings_path
|
|
33
|
+
add_breadcrumb "Bulk Editor", "#"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class Admin::SettingsController < ApplicationController
|
|
4
|
+
before_action :set_initial_breadcrumb, only: %i[index show]
|
|
5
|
+
before_action :authenticate_admin_user!
|
|
6
|
+
|
|
7
|
+
def index
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def show
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def set_initial_breadcrumb
|
|
16
|
+
add_breadcrumb "Settings", admin_settings_path
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class ApplicationController < ::ActionController::Base
|
|
4
|
+
include ApplicationHelper
|
|
5
|
+
include ::ApplicationHelper
|
|
6
|
+
|
|
7
|
+
protect_from_forgery with: :exception
|
|
8
|
+
|
|
9
|
+
# Add flash types for improved alert support with Tailwind
|
|
10
|
+
add_flash_types :success, :warning, :error, :info
|
|
11
|
+
|
|
12
|
+
before_action :set_current_request_details
|
|
13
|
+
|
|
14
|
+
helper_method :breadcrumbs
|
|
15
|
+
helper_method :current_user
|
|
16
|
+
helper_method :user_signed_in?
|
|
17
|
+
|
|
18
|
+
def breadcrumbs
|
|
19
|
+
@breadcrumbs ||= []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_breadcrumb(name, path = nil)
|
|
23
|
+
breadcrumbs << Breadcrumb.new(name, path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Set the current request details
|
|
27
|
+
# @return [void]
|
|
28
|
+
def set_current_request_details
|
|
29
|
+
Panda::CMS::Current.request_id = request.uuid
|
|
30
|
+
Panda::CMS::Current.user_agent = request.user_agent
|
|
31
|
+
Panda::CMS::Current.ip_address = request.ip
|
|
32
|
+
Panda::CMS::Current.root = request.base_url
|
|
33
|
+
Panda::CMS::Current.page = nil
|
|
34
|
+
Panda::CMS::Current.user ||= User.find_by(id: session[:user_id]) if session[:user_id]
|
|
35
|
+
|
|
36
|
+
Panda::CMS.config.url ||= Panda::CMS::Current.root
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def authenticate_user!
|
|
40
|
+
redirect_to root_path, flash: {error: "Please login to view this!"} unless user_signed_in?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def authenticate_admin_user!
|
|
44
|
+
redirect_to root_path, flash: {error: "Please login to view this!"} unless user_signed_in? && current_user.admin?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Required for paper_trail and seems as good as convention these days
|
|
48
|
+
def current_user
|
|
49
|
+
Panda::CMS::Current.user
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def user_signed_in?
|
|
53
|
+
!!Panda::CMS::Current.user
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class ErrorsController < ApplicationController
|
|
4
|
+
layout "error"
|
|
5
|
+
|
|
6
|
+
def show
|
|
7
|
+
exception = request.env["action_dispatch.exception"]
|
|
8
|
+
status_code = exception.try(:status_code) || ActionDispatch::ExceptionWrapper.new(request.env, exception).status_code
|
|
9
|
+
|
|
10
|
+
render view_for_code(status_code), status: status_code
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def error_503
|
|
14
|
+
render view_for_code(503), status: 503
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def view_for_code(code)
|
|
20
|
+
supported_error_codes.fetch(code) { "404" }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def supported_error_codes
|
|
24
|
+
{
|
|
25
|
+
403 => "403",
|
|
26
|
+
404 => "404",
|
|
27
|
+
500 => "500",
|
|
28
|
+
503 => "503"
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class FormSubmissionsController < ApplicationController
|
|
4
|
+
def create
|
|
5
|
+
vars = params.except(:authenticity_token, :controller, :action, :id)
|
|
6
|
+
|
|
7
|
+
form = Panda::CMS::Form.find(params[:id])
|
|
8
|
+
form_submission = Panda::CMS::FormSubmission.create(form_id: params[:id], data: vars.to_unsafe_h)
|
|
9
|
+
form.update(submission_count: form.submission_count + 1)
|
|
10
|
+
|
|
11
|
+
Panda::CMS::FormMailer.notification_email(form: form, form_submission: form_submission).deliver_now
|
|
12
|
+
|
|
13
|
+
if (completion_path = form&.completion_path)
|
|
14
|
+
redirect_to completion_path
|
|
15
|
+
else
|
|
16
|
+
# TODO: This isn't a great fallback, we should do something nice here...
|
|
17
|
+
# Perhaps a simple JS alert when sent?
|
|
18
|
+
redirect_to "/"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class PagesController < ApplicationController
|
|
4
|
+
include ActionView::Helpers::TagHelper
|
|
5
|
+
|
|
6
|
+
before_action :check_login_required, only: [:root, :show]
|
|
7
|
+
after_action :record_visit, only: [:root, :show], unless: :ignore_visit?
|
|
8
|
+
|
|
9
|
+
def root
|
|
10
|
+
params[:path] = ""
|
|
11
|
+
show
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show
|
|
15
|
+
page = if @overrides&.dig(:page_path_match)
|
|
16
|
+
Panda::CMS::Page.find_by(path: @overrides.dig(:page_path_match))
|
|
17
|
+
else
|
|
18
|
+
Panda::CMS::Page.find_by(path: "/" + params[:path].to_s)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Panda::CMS::Current.page = page || Panda::CMS::Page.find_by(path: "/404")
|
|
22
|
+
if @overrides
|
|
23
|
+
Panda::CMS::Current.page.title = @overrides&.dig(:title) || page.title
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
layout = page&.template&.file_path
|
|
27
|
+
|
|
28
|
+
if page.nil? || page.status == "archived" || layout.nil?
|
|
29
|
+
# This works for now, but we may want to override in future (e.g. custom 404s)
|
|
30
|
+
render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found and return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
template_vars = {
|
|
34
|
+
page: page,
|
|
35
|
+
title: Panda::CMS::Current.page&.title || Panda::CMS.config.title
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
render inline: "", assigns: template_vars, status: :ok, layout: layout
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def check_login_required
|
|
44
|
+
if Panda::CMS.config.require_login_to_view && !user_signed_in?
|
|
45
|
+
redirect_to panda_cms_maintenance_path and return
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ignore_visit?
|
|
50
|
+
# Ignore visits from bots (TODO: make this configurable)
|
|
51
|
+
return true if /bot/i.match?(request.user_agent)
|
|
52
|
+
# Ignore visits from Honeybadger
|
|
53
|
+
return true if request.headers.to_h.key? "Honeybadger-Token"
|
|
54
|
+
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def record_visit
|
|
59
|
+
RecordVisitJob.perform_later(
|
|
60
|
+
url: request.url,
|
|
61
|
+
user_agent: request.user_agent,
|
|
62
|
+
referrer: request.referrer,
|
|
63
|
+
ip_address: request.remote_ip,
|
|
64
|
+
page_id: Panda::CMS::Current.page&.id,
|
|
65
|
+
current_user_id: current_user&.id,
|
|
66
|
+
params: params.to_unsafe_h.except(:controller, :action, :path),
|
|
67
|
+
visited_at: Time.zone.now
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class PostsController < ApplicationController
|
|
4
|
+
def show
|
|
5
|
+
@posts_index_page = Panda::CMS::Page.find_by(path: "/#{Panda::CMS.config.posts[:prefix]}")
|
|
6
|
+
@post = Panda::CMS::Post.find_by!(slug: "/#{params[:slug]}")
|
|
7
|
+
@title = @post.title
|
|
8
|
+
|
|
9
|
+
render inline: "", status: :ok, layout: "layouts/post"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
module Admin
|
|
4
|
+
module PostsHelper
|
|
5
|
+
def editor_content_for(post, preserved_content = nil)
|
|
6
|
+
Rails.logger.debug "Editor content for post: #{post.inspect}"
|
|
7
|
+
Rails.logger.debug "Preserved content: #{preserved_content.inspect}"
|
|
8
|
+
Rails.logger.debug "Post content: #{post.content.inspect}"
|
|
9
|
+
|
|
10
|
+
content = if preserved_content.present?
|
|
11
|
+
# If preserved_content is a string (JSON), parse it
|
|
12
|
+
begin
|
|
13
|
+
preserved_content.is_a?(String) ? JSON.parse(preserved_content) : preserved_content
|
|
14
|
+
rescue JSON::ParserError => e
|
|
15
|
+
Rails.logger.error "Failed to parse preserved content: #{e.message}"
|
|
16
|
+
post.content
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
post.content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Rails.logger.debug "Using content: #{content.inspect}"
|
|
23
|
+
|
|
24
|
+
# Ensure we have a valid editor content structure
|
|
25
|
+
content = if content.blank? || !content.is_a?(Hash) || !content.key?("blocks")
|
|
26
|
+
Rails.logger.debug "Creating new editor content structure"
|
|
27
|
+
{
|
|
28
|
+
time: Time.now.to_i * 1000,
|
|
29
|
+
blocks: [],
|
|
30
|
+
version: "2.28.2"
|
|
31
|
+
}
|
|
32
|
+
else
|
|
33
|
+
# Ensure we have all required fields
|
|
34
|
+
Rails.logger.debug "Ensuring required fields are present"
|
|
35
|
+
content["time"] ||= Time.now.to_i * 1000
|
|
36
|
+
content["version"] ||= "2.28.2"
|
|
37
|
+
content["blocks"] ||= []
|
|
38
|
+
content
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
json = content.to_json
|
|
42
|
+
Rails.logger.debug "Returning JSON: #{json}"
|
|
43
|
+
json
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
module ApplicationHelper
|
|
4
|
+
#
|
|
5
|
+
# Helper method to render a ViewComponent
|
|
6
|
+
# @see ViewComponent::Rendering#render
|
|
7
|
+
# @usage <%= component "example", title: "Hello World!" %>
|
|
8
|
+
#
|
|
9
|
+
def component(name, *, **, &)
|
|
10
|
+
component = name.to_s.camelize.constantize::Component
|
|
11
|
+
render(component.new(*, **), &)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# TODO: Expose this elsewhere later, or you have to include helpers
|
|
15
|
+
# in the calling application
|
|
16
|
+
def title_tag
|
|
17
|
+
if @breadcrumbs.present?
|
|
18
|
+
"#{@breadcrumbs.last&.name} · #{Panda::CMS.config.title}".html_safe
|
|
19
|
+
elsif @title.present?
|
|
20
|
+
"#{@title} · #{Panda::CMS.config.title}".html_safe
|
|
21
|
+
else
|
|
22
|
+
Panda::CMS.config.title
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
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")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def active_link?(path, match: :starts_with)
|
|
33
|
+
if match == :starts_with
|
|
34
|
+
return request.path.starts_with?(path)
|
|
35
|
+
elsif match == :exact
|
|
36
|
+
return (request.path == path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def block_link_to(name = nil, options = nil, html_options = {}, &)
|
|
43
|
+
html_options[:class] = "block-link"
|
|
44
|
+
link_to(name, options, html_options, &)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def panda_cms_form_with(**options, &)
|
|
48
|
+
options[:builder] = Panda::CMS::FormBuilder
|
|
49
|
+
options[:class] = ["block visible p-6 bg-mid/5 rounded-lg border-mid border", options[:class]].compact.join(" ")
|
|
50
|
+
form_with(**options, &)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def nav_class(mode)
|
|
54
|
+
if mode == "mobile"
|
|
55
|
+
"-mx-3 block rounded-lg px-3 py-2 font-semibold leading-6 text-white hover:text-white hover:underline focus:underline"
|
|
56
|
+
else
|
|
57
|
+
"font-semibold leading-6 text-white hover:text-white hover:underline focus:underline"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def selected_nav_highlight_colour_classes(request)
|
|
62
|
+
"bg-mid text-white relative flex transition-all py-3 px-2 mb-2 rounded-md group flex gap-x-3 rounded-md text-base leading-6 font-normal "
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def nav_highlight_colour_classes(request)
|
|
66
|
+
"text-white hover:bg-mid/60 transition-all group flex gap-x-3 py-3 px-2 mb-2 rounded-md text-base leading-6 font-normal "
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def level_indent(level)
|
|
70
|
+
case level
|
|
71
|
+
when 0
|
|
72
|
+
"ml-0"
|
|
73
|
+
when 1
|
|
74
|
+
"ml-4"
|
|
75
|
+
when 2
|
|
76
|
+
"ml-8"
|
|
77
|
+
when 3
|
|
78
|
+
"ml-12"
|
|
79
|
+
when 4
|
|
80
|
+
"ml-16"
|
|
81
|
+
when 5
|
|
82
|
+
"ml-20"
|
|
83
|
+
when 6
|
|
84
|
+
"ml-24"
|
|
85
|
+
when 7
|
|
86
|
+
"ml-28"
|
|
87
|
+
when 8
|
|
88
|
+
"ml-32"
|
|
89
|
+
when 9
|
|
90
|
+
"ml-36"
|
|
91
|
+
when 10
|
|
92
|
+
"ml-40" # We can go to 72...
|
|
93
|
+
else
|
|
94
|
+
"ml-48"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def table_indent(item_with_level_attribute)
|
|
99
|
+
level_indent(item_with_level_attribute.level)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def menu_indent(item_with_level_attribute, indent_with: "pl-")
|
|
103
|
+
case item_with_level_attribute.level
|
|
104
|
+
when 0
|
|
105
|
+
"#{indent_with}0"
|
|
106
|
+
when 1
|
|
107
|
+
"#{indent_with}4"
|
|
108
|
+
when 2
|
|
109
|
+
"#{indent_with}8"
|
|
110
|
+
when 3
|
|
111
|
+
"#{indent_with}12"
|
|
112
|
+
when 4
|
|
113
|
+
"#{indent_with}16"
|
|
114
|
+
else
|
|
115
|
+
"#{indent_with}20"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
module ThemeHelper
|
|
4
|
+
# TODO: Move these into one method?
|
|
5
|
+
def h1(text, icon: "", additional_styles: "")
|
|
6
|
+
render HeadingComponent.new(text: text, level: 1, icon: icon, additional_styles: additional_styles)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def h2(text, icon: "", additional_styles: "")
|
|
10
|
+
render HeadingComponent.new(text: text, level: 2, icon: icon, additional_styles: additional_styles)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def h3(text, icon: "", additional_styles: "")
|
|
14
|
+
render HeadingComponent.new(text: text, level: 3, icon: icon, additional_styles: additional_styles)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|