panda-cms 0.8.2 → 0.10.2
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 +75 -5
- data/app/components/panda/cms/code_component.rb +154 -39
- data/app/components/panda/cms/grid_component.rb +26 -6
- data/app/components/panda/cms/menu_component.rb +72 -34
- data/app/components/panda/cms/page_menu_component.rb +102 -13
- data/app/components/panda/cms/rich_text_component.rb +229 -139
- data/app/components/panda/cms/text_component.rb +107 -42
- data/app/controllers/panda/cms/admin/base_controller.rb +19 -3
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +3 -3
- data/app/controllers/panda/cms/admin/files_controller.rb +7 -0
- data/app/controllers/panda/cms/admin/menus_controller.rb +47 -3
- data/app/controllers/panda/cms/admin/pages_controller.rb +11 -2
- data/app/controllers/panda/cms/admin/posts_controller.rb +3 -1
- data/app/controllers/panda/cms/form_submissions_controller.rb +134 -11
- data/app/controllers/panda/cms/pages_controller.rb +7 -2
- data/app/controllers/panda/cms/posts_controller.rb +16 -0
- data/app/helpers/panda/cms/application_helper.rb +17 -4
- data/app/helpers/panda/cms/asset_helper.rb +14 -61
- data/app/helpers/panda/cms/forms_helper.rb +60 -0
- data/app/helpers/panda/cms/seo_helper.rb +85 -0
- data/app/javascript/panda/cms/{application_panda_cms.js → application.js} +5 -1
- data/app/javascript/panda/cms/controllers/code_editor_controller.js +95 -0
- data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +31 -4
- data/app/javascript/panda/cms/controllers/file_gallery_controller.js +128 -0
- data/app/javascript/panda/cms/controllers/file_upload_controller.js +165 -0
- data/app/javascript/panda/cms/controllers/index.js +54 -13
- data/app/javascript/panda/cms/controllers/inline_code_editor_controller.js +96 -0
- data/app/javascript/panda/cms/controllers/menu_form_controller.js +53 -0
- data/app/javascript/panda/cms/controllers/nested_form_controller.js +35 -0
- data/app/javascript/panda/cms/controllers/page_form_controller.js +454 -0
- data/app/javascript/panda/cms/controllers/tree_controller.js +214 -0
- data/app/javascript/panda/cms/stimulus-loading.js +6 -7
- data/app/models/panda/cms/block_content.rb +9 -0
- data/app/models/panda/cms/menu.rb +12 -0
- data/app/models/panda/cms/page.rb +147 -0
- data/app/models/panda/cms/post.rb +98 -0
- data/app/views/layouts/homepage.html.erb +1 -4
- data/app/views/layouts/page.html.erb +1 -4
- data/app/views/panda/cms/admin/dashboard/show.html.erb +5 -5
- data/app/views/panda/cms/admin/files/_file_details.html.erb +45 -0
- data/app/views/panda/cms/admin/files/index.html.erb +11 -118
- data/app/views/panda/cms/admin/forms/index.html.erb +2 -2
- data/app/views/panda/cms/admin/forms/new.html.erb +1 -2
- data/app/views/panda/cms/admin/forms/show.html.erb +15 -30
- data/app/views/panda/cms/admin/menus/_menu_item_fields.html.erb +11 -0
- data/app/views/panda/cms/admin/menus/edit.html.erb +62 -0
- data/app/views/panda/cms/admin/menus/index.html.erb +3 -2
- data/app/views/panda/cms/admin/menus/new.html.erb +38 -0
- data/app/views/panda/cms/admin/pages/edit.html.erb +147 -22
- data/app/views/panda/cms/admin/pages/index.html.erb +49 -11
- data/app/views/panda/cms/admin/pages/new.html.erb +3 -11
- data/app/views/panda/cms/admin/posts/_form.html.erb +44 -15
- data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +6 -6
- 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 +3 -3
- data/app/views/shared/_header.html.erb +1 -4
- data/config/brakeman.ignore +38 -0
- data/config/importmap.rb +10 -10
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb.disabled +31 -0
- data/config/initializers/panda/cms.rb +52 -10
- data/config/locales/en.yml +41 -0
- data/config/routes.rb +5 -3
- data/db/migrate/20240305000000_convert_html_content_to_editor_js.rb +2 -2
- data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +6 -1
- data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +23 -21
- data/db/migrate/20251104150640_add_cached_last_updated_at_to_panda_cms_pages.rb +22 -0
- data/db/migrate/20251104172242_add_page_type_to_panda_cms_pages.rb +6 -0
- data/db/migrate/20251104172638_set_page_types_for_existing_pages.rb +27 -0
- data/db/migrate/20251105000001_add_pending_review_status_to_pages_and_posts.panda_cms.rb +21 -0
- data/db/migrate/20251109131150_add_seo_fields_to_pages.rb +32 -0
- data/db/migrate/20251109131205_add_seo_fields_to_posts.rb +27 -0
- data/db/migrate/20251110114258_add_spam_tracking_to_form_submissions.rb +7 -0
- data/db/migrate/20251110122812_add_performance_indexes_to_pages_and_redirects.rb +13 -0
- data/lib/generators/panda/cms/install_generator.rb +2 -5
- data/lib/panda/cms/asset_loader.rb +46 -76
- data/lib/panda/cms/bulk_editor.rb +288 -12
- data/lib/panda/cms/debug.rb +29 -0
- data/lib/panda/cms/engine/asset_config.rb +49 -0
- data/lib/panda/cms/engine/autoload_config.rb +19 -0
- data/lib/panda/cms/engine/backtrace_config.rb +42 -0
- data/lib/panda/cms/engine/core_config.rb +106 -0
- data/lib/panda/cms/engine/helper_config.rb +20 -0
- data/lib/panda/cms/engine/route_config.rb +34 -0
- data/lib/panda/cms/engine/view_component_config.rb +31 -0
- data/lib/panda/cms/engine.rb +44 -162
- data/lib/panda/cms/features.rb +52 -0
- data/lib/panda/cms.rb +10 -0
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +20 -7
- data/lib/tasks/panda_cms_tasks.rake +16 -0
- metadata +41 -50
- data/app/components/panda/cms/admin/container_component.html.erb +0 -13
- data/app/components/panda/cms/admin/flash_message_component.html.erb +0 -31
- data/app/components/panda/cms/admin/panel_component.html.erb +0 -7
- data/app/components/panda/cms/admin/slideover_component.html.erb +0 -9
- data/app/components/panda/cms/admin/slideover_component.rb +0 -15
- data/app/components/panda/cms/admin/statistics_component.html.erb +0 -4
- data/app/components/panda/cms/admin/statistics_component.rb +0 -16
- data/app/components/panda/cms/admin/tab_bar_component.html.erb +0 -35
- data/app/components/panda/cms/admin/tab_bar_component.rb +0 -15
- data/app/components/panda/cms/admin/table_component.html.erb +0 -29
- data/app/components/panda/cms/admin/user_activity_component.html.erb +0 -7
- data/app/components/panda/cms/admin/user_activity_component.rb +0 -20
- data/app/components/panda/cms/admin/user_display_component.html.erb +0 -17
- data/app/components/panda/cms/admin/user_display_component.rb +0 -21
- data/app/components/panda/cms/grid_component.html.erb +0 -6
- data/app/components/panda/cms/menu_component.html.erb +0 -6
- data/app/components/panda/cms/page_menu_component.html.erb +0 -21
- data/app/components/panda/cms/rich_text_component.html.erb +0 -90
- data/app/javascript/panda_cms/stimulus-loading.js +0 -39
- data/app/views/layouts/panda/cms/application.html.erb +0 -42
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +0 -28
- data/app/views/panda/cms/admin/shared/_flash.html.erb +0 -5
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +0 -41
- data/app/views/panda/cms/shared/_footer.html.erb +0 -2
- data/app/views/panda/cms/shared/_header.html.erb +0 -25
- data/app/views/panda/cms/shared/_importmap.html.erb +0 -34
- data/config/initializers/inflections.rb +0 -5
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb +0 -13
- data/lib/tasks/assets.rake +0 -587
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<div class="fixed top-2 right-2 z-[9999] p-2 space-y-4 w-full max-w-sm sm:items-end"
|
|
2
|
-
data-controller="alert"
|
|
3
|
-
<% if @temporary %> data-alert-dismiss-after-value="3000"<% end %>
|
|
4
|
-
data-transition-enter="ease-in-out duration-500"
|
|
5
|
-
data-transition-enter-from="translate-x-full opacity-0"
|
|
6
|
-
data-transition-enter-to="translate-x-0 opacity-100"
|
|
7
|
-
data-transition-leave="ease-in-out duration-500"
|
|
8
|
-
data-transition-leave-from="translate-x-0 opacity-100"
|
|
9
|
-
data-transition-leave-to="translate-x-full opacity-0">
|
|
10
|
-
<div class="overflow-hidden w-full max-w-sm bg-white rounded-lg ring-1 ring-black ring-opacity-5 shadow-lg">
|
|
11
|
-
<div class="p-4">
|
|
12
|
-
<div class="flex items-start">
|
|
13
|
-
<div class="flex-shrink-0">
|
|
14
|
-
<i class="fa-regular text-xl <%= icon_css %> <%= text_colour_css %>"></i>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="flex-1 pt-0.5 ml-3 w-0">
|
|
17
|
-
<p class="mb-1 text-sm font-medium flash-message-title <%= text_colour_css %>"><%= kind.to_s.titleize %></p>
|
|
18
|
-
<p class="mt-1 mb-0 text-sm text-gray-500 flash-message-text"><%= message %></p>
|
|
19
|
-
</div>
|
|
20
|
-
<div class="flex flex-shrink-0 ml-4">
|
|
21
|
-
<button data-action="alert#close" type="button" class="inline-flex text-gray-400 bg-white rounded-md transition duration-150 ease-in-out hover:text-gray-500 focus:ring-2 focus:ring-offset-2 focus:outline-none focus:ring-sky-500">
|
|
22
|
-
<span class="sr-only">Close</span>
|
|
23
|
-
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
24
|
-
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
|
25
|
-
</svg>
|
|
26
|
-
</button>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Panda
|
|
4
|
-
module CMS
|
|
5
|
-
module Admin
|
|
6
|
-
class StatisticsComponent < ViewComponent::Base
|
|
7
|
-
attr_reader :metric, :value
|
|
8
|
-
|
|
9
|
-
def initialize(metric:, value:)
|
|
10
|
-
@metric = metric
|
|
11
|
-
@value = value
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<div class="mt-3 sm:mt-2">
|
|
2
|
-
<div class="sm:hidden">
|
|
3
|
-
<label for="tabs" class="sr-only">Select a tab</label>
|
|
4
|
-
<!-- Use an "onChange" listener to redirect the user to the selected tab URL. -->
|
|
5
|
-
<select id="tabs" name="tabs" class="block py-1.5 pr-10 pl-3 w-full text-gray-900 rounded-md border-0 ring-1 ring-inset focus:ring-2 focus:ring-inset ring-mid focus:border-panda-dark focus:ring-panda-dark">
|
|
6
|
-
<% tabs.each do |tab| %>
|
|
7
|
-
<option><%= tab %></option>
|
|
8
|
-
<% end %>
|
|
9
|
-
</select>
|
|
10
|
-
</div>
|
|
11
|
-
<div class="hidden sm:block">
|
|
12
|
-
<div class="flex items-center border-b border-gray-200">
|
|
13
|
-
<nav class="flex flex-1 -mb-px space-x-6 xl:space-x-8" aria-label="Tabs">
|
|
14
|
-
<!-- Current: "border-panda-dark text-panda-dark", Default: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" -->
|
|
15
|
-
<a href="#" aria-current="page" class="py-4 px-1 text-sm font-medium whitespace-nowrap border-b-2 border-panda-dark text-panda-dark">Recently Viewed</a>
|
|
16
|
-
<a href="#" class="py-4 px-1 text-sm font-medium text-gray-500 whitespace-nowrap border-b-2 border-transparent hover:text-gray-700 hover:border-gray-300">Recently Added</a>
|
|
17
|
-
<a href="#" class="py-4 px-1 text-sm font-medium text-gray-500 whitespace-nowrap border-b-2 border-transparent hover:text-gray-700 hover:border-gray-300">Favourited</a>
|
|
18
|
-
</nav>
|
|
19
|
-
<div class="hidden items-center p-0.5 ml-6 bg-gray-100 rounded-lg sm:flex">
|
|
20
|
-
<button type="button" class="p-1.5 text-gray-400 rounded-md hover:bg-white hover:shadow-sm focus:ring-2 focus:ring-inset focus:outline-none focus:ring-panda-dark">
|
|
21
|
-
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
22
|
-
<path fill-rule="evenodd" d="M2 3.75A.75.75 0 012.75 3h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 3.75zm0 4.167a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75zm0 4.166a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75zm0 4.167a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z" clip-rule="evenodd" />
|
|
23
|
-
</svg>
|
|
24
|
-
<span class="sr-only">Use list view</span>
|
|
25
|
-
</button>
|
|
26
|
-
<button type="button" class="p-1.5 ml-0.5 text-gray-400 bg-white rounded-md shadow-sm focus:ring-2 focus:ring-inset focus:outline-none focus:ring-panda-dark">
|
|
27
|
-
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
28
|
-
<path fill-rule="evenodd" d="M4.25 2A2.25 2.25 0 002 4.25v2.5A2.25 2.25 0 004.25 9h2.5A2.25 2.25 0 009 6.75v-2.5A2.25 2.25 0 006.75 2h-2.5zm0 9A2.25 2.25 0 002 13.25v2.5A2.25 2.25 0 004.25 18h2.5A2.25 2.25 0 009 15.75v-2.5A2.25 2.25 0 006.75 11h-2.5zm9-9A2.25 2.25 0 0011 4.25v2.5A2.25 2.25 0 0013.25 9h2.5A2.25 2.25 0 0018 6.75v-2.5A2.25 2.25 0 0015.75 2h-2.5zm0 9A2.25 2.25 0 0011 13.25v2.5A2.25 2.25 0 0013.25 18h2.5A2.25 2.25 0 0018 15.75v-2.5A2.25 2.25 0 0015.75 11h-2.5z" clip-rule="evenodd" />
|
|
29
|
-
</svg>
|
|
30
|
-
<span class="sr-only">Use grid view</span>
|
|
31
|
-
</button>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<div class="table overflow-x-auto mb-12 w-full rounded-lg border border-mid">
|
|
2
|
-
<div class="table-header-group">
|
|
3
|
-
<div class="table-row text-base font-medium text-white bg-mid">
|
|
4
|
-
<% columns.each_with_index do |column, i| %>
|
|
5
|
-
<div class="table-cell sticky top-0 z-10 p-4 <% if i.zero? %>rounded-tl-md<% elsif i == columns.size - 1 %>rounded-tr-md<% end %>"><%= column.label %></div>
|
|
6
|
-
<% end %>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<% if @rows.any? %>
|
|
11
|
-
<div class="table-row-group">
|
|
12
|
-
<% @rows.each do |row| %>
|
|
13
|
-
<div class="table-row relative bg-mid/5 hover:bg-mid/20" data-post-id="<%= row.id %>">
|
|
14
|
-
<% @columns.each do |column| %>
|
|
15
|
-
<div class="table-cell py-5 px-3 h-20 text-sm align-middle whitespace-nowrap border-b border-mid/20">
|
|
16
|
-
<%= view_context.capture(row, &column.cell) %>
|
|
17
|
-
</div>
|
|
18
|
-
<% end %>
|
|
19
|
-
</div>
|
|
20
|
-
<% end %>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
<% else %>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="text-center mx-12 block border border-dashed py-12 rounded-lg">
|
|
26
|
-
<h3 class="py-1 text-xl font-semibold text-gray-900">No <%= @term.pluralize %></h3>
|
|
27
|
-
<p class="py-1 text-base text-gray-500">Get started by creating a new <%= @term %>.</p>
|
|
28
|
-
</div>
|
|
29
|
-
<% end %>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
<% if user.is_a?(Panda::Core::User) && time %>
|
|
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") %>
|
|
5
|
-
<% elsif time %>
|
|
6
|
-
<div class="text-black/60"><%= time_ago_in_words(time) %> ago</div>
|
|
7
|
-
<% end %>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Panda
|
|
4
|
-
module CMS
|
|
5
|
-
module Admin
|
|
6
|
-
class UserActivityComponent < ViewComponent::Base
|
|
7
|
-
attr_accessor :model, :time, :user
|
|
8
|
-
|
|
9
|
-
# @param model [ActiveRecord::Base] Model instance to which the user activity is related
|
|
10
|
-
# @param at [ActiveSupport::TimeWithZone] Time of the activity
|
|
11
|
-
# @param user [Panda::Core::User] User who performed the activity
|
|
12
|
-
def initialize(model: nil, at: nil, user: nil)
|
|
13
|
-
@model = model
|
|
14
|
-
@user = user if user.is_a?(::Panda::Core::User)
|
|
15
|
-
@time = at if at.is_a?(::ActiveSupport::TimeWithZone)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<div class="block flex-shrink-0 group">
|
|
2
|
-
<div class="flex items-center">
|
|
3
|
-
<% unless user.image_url.empty? %>
|
|
4
|
-
<div>
|
|
5
|
-
<img class="inline-block w-10 h-10 rounded-full" src="<%= user.image_url %>" alt="">
|
|
6
|
-
</div>
|
|
7
|
-
<% else %>
|
|
8
|
-
<div class="inline-block w-10 h-10 bg-gray-200 rounded-full">
|
|
9
|
-
<span class="text-center"><i class="text-mid/50 text-4xl fa-sharp fa-circle-user fa-fw"></i></span>
|
|
10
|
-
</div>
|
|
11
|
-
<% end %>
|
|
12
|
-
<div class="ml-3">
|
|
13
|
-
<p class="text-sm text-black"><%= user.name %></p>
|
|
14
|
-
<% if metadata %><p class="text-sm text-black/60"><%= metadata %></p><% end %>
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Panda
|
|
4
|
-
module CMS
|
|
5
|
-
module Admin
|
|
6
|
-
class UserDisplayComponent < ViewComponent::Base
|
|
7
|
-
attr_accessor :user_id, :user, :metadata
|
|
8
|
-
|
|
9
|
-
def initialize(user_id: nil, user: nil, metadata: "")
|
|
10
|
-
@user = if user.nil? && user_id.present? && Panda::Core::User.find(user_id)
|
|
11
|
-
Panda::Core::User.find(user_id)
|
|
12
|
-
else
|
|
13
|
-
user
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
@metadata = metadata
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
<% @menu_items.each do |menu_item| %>
|
|
2
|
-
<a href="<%= menu_item.resolved_link %>" class="<%= menu_item.css_classes %>"><%= menu_item.text %></a>
|
|
3
|
-
<% if @render_page_menu && menu_item.page %>
|
|
4
|
-
<%= render Panda::CMS::PageMenuComponent.new(page: menu_item.page, start_depth: 1, styles: @page_menu_styles, show_heading: false) %>
|
|
5
|
-
<% end %>
|
|
6
|
-
<% end %>
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
<nav class="<%= styles[:container] %>">
|
|
2
|
-
<ul role="list" class="p-0 m-0">
|
|
3
|
-
<% if @show_heading %>
|
|
4
|
-
<li>
|
|
5
|
-
<a href="<%= menu_item.page.path %>" class="<%= menu_item.page == Panda::CMS::Current.page ? styles[:current_page_active] : styles[:current_page_inactive] %>">
|
|
6
|
-
<%= menu_item.text %>
|
|
7
|
-
</a>
|
|
8
|
-
</li>
|
|
9
|
-
<% end %>
|
|
10
|
-
<ul>
|
|
11
|
-
<% Panda::CMS::MenuItem.includes(:page).each_with_level(menu_item.descendants) do |submenu_item, level| %>
|
|
12
|
-
<% next if Panda::CMS::Current.page == menu_item.page && level > 1 # If we're on the "top" menu item, only show its direct ancestors %>
|
|
13
|
-
<% next if submenu_item.page&.path[/\:/] %>
|
|
14
|
-
<% next if submenu_item&.page.nil? || Panda::CMS::Current.page.nil? || (submenu_item.page&.depth&.to_i > Panda::CMS::Current.page&.depth&.to_i && !Panda::CMS::Current.page&.in?(submenu_item.page.ancestors)) %>
|
|
15
|
-
<li data-level="<%= level %>" data-page-id="<%= submenu_item.page.id %>" class="<%= submenu_item.page == Panda::CMS::Current.page ? @styles[:active] : @styles[:inactive] %>">
|
|
16
|
-
<a href="<%= submenu_item.page&.path %>" class="<%= helpers.menu_indent(submenu_item, indent_with: @styles[:indent_with]) %>"><%= submenu_item.page&.title %></a>
|
|
17
|
-
</li>
|
|
18
|
-
<% end %>
|
|
19
|
-
</ul>
|
|
20
|
-
</ul>
|
|
21
|
-
</nav>
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
editor_data = if @editable
|
|
3
|
-
begin
|
|
4
|
-
content = if @content.is_a?(String)
|
|
5
|
-
if @content.start_with?("{")
|
|
6
|
-
JSON.parse(@content)
|
|
7
|
-
else
|
|
8
|
-
# If it's HTML content, convert it to EditorJS format
|
|
9
|
-
{
|
|
10
|
-
"time" => Time.current.to_i * 1000,
|
|
11
|
-
"blocks" => [
|
|
12
|
-
{
|
|
13
|
-
"type" => "paragraph",
|
|
14
|
-
"data" => {
|
|
15
|
-
"text" => @content.to_s
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"version" => "2.28.2"
|
|
20
|
-
}
|
|
21
|
-
end
|
|
22
|
-
else
|
|
23
|
-
@content
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
content = content.deep_transform_keys(&:to_s)
|
|
27
|
-
content["blocks"] = (content["blocks"] || []).map do |block|
|
|
28
|
-
case block["type"]
|
|
29
|
-
when "paragraph"
|
|
30
|
-
block["data"] = block["data"].merge(
|
|
31
|
-
"text" => block["data"]["text"].to_s.presence || ""
|
|
32
|
-
)
|
|
33
|
-
when "header"
|
|
34
|
-
block["data"] = block["data"].merge(
|
|
35
|
-
"text" => block["data"]["text"].to_s.presence || "",
|
|
36
|
-
"level" => block["data"]["level"].to_i
|
|
37
|
-
)
|
|
38
|
-
when "list"
|
|
39
|
-
block["data"] = block["data"].merge(
|
|
40
|
-
"items" => (block["data"]["items"] || []).map { |item| item.to_s.presence || "" }
|
|
41
|
-
)
|
|
42
|
-
end
|
|
43
|
-
block
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
content["version"] ||= "2.28.2"
|
|
47
|
-
content["time"] ||= Time.current.to_i * 1000
|
|
48
|
-
|
|
49
|
-
Base64.strict_encode64(content.to_json)
|
|
50
|
-
rescue StandardError => e
|
|
51
|
-
Rails.logger.error("Error encoding editor data: #{e.message}")
|
|
52
|
-
Rails.logger.error("Original content: #{@content.inspect}")
|
|
53
|
-
# Fall back to a simple paragraph with the original content
|
|
54
|
-
fallback_content = {
|
|
55
|
-
"time" => Time.current.to_i * 1000,
|
|
56
|
-
"blocks" => [
|
|
57
|
-
{
|
|
58
|
-
"type" => "paragraph",
|
|
59
|
-
"data" => {
|
|
60
|
-
"text" => @content.to_s
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
],
|
|
64
|
-
"version" => "2.28.2"
|
|
65
|
-
}
|
|
66
|
-
Base64.strict_encode64(fallback_content.to_json)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
%>
|
|
70
|
-
<% if @editable %>
|
|
71
|
-
<div class="panda-cms-content"
|
|
72
|
-
data-editable-previous-data="<%= editor_data %>"
|
|
73
|
-
data-editable-content="<%= editor_data %>"
|
|
74
|
-
data-editable-initialized="false"
|
|
75
|
-
data-editable-version="2.28.2"
|
|
76
|
-
data-editable-autosave="false"
|
|
77
|
-
data-editable-tools='{"paragraph":true,"header":true,"list":true,"quote":true,"table":true}'
|
|
78
|
-
id="editor-<%= @options[:id] %>"
|
|
79
|
-
data-editable-kind="rich_text"
|
|
80
|
-
data-editable-block-content-id="<%= @options[:id] %>"
|
|
81
|
-
data-editable-page-id="<%= @options[:data][:page_id] %>"
|
|
82
|
-
data-controller="editor-js"
|
|
83
|
-
data-editor-js-initialized-value="false"
|
|
84
|
-
data-editor-js-content-value="<%= editor_data %>">
|
|
85
|
-
</div>
|
|
86
|
-
<% else %>
|
|
87
|
-
<div class="panda-cms-content">
|
|
88
|
-
<%= @content.presence || "<p></p>".html_safe %>
|
|
89
|
-
</div>
|
|
90
|
-
<% end %>
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Stimulus loading utilities for Panda CMS
|
|
2
|
-
// This provides the loading functionality that would normally come from stimulus-rails
|
|
3
|
-
|
|
4
|
-
import { Application } from "@hotwired/stimulus"
|
|
5
|
-
|
|
6
|
-
const application = Application.start()
|
|
7
|
-
|
|
8
|
-
// Configure debug mode based on environment
|
|
9
|
-
const railsEnv = document.body?.dataset?.environment || "production";
|
|
10
|
-
application.debug = railsEnv === "development"
|
|
11
|
-
window.Stimulus = application
|
|
12
|
-
|
|
13
|
-
// Auto-registration functionality
|
|
14
|
-
function eagerLoadControllersFrom(context) {
|
|
15
|
-
const definitions = []
|
|
16
|
-
for (const path of context.keys()) {
|
|
17
|
-
const module = context(path)
|
|
18
|
-
const controller = module.default
|
|
19
|
-
if (controller && path.match(/[_-]controller\.(js|ts)$/)) {
|
|
20
|
-
const name = path
|
|
21
|
-
.replace(/^.*\//, "")
|
|
22
|
-
.replace(/[_-]controller\.(js|ts)$/, "")
|
|
23
|
-
.replace(/_/g, "-")
|
|
24
|
-
definitions.push({ name, module: controller, filename: path })
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return definitions
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function lazyLoadControllersFrom(context) {
|
|
31
|
-
return eagerLoadControllersFrom(context)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Export the functions that stimulus-loading typically provides
|
|
35
|
-
export {
|
|
36
|
-
application,
|
|
37
|
-
eagerLoadControllersFrom,
|
|
38
|
-
lazyLoadControllersFrom
|
|
39
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
<%= render "panda/cms/shared/header", html_class: "h-full bg-white" %>
|
|
2
|
-
<div class="flex h-full" id="panda-container">
|
|
3
|
-
<div class="absolute top-0 w-full lg:flex lg:fixed lg:inset-y-0 lg:z-50 lg:flex-col lg:w-72">
|
|
4
|
-
<div class="flex overflow-y-auto flex-col gap-y-5 px-4 pb-4 max-h-16 bg-gradient-to-br lg:max-h-full grow from-dark to-mid" data-transition-enter="transition-all ease-in-out duration-300" data-transition-enter-from="m-h-16" data-transition-enter-to="max-h-full" data-transition-leave="transition-all ease-in-out duration-300" data-transition-leave-from="max-h-full" data-transition-leave-to="max-h-16">
|
|
5
|
-
<%= render "panda/cms/admin/shared/sidebar" %>
|
|
6
|
-
</div>
|
|
7
|
-
</div>
|
|
8
|
-
<div class="flex flex-col flex-1 mt-16 ml-0 lg:mt-0 lg:ml-72" id="panda-inner-container" <% if content_for :sidebar %> data-controller="toggle" data-action="keydown.esc->modal#close" tabindex="-1"<% end %>>
|
|
9
|
-
<section id="panda-main" class="flex flex-row h-full">
|
|
10
|
-
<div class="flex-1 h-full" id="panda-cms-primary-content">
|
|
11
|
-
<%= render "panda/cms/admin/shared/breadcrumbs" %>
|
|
12
|
-
<%= render "panda/cms/admin/shared/flash" %>
|
|
13
|
-
<%= yield %>
|
|
14
|
-
</div>
|
|
15
|
-
<% if content_for :sidebar %>
|
|
16
|
-
<div data-toggle-target="toggleable" class="hidden flex absolute right-0 flex-col h-full bg-white divide-y divide-gray-200 shadow-xl basis-3/12"
|
|
17
|
-
data-transition-enter="transform transition ease-in-out duration-500"
|
|
18
|
-
data-transition-enter-from="translate-x-full"
|
|
19
|
-
data-transition-enter-to="translate-x-0"
|
|
20
|
-
data-transition-leave="transform transition ease-in-out duration-500"
|
|
21
|
-
data-transition-leave-from="translate-x-full"
|
|
22
|
-
data-transition-leave-to="translate-x-0"
|
|
23
|
-
id="slideover">
|
|
24
|
-
<div class="overflow-y-auto flex-1 h-0">
|
|
25
|
-
<div class="py-3 px-4 mb-4 bg-black">
|
|
26
|
-
<div class="flex justify-between items-center">
|
|
27
|
-
<h2 class="text-base font-semibold leading-6 text-white" id="slideover-title"><i class="mr-2 fa-light fa-gear"></i> <%= yield :sidebar_title %> </h2>
|
|
28
|
-
<button type="button" data-action="click->toggle#toggle touch->toggle#toggle"><i class="font-bold text-white fa-regular fa-xmark right"></i></button>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
<div class="flex flex-col flex-1 justify-between">
|
|
32
|
-
<div class="px-4 space-y-6">
|
|
33
|
-
<%= yield :sidebar %>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
<% end %>
|
|
39
|
-
</section>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
<%= render "panda/cms/shared/footer" %>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<% breadcrumb_styles = "text-black/60 hover:text-black/80" %>
|
|
2
|
-
|
|
3
|
-
<div class="flex mt-1 -mb-1">
|
|
4
|
-
<nav aria-label="Breadcrumb" id="panda-breadcrumbs" class="grow">
|
|
5
|
-
<ol role="list" class="px-4 w-full sm:px-6">
|
|
6
|
-
<li class="inline-block">
|
|
7
|
-
<a href="/admin" class="<%= breadcrumb_styles %>">
|
|
8
|
-
<i class="fa fa-regular fa-house"></i>
|
|
9
|
-
<span class="sr-only">Home</span>
|
|
10
|
-
</a>
|
|
11
|
-
</li>
|
|
12
|
-
<% breadcrumbs.each do |crumb| %>
|
|
13
|
-
<li class="inline-block">
|
|
14
|
-
<i class="fa fa-regular fa-chevron-right font-light <%= breadcrumb_styles %> py-5 px-2"></i>
|
|
15
|
-
<a href="<%= crumb.path %>" class="text-sm font-normal <%= breadcrumb_styles %>"><%= crumb.name %></a>
|
|
16
|
-
</li>
|
|
17
|
-
<% end %>
|
|
18
|
-
</ol>
|
|
19
|
-
</nav>
|
|
20
|
-
|
|
21
|
-
<% if content_for :sidebar %>
|
|
22
|
-
<div class="pt-4 pr-8 text-black/80" tabindex="-1" data-controller="toggle">
|
|
23
|
-
<button type="button" id="slideover-toggle" data-action="click->toggle#toggle touch->toggle#toggle" class="text-sm font-light">
|
|
24
|
-
<i class="mr-1 fa-light fa-gear"></i> <%= yield :sidebar_title %>
|
|
25
|
-
</button>
|
|
26
|
-
</div>
|
|
27
|
-
<% end %>
|
|
28
|
-
</div>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<nav class="flex flex-col flex-1">
|
|
2
|
-
<ul role="list" class="flex flex-col flex-1">
|
|
3
|
-
<a class="block p-0 mt-4 mb-4 ml-2 text-xl font-medium text-white"><%= Panda::CMS.config.title %></a>
|
|
4
|
-
<% [
|
|
5
|
-
{ path: admin_cms_dashboard_path, text: "Dashboard", icon: "fa-house", matcher: :exact },
|
|
6
|
-
{ path: admin_cms_pages_path, text: "Pages", icon: "fa-page", matcher: :starts_with },
|
|
7
|
-
{ path: admin_cms_posts_path, text: "Posts", icon: "fa-notebook", matcher: :starts_with },
|
|
8
|
-
{ path: admin_cms_forms_path, text: "Forms", icon: "fa-cabinet-filing", matcher: :starts_with },
|
|
9
|
-
{ path: admin_cms_menus_path, text: "Menus", icon: "fa-list-dropdown", matcher: :starts_with },
|
|
10
|
-
{ path: admin_cms_settings_path, text: "Settings", icon: "fa-cog", matcher: :starts_with },
|
|
11
|
-
].each do |link| %>
|
|
12
|
-
<li>
|
|
13
|
-
<%= link_to link[:path], class: "#{active_link?(link[:path], match: link[:matcher]) ? selected_nav_highlight_colour_classes(request) : nav_highlight_colour_classes(request)}" do %>
|
|
14
|
-
<span class="text-center"><i class="fa-regular <%= link[:icon] %> text-xl fa-fw"></i></span>
|
|
15
|
-
<span class="mt-1"><%= link[:text] %></span>
|
|
16
|
-
<% end %>
|
|
17
|
-
</li>
|
|
18
|
-
<% end %>
|
|
19
|
-
<li>
|
|
20
|
-
<%= button_to panda_core.admin_logout_path, method: :delete, id: "logout-link", data: { turbo: false }, class: nav_highlight_colour_classes(request) + " w-full" do %>
|
|
21
|
-
<span class="text-center"><i class="text-xl fa-regular fa-door-open fa-fw"></i></span>
|
|
22
|
-
<span class="mt-1">Logout</span>
|
|
23
|
-
<% end %>
|
|
24
|
-
</li>
|
|
25
|
-
<li class="mt-auto">
|
|
26
|
-
<%= link_to panda_core.edit_admin_my_profile_path, class: nav_highlight_colour_classes(request) + " w-full", title: "Edit my Profile" do %>
|
|
27
|
-
<% if !current_user.image_url.to_s.empty? %>
|
|
28
|
-
<span class="text-center"><img src="<%= current_user.image_url %>" class="w-auto h-7 rounded-full"></span>
|
|
29
|
-
<% else %>
|
|
30
|
-
<span class="text-center"><i class="text-xl fa-regular fa-circle-user fa-fw"></i></span>
|
|
31
|
-
<% end %>
|
|
32
|
-
<span class="mt-0.5"><%= current_user.firstname %> <%= current_user.lastname %></span>
|
|
33
|
-
<% end %>
|
|
34
|
-
</li>
|
|
35
|
-
<li class="">
|
|
36
|
-
<img src="/panda-cms-assets/panda-nav.png" class="inline py-2 mr-1 ml-0.5 w-auto h-14">
|
|
37
|
-
<span class="text-xs text-white">v<%= Panda::CMS::VERSION %> | <span class="font-semibold">License:</span> Perpetual</span>
|
|
38
|
-
<a class="absolute right-0 mt-4 mr-5 text-lg text-white lg:hidden" href="#" data-action="click->panda-cms-menu#toggle touch->panda-cms-menu#toggle"><i class="mr-2 text-white fa-sharp fa-solid fa-bars"></i> Menu</a>
|
|
39
|
-
</li>
|
|
40
|
-
</ul>
|
|
41
|
-
</nav>
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html
|
|
3
|
-
data-theme="<%= Panda::CMS::Current&.user&.current_theme || "default" %>"
|
|
4
|
-
class="<%= html_class ||= "" %>">
|
|
5
|
-
<head>
|
|
6
|
-
<title><%= title_tag %></title>
|
|
7
|
-
<%= csrf_meta_tags %>
|
|
8
|
-
<%= csp_meta_tag %>
|
|
9
|
-
<script src="https://kit.fontawesome.com/7835d81e75.js" defer="true" crossorigin="anonymous"></script>
|
|
10
|
-
<%= stylesheet_link_tag "panda.cms", "data-turbo-track": "reload", media: "all" %>
|
|
11
|
-
|
|
12
|
-
<% if Panda::CMS::AssetLoader.use_github_assets? || Rails.env.test? %>
|
|
13
|
-
<!-- Using compiled Panda CMS assets -->
|
|
14
|
-
<%= panda_cms_complete_assets %>
|
|
15
|
-
<% else %>
|
|
16
|
-
<!-- Using development importmap assets -->
|
|
17
|
-
<script async src="https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js" defer="true" crossorigin="anonymous"></script>
|
|
18
|
-
<%= render "panda/cms/shared/importmap" %>
|
|
19
|
-
<% end %>
|
|
20
|
-
|
|
21
|
-
<%= render "panda/cms/shared/favicons" %>
|
|
22
|
-
<%= yield :head %>
|
|
23
|
-
</head>
|
|
24
|
-
<body class="overflow-hidden h-full" data-environment="<%= Rails.env %>">
|
|
25
|
-
<%= render "panda/cms/admin/shared/flash" %>
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
<script type="importmap" data-turbo-track="reload">
|
|
2
|
-
<%=
|
|
3
|
-
# This ended up being the best solution to not drag in the importmap from the host app when we're editing the site
|
|
4
|
-
JSON.pretty_generate({
|
|
5
|
-
imports: {
|
|
6
|
-
# Main loader
|
|
7
|
-
"application_panda_cms": asset_path("panda/cms/application_panda_cms.js"),
|
|
8
|
-
# Vendored
|
|
9
|
-
"@hotwired/turbo": asset_path("panda/cms/@hotwired--turbo.js"),
|
|
10
|
-
"@hotwired/stimulus": asset_path("panda/cms/@hotwired--stimulus.js"),
|
|
11
|
-
"@hotwired/stimulus-loading": asset_path("panda_cms/stimulus-loading.js"),
|
|
12
|
-
"@editorjs/editorjs": asset_path("panda/cms/@editorjs--editorjs.js"),
|
|
13
|
-
"tailwindcss-stimulus-components": asset_path("panda/cms/tailwindcss-stimulus-components.js"),
|
|
14
|
-
# Our page editor
|
|
15
|
-
"panda/cms/editor/plain_text_editor": asset_path("panda/cms/editor/plain_text_editor.js"),
|
|
16
|
-
"panda/cms/editor/resource_loader": asset_path("panda/cms/editor/resource_loader.js"),
|
|
17
|
-
"panda/cms/editor/editor_js_initializer": asset_path("panda/cms/editor/editor_js_initializer.js"),
|
|
18
|
-
"panda/cms/editor/editor_js_config": asset_path("panda/cms/editor/editor_js_config.js"),
|
|
19
|
-
"panda/cms/editor/css_extractor": asset_path("panda/cms/editor/css_extractor.js"),
|
|
20
|
-
# Our controllers
|
|
21
|
-
"panda/cms/controllers/dashboard_controller": asset_path("panda/cms/controllers/dashboard_controller.js"),
|
|
22
|
-
"panda/cms/controllers/slug_controller": asset_path("panda/cms/controllers/slug_controller.js"),
|
|
23
|
-
"panda/cms/controllers/editor_form_controller": asset_path("panda/cms/controllers/editor_form_controller.js"),
|
|
24
|
-
"panda/cms/controllers/editor_iframe_controller": asset_path("panda/cms/controllers/editor_iframe_controller.js"),
|
|
25
|
-
"panda/cms/controllers/theme_form_controller": asset_path("panda/cms/controllers/theme_form_controller.js"),
|
|
26
|
-
# Main controller loader
|
|
27
|
-
"controllers": asset_path("panda/cms/controllers/index.js"),
|
|
28
|
-
}
|
|
29
|
-
}).html_safe
|
|
30
|
-
%>
|
|
31
|
-
</script>
|
|
32
|
-
<script type="module">
|
|
33
|
-
import "application_panda_cms"
|
|
34
|
-
</script>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "silencer/rails/logger"
|
|
4
|
-
|
|
5
|
-
# Don't log requests to the healthcheck endpoint
|
|
6
|
-
Rails.application.configure do
|
|
7
|
-
config.middleware.swap(
|
|
8
|
-
Rails::Rack::Logger,
|
|
9
|
-
Silencer::Logger,
|
|
10
|
-
config.log_tags,
|
|
11
|
-
silence: ["/up"]
|
|
12
|
-
)
|
|
13
|
-
end
|