panda-cms 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/panda/cms/admin/user_activity_component.html.erb +2 -2
  3. data/app/components/panda/cms/admin/user_activity_component.rb +2 -2
  4. data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
  5. data/app/components/panda/cms/admin/user_display_component.rb +2 -2
  6. data/app/components/panda/cms/rich_text_component.rb +5 -5
  7. data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
  8. data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
  9. data/app/controllers/panda/cms/admin/dashboard_controller.rb +5 -4
  10. data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
  11. data/app/controllers/panda/cms/admin/forms_controller.rb +3 -4
  12. data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
  13. data/app/controllers/panda/cms/admin/pages_controller.rb +7 -7
  14. data/app/controllers/panda/cms/admin/posts_controller.rb +9 -10
  15. data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +4 -4
  16. data/app/controllers/panda/cms/admin/settings_controller.rb +2 -3
  17. data/app/controllers/panda/cms/application_controller.rb +13 -5
  18. data/app/controllers/panda/cms/pages_controller.rb +2 -2
  19. data/app/helpers/panda/cms/application_helper.rb +3 -3
  20. data/app/helpers/panda/cms/asset_helper.rb +14 -1
  21. data/app/javascript/panda/cms/application_panda_cms.js +2 -34
  22. data/app/javascript/panda/cms/controllers/index.js +5 -15
  23. data/app/models/panda/cms/block_content.rb +1 -1
  24. data/app/models/panda/cms/current.rb +3 -12
  25. data/app/models/panda/cms/post.rb +3 -3
  26. data/app/models/panda/cms/visit.rb +1 -1
  27. data/app/views/layouts/panda/cms/application.html.erb +1 -1
  28. data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
  29. data/app/views/panda/cms/admin/files/index.html.erb +1 -1
  30. data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
  31. data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
  32. data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
  33. data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
  34. data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
  35. data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
  36. data/app/views/panda/cms/admin/pages/new.html.erb +2 -2
  37. data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
  38. data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
  39. data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
  40. data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
  41. data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
  42. data/app/views/panda/cms/admin/settings/index.html.erb +3 -3
  43. data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
  44. data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
  45. data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
  46. data/config/initializers/panda/cms.rb +6 -3
  47. data/config/routes.rb +8 -16
  48. data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +3 -1
  49. data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +2 -0
  50. data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
  51. data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -0
  52. data/lib/panda/cms/asset_loader.rb +4 -4
  53. data/lib/panda/cms/engine.rb +42 -98
  54. data/lib/panda-cms/version.rb +1 -1
  55. data/lib/panda-cms.rb +50 -40
  56. data/lib/tasks/assets.rake +162 -122
  57. data/lib/tasks/panda/cms/migrations.rake +13 -0
  58. metadata +19 -36
  59. data/app/builders/panda/cms/form_builder.rb +0 -225
  60. data/app/components/panda/cms/admin/button_component.rb +0 -70
  61. data/app/components/panda/cms/admin/container_component.rb +0 -13
  62. data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
  63. data/app/components/panda/cms/admin/heading_component.rb +0 -46
  64. data/app/components/panda/cms/admin/panel_component.rb +0 -13
  65. data/app/components/panda/cms/admin/table_component.rb +0 -46
  66. data/app/components/panda/cms/admin/tag_component.rb +0 -35
  67. data/app/constraints/panda/cms/admin_constraint.rb +0 -21
  68. data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -44
  69. data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -92
  70. data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -25
  71. data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
  72. data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
  73. data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
  74. data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
  75. data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
  76. data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
  77. data/app/models/panda/cms/breadcrumb.rb +0 -14
  78. data/app/models/panda/cms/user.rb +0 -33
  79. data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -195
  80. data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
  81. data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
  82. data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -7
  83. data/lib/panda/cms/editor_js/blocks/alert.rb +0 -36
  84. data/lib/panda/cms/editor_js/blocks/base.rb +0 -35
  85. data/lib/panda/cms/editor_js/blocks/header.rb +0 -17
  86. data/lib/panda/cms/editor_js/blocks/image.rb +0 -39
  87. data/lib/panda/cms/editor_js/blocks/list.rb +0 -34
  88. data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -18
  89. data/lib/panda/cms/editor_js/blocks/quote.rb +0 -44
  90. data/lib/panda/cms/editor_js/blocks/table.rb +0 -52
  91. data/lib/panda/cms/editor_js/renderer.rb +0 -127
  92. data/lib/panda/cms/editor_js.rb +0 -18
  93. data/lib/panda/cms/editor_js_content.rb +0 -61
@@ -1,7 +1,7 @@
1
1
  <div class="" data-controller="dashboard">
2
- <%= render Panda::CMS::Admin::ContainerComponent.new do |container| %>
2
+ <%= render Panda::Core::Admin::ContainerComponent.new do |container| %>
3
3
  <% container.with_heading(text: "Dashboard", level: 1) do |heading| %>
4
- <% heading.with_button(action: :add, text: "Add Page", link: new_admin_page_path) %>
4
+ <% heading.with_button(action: :add, text: "Add Page", link: new_admin_cms_page_path) %>
5
5
  <% end %>
6
6
  <dl class="grid grid-cols-1 gap-5 mt-5 sm:grid-cols-3">
7
7
  <%= render Panda::CMS::Admin::StatisticsComponent.new(metric: "Views Today", value: Panda::CMS::Visit.group_by_day(:visited_at, last: 1).count.values.first) %>
@@ -1,4 +1,4 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Files", level: 1) %>
3
3
 
4
4
  <% component.with_slideover(title: "File Details") do %>
@@ -1,10 +1,10 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Forms", level: 1) do |heading| %>
3
3
  <% end %>
4
4
 
5
- <%= render Panda::CMS::Admin::TableComponent.new(term: "form", rows: forms) do |table| %>
6
- <% table.column("Name") { |form| block_link_to form.name, admin_form_path(form) } %>
7
- <% table.column("Status") { |form| render Panda::CMS::Admin::TagComponent.new(status: :active) } %>
5
+ <%= render Panda::Core::Admin::TableComponent.new(term: "form", rows: forms) do |table| %>
6
+ <% table.column("Name") { |form| block_link_to form.name, admin_cms_form_path(form) } %>
7
+ <% table.column("Status") { |form| render Panda::Core::Admin::TagComponent.new(status: :active) } %>
8
8
  <% table.column("Last Submission") do |form| %>
9
9
  <%= "#{time_ago_in_words(form.form_submissions&.last&.created_at)} ago" if form.form_submissions&.last %>
10
10
  <% end %>
@@ -1,7 +1,7 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Add Page", level: 1) do |heading| %>
3
3
  <% end %>
4
- <%= panda_cms_form_with model: page, url: admin_pages_path, method: :post do |f| %>
4
+ <%= panda_cms_form_with model: page, url: admin_cms_pages_path, method: :post do |f| %>
5
5
  <% options = nested_set_options(Panda::CMS::Page, page) { |i| "#{"-" * i.level} #{i.title} (#{i.path})" } %>
6
6
  <div data-controller="slug">
7
7
  <input type="hidden" value="<%= Panda::CMS::Current.root %>" data-slug-target="existing_root">
@@ -1,4 +1,4 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: form.name, level: 1) do |heading| %>
3
3
  <% end %>
4
4
 
@@ -1,8 +1,8 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Menus", level: 1) do |heading| %>
3
3
  <% end %>
4
- <%= render Panda::CMS::Admin::TableComponent.new(term: "menu", rows: menus) do |table| %>
5
- <% table.column("Name") { |menu| link_to menu.name, edit_admin_menu_path(menu) } %>
6
- <% table.column("Kind") { |menu| render Panda::CMS::Admin::TagComponent.new(status: :active, text: menu.kind.titleize) } %>
4
+ <%= render Panda::Core::Admin::TableComponent.new(term: "menu", rows: menus) do |table| %>
5
+ <% table.column("Name") { |menu| link_to menu.name, edit_admin_cms_menu_path(menu) } %>
6
+ <% table.column("Kind") { |menu| render Panda::Core::Admin::TagComponent.new(status: :active, text: menu.kind.titleize) } %>
7
7
  <% end %>
8
8
  <% end %>
@@ -1,7 +1,7 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "#{page.title}", level: 1) %>
3
3
  <% component.with_slideover(title: "Page Details") do %>
4
- <%= panda_cms_form_with model: page, url: admin_page_path, method: :put do |f| %>
4
+ <%= panda_cms_form_with model: page, url: admin_cms_page_path, method: :put do |f| %>
5
5
  <%= f.text_field :title, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
6
6
  <%= f.text_field :template, value: template.name, readonly: true, class: "read-only:bg-gray-100 block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
7
7
  <%= f.select :status, options_for_select([["Active", "active"], ["Draft", "draft"], ["Hidden", "hidden"], ["Archived", "archived"]], selected: page.status), {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
@@ -9,17 +9,17 @@
9
9
  <% end %>
10
10
  <% end %>
11
11
  <div id="successMessage" class="hidden">
12
- <%= render Panda::CMS::Admin::FlashMessageComponent.new(kind: "success", message: "This page was successfully updated!", temporary: false) %>
12
+ <%= render Panda::Core::Admin::FlashMessageComponent.new(kind: "success", message: "This page was successfully updated!", temporary: false) %>
13
13
  </div>
14
14
  <div id="errorMessage" class="hidden">
15
- <%= render Panda::CMS::Admin::FlashMessageComponent.new(kind: "error", message: "There was an error updating this page.", temporary: false) %>
15
+ <%= render Panda::Core::Admin::FlashMessageComponent.new(kind: "error", message: "There was an error updating this page.", temporary: false) %>
16
16
  </div>
17
17
  <div class="grid grid-cols-2 mb-4 -mt-5">
18
18
  <div>
19
19
  <a class="inline-block mb-2 text-sm text-black/60" target="_blank" href="<%= @page.path %>"><%= @page.path %> <i class="ml-2 fa-solid fa-arrow-up-right-from-square"></i></a>
20
20
  </div>
21
21
  <div class="relative -mt-5">
22
- <span class="absolute right-0"><%= render Panda::CMS::Admin::ButtonComponent.new(text: "Save Changes", action: :save_inactive, icon: "check", link: "#", size: :regular, id: "saveEditableButton") %></span>
22
+ <span class="absolute right-0"><%= render Panda::Core::Admin::ButtonComponent.new(text: "Save Changes", action: :save_inactive, icon: "check", link: "#", size: :regular, id: "saveEditableButton") %></span>
23
23
  </div>
24
24
  </div>
25
25
  <%= content_tag :iframe, nil,
@@ -30,7 +30,7 @@
30
30
  data: {
31
31
  controller: "editor-iframe",
32
32
  editor_iframe_page_id_value: @page.id,
33
- editor_iframe_admin_path_value: "#{admin_dashboard_url}",
33
+ editor_iframe_admin_path_value: "#{admin_cms_dashboard_url}",
34
34
  editor_iframe_autosave_value: false
35
35
  } %>
36
36
  <% end %>
@@ -1,17 +1,17 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Pages", level: 1) do |heading| %>
3
- <% heading.with_button(action: :add, text: "Add Page", link: new_admin_page_path) %>
3
+ <% heading.with_button(action: :add, text: "Add Page", link: new_admin_cms_page_path) %>
4
4
  <% end %>
5
5
 
6
6
  <% if root_page %>
7
- <%= render Panda::CMS::Admin::TableComponent.new(term: "page", rows: root_page.self_and_descendants) do |table| %>
7
+ <%= render Panda::Core::Admin::TableComponent.new(term: "page", rows: root_page.self_and_descendants) do |table| %>
8
8
  <% table.column("Name") do |page| %>
9
9
  <div class="<%= table_indent(page) %>">
10
- <%= link_to page.title, edit_admin_page_path(page), class: "block h-full w-full" %>
10
+ <%= link_to page.title, edit_admin_cms_page_path(page), class: "block h-full w-full" %>
11
11
  <span class="block text-xs text-black/60"><%= page.path %></span>
12
12
  </div>
13
13
  <% end %>
14
- <% table.column("Status") { |page| render Panda::CMS::Admin::TagComponent.new(status: page.status) } %>
14
+ <% table.column("Status") { |page| render Panda::Core::Admin::TagComponent.new(status: page.status) } %>
15
15
  <% table.column("Last Updated") { |page| render Panda::CMS::Admin::UserActivityComponent.new(model: page) } %>
16
16
  <% end %>
17
17
  <% else %>
@@ -1,7 +1,7 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Add Page", level: 1) do |heading| %>
3
3
  <% end %>
4
- <%= panda_cms_form_with model: page, url: admin_pages_path, method: :post do |f| %>
4
+ <%= panda_cms_form_with model: page, url: admin_cms_pages_path, method: :post do |f| %>
5
5
  <% if page.errors.any? %>
6
6
  <div class="mb-4 p-4 bg-red-50 border border-red-200 rounded-md">
7
7
  <div class="text-sm text-red-600">
@@ -22,7 +22,7 @@
22
22
  "slug-target": "output_text"
23
23
  } %>
24
24
  </div>
25
- <%= f.select :author_id, Panda::CMS::User.admin.map { |u| [u.name, u.id] }, {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
25
+ <%= f.select :author_id, Panda::Core::User.admin.map { |u| [u.name, u.id] }, {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
26
26
  <%= f.datetime_field :published_at, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
27
27
  <%= f.select :status, options_for_select([["Active", "active"], ["Draft", "draft"], ["Hidden", "hidden"], ["Archived", "archived"]], selected: post.status), {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
28
28
 
@@ -1,7 +1,7 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: post.title, level: 1) do |heading| %>
3
3
  <% heading.with_button(action: :view, text: "View Post", link: post_path(post.admin_param)) %>
4
4
  <% end %>
5
5
 
6
- <%= render "form", post: post, url: admin_post_path(post.admin_param), method: :patch %>
6
+ <%= render "form", post: post, url: admin_cms_post_path(post.admin_param), method: :patch %>
7
7
  <% end %>
@@ -1,18 +1,18 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Posts", level: 1) do |heading| %>
3
- <% heading.with_button(action: :add, text: "Add Post", link: new_admin_post_path) %>
3
+ <% heading.with_button(action: :add, text: "Add Post", link: new_admin_cms_post_path) %>
4
4
  <% end %>
5
5
 
6
- <%= render Panda::CMS::Admin::TableComponent.new(term: "post", rows: posts) do |table| %>
6
+ <%= render Panda::Core::Admin::TableComponent.new(term: "post", rows: posts) do |table| %>
7
7
  <% table.column("Title") do |post| %>
8
8
  <div>
9
- <%= link_to post.title, edit_admin_post_path(post.admin_param), class: "block h-full w-full" %>
9
+ <%= link_to post.title, edit_admin_cms_post_path(post.admin_param), class: "block h-full w-full" %>
10
10
  <span class="block text-xs text-black/60">
11
11
  <%= CGI.unescape("#{Panda::CMS.config.posts[:prefix]}#{post.slug}") %>
12
12
  </span>
13
13
  </div>
14
14
  <% end %>
15
- <% table.column("Status") { |post| render Panda::CMS::Admin::TagComponent.new(status: post.status) } %>
15
+ <% table.column("Status") { |post| render Panda::Core::Admin::TagComponent.new(status: post.status) } %>
16
16
  <% table.column("Published") { |post| render Panda::CMS::Admin::UserActivityComponent.new(at: post.published_at, user: post.author)} %>
17
17
  <% table.column("Last Updated") { |post| render Panda::CMS::Admin::UserActivityComponent.new(at: post.updated_at)} %>
18
18
  <% end %>
@@ -1,4 +1,4 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Add Post", level: 1) do |heading| %>
3
3
  <% end %>
4
4
 
@@ -1,4 +1,4 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Bulk Editor", level: 1) %>
3
3
 
4
4
  <% if @debug && @debug[:error].any? %>
@@ -1,7 +1,7 @@
1
- <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
1
+ <%= render Panda::Core::Admin::ContainerComponent.new do |component| %>
2
2
  <% component.with_heading(text: "Settings", level: 1) %>
3
3
 
4
- <%= render Panda::CMS::Admin::PanelComponent.new do |panel| %>
4
+ <%= render Panda::Core::Admin::PanelComponent.new do |panel| %>
5
5
  <% panel.with_heading(text: "System Status") %>
6
6
 
7
7
  <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Panda CMS:</span> v<%= Panda::CMS::VERSION %></p>
@@ -10,7 +10,7 @@
10
10
  <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Environment:</span> <%= Rails.env.titleize %></p>
11
11
  <% end %>
12
12
 
13
- <%= render Panda::CMS::Admin::PanelComponent.new do |panel| %>
13
+ <%= render Panda::Core::Admin::PanelComponent.new do |panel| %>
14
14
  <% panel.with_heading(text: "Integrations") %>
15
15
 
16
16
  <p class="text-base leading-loose"><i class="mr-2 text-active fa-brands fa-instagram"></i> <span class="font-medium">Instagram:</span> <%= Panda::CMS.config.instagram[:enabled] ? "Connected (@#{Panda::CMS.config.instagram[:username]})" : "Not Connected" %></p>
@@ -19,10 +19,10 @@
19
19
  </nav>
20
20
 
21
21
  <% if content_for :sidebar %>
22
- <div class="pt-4 pr-8 text-black/80" tabindex="-1">
23
- <a href="#" id="slideover-toggle" data-action="click->toggle#toggle touch->toggle#toggle" class="text-sm font-light">
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
24
  <i class="mr-1 fa-light fa-gear"></i> <%= yield :sidebar_title %>
25
- </a>
25
+ </button>
26
26
  </div>
27
27
  <% end %>
28
28
  </div>
@@ -1,5 +1,5 @@
1
1
  <% if flash.any? %>
2
2
  <% flash.each do |kind, message| %>
3
- <%= render Panda::CMS::Admin::FlashMessageComponent.new(kind: kind, message: message) %>
3
+ <%= render Panda::Core::Admin::FlashMessageComponent.new(kind: kind, message: message) %>
4
4
  <% end %>
5
5
  <% end %>
@@ -2,12 +2,12 @@
2
2
  <ul role="list" class="flex flex-col flex-1">
3
3
  <a class="block p-0 mt-4 mb-4 ml-2 text-xl font-medium text-white"><%= Panda::CMS.config.title %></a>
4
4
  <% [
5
- { path: admin_dashboard_path, text: "Dashboard", icon: "fa-house", matcher: :exact },
6
- { path: admin_pages_path, text: "Pages", icon: "fa-page", matcher: :starts_with },
7
- { path: admin_posts_path, text: "Posts", icon: "fa-notebook", matcher: :starts_with },
8
- { path: admin_forms_path, text: "Forms", icon: "fa-cabinet-filing", matcher: :starts_with },
9
- { path: admin_menus_path, text: "Menus", icon: "fa-list-dropdown", matcher: :starts_with },
10
- { path: admin_settings_path, text: "Settings", icon: "fa-cog", matcher: :starts_with },
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
11
  ].each do |link| %>
12
12
  <li>
13
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 %>
@@ -17,13 +17,13 @@
17
17
  </li>
18
18
  <% end %>
19
19
  <li>
20
- <%= button_to admin_logout_path, method: :delete, id: "logout-link", data: { turbo: false }, class: nav_highlight_colour_classes(request) + " w-full" do %>
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
21
  <span class="text-center"><i class="text-xl fa-regular fa-door-open fa-fw"></i></span>
22
22
  <span class="mt-1">Logout</span>
23
23
  <% end %>
24
24
  </li>
25
25
  <li class="mt-auto">
26
- <%= link_to edit_admin_my_profile_path, class: nav_highlight_colour_classes(request) + " w-full", title: "Edit my Profile" do %>
26
+ <%= link_to panda_core.edit_admin_my_profile_path, class: nav_highlight_colour_classes(request) + " w-full", title: "Edit my Profile" do %>
27
27
  <% if !current_user.image_url.to_s.empty? %>
28
28
  <span class="text-center"><img src="<%= current_user.image_url %>" class="w-auto h-7 rounded-full"></span>
29
29
  <% else %>
@@ -4,9 +4,12 @@ Panda::CMS.configure do |config|
4
4
  # The main title of your website
5
5
  config.title = "Demo Site"
6
6
 
7
- # The path to the administration panel
8
- config.admin_path = "/admin"
9
-
10
7
  # Site access control
11
8
  config.require_login_to_view = false
12
9
  end
10
+
11
+ # Admin path is now configured via Panda::Core
12
+ Panda::Core.configure do |config|
13
+ # The path to the administration panel
14
+ config.admin_path = "/admin"
15
+ end
data/config/routes.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../app/constraints/panda/cms/admin_constraint"
4
-
5
3
  Panda::CMS::Engine.routes.draw do
6
- constraints Panda::CMS::AdminConstraint.new(&:present?) do
7
- namespace Panda::CMS.route_namespace, as: :admin, module: :admin do
4
+ constraints Panda::Core::AdminConstraint.new(&:present?) do
5
+ # CMS-specific dashboard (using Core's admin_path)
6
+ admin_path = Panda::Core.configuration.admin_path
7
+ get "#{admin_path}/cms", to: "admin/dashboard#show", as: :admin_cms_dashboard
8
+
9
+ namespace admin_path.delete_prefix("/").to_sym, path: "#{admin_path}/cms", as: :admin_cms, module: :admin do
8
10
  resources :files
9
11
  resources :forms, only: %i[index show]
10
12
  resources :menus
@@ -15,28 +17,18 @@ Panda::CMS::Engine.routes.draw do
15
17
 
16
18
  get "settings", to: "settings#index"
17
19
 
18
- resource :my_profile, only: %i[edit update], controller: :my_profile
20
+ # Profile management moved to panda-core at /admin/my_profile
19
21
 
20
22
  namespace :settings, as: :settings do
21
23
  get "bulk_editor", to: "bulk_editor#new"
22
24
  post "bulk_editor", to: "bulk_editor#create"
23
25
  end
24
26
  end
25
-
26
- get Panda::CMS.route_namespace, to: "admin/dashboard#show", as: :admin_dashboard
27
27
  end
28
28
 
29
29
  ### PUBLIC ROUTES ###
30
30
 
31
- # Authentication routes
32
- get Panda::CMS.route_namespace, to: "admin/sessions#new", as: :admin_login
33
- # Get and post options here are for OmniAuth coming back in, not going out
34
- match "#{Panda::CMS.route_namespace}/auth/:provider/callback", to: "admin/sessions#create",
35
- as: :admin_login_callback, via: %i[get post]
36
- match "#{Panda::CMS.route_namespace}/auth/failure", to: "admin/sessions#failure", as: :admin_login_failure,
37
- via: %i[get post]
38
- # OmniAuth additionally adds a GET route for "#{Panda::CMS.route_namespace}/auth/:provider" but doesn't name it
39
- delete Panda::CMS.route_namespace, to: "admin/sessions#destroy", as: :admin_logout
31
+ # Authentication routes are now handled by Panda::Core
40
32
 
41
33
  ### APPENDED ROUTES ###
42
34
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  class AddAuthorIdToPandaCMSPosts < ActiveRecord::Migration[8.0]
4
4
  def change
5
- add_reference :panda_cms_posts, :author, type: :uuid, foreign_key: {to_table: :panda_cms_users}
5
+ unless column_exists?(:panda_cms_posts, :author_id)
6
+ add_reference :panda_cms_posts, :author, type: :uuid, foreign_key: {to_table: :panda_core_users}
7
+ end
6
8
  end
7
9
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  class CreatePandaSocialInstagramPosts < ActiveRecord::Migration[7.1]
4
4
  def change
5
+ return if table_exists?(:panda_social_instagram_posts)
6
+
5
7
  create_table :panda_social_instagram_posts, id: :uuid do |t|
6
8
  t.string :instagram_id, null: false
7
9
  t.text :caption
@@ -0,0 +1,111 @@
1
+ class MigrateUsersToPandaCore < ActiveRecord::Migration[8.0]
2
+ def up
3
+ # First ensure panda_core_users table exists (from Core engine)
4
+ # It should already exist from the Core migration
5
+
6
+ # Migrate data from panda_cms_users to panda_core_users if needed
7
+ if table_exists?(:panda_cms_users) && table_exists?(:panda_core_users)
8
+ # Check if there's any data to migrate
9
+ cms_user_count = ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM panda_cms_users")
10
+ return if cms_user_count == 0
11
+ # Copy all user data
12
+ execute <<-SQL
13
+ INSERT INTO panda_core_users (
14
+ id, name, email, image_url, is_admin, created_at, updated_at
15
+ )
16
+ SELECT
17
+ id,
18
+ COALESCE(name, CONCAT(firstname, ' ', lastname), 'Unknown User'),
19
+ email,
20
+ image_url,
21
+ COALESCE(admin, false),
22
+ created_at,
23
+ updated_at
24
+ FROM panda_cms_users
25
+ WHERE NOT EXISTS (
26
+ SELECT 1 FROM panda_core_users WHERE panda_core_users.id = panda_cms_users.id
27
+ )
28
+ SQL
29
+
30
+ # Update foreign key references in other tables
31
+
32
+ # Posts author_id
33
+ if column_exists?(:panda_cms_posts, :author_id)
34
+ remove_foreign_key :panda_cms_posts, column: :author_id if foreign_key_exists?(:panda_cms_posts, :panda_cms_users, column: :author_id)
35
+ add_foreign_key :panda_cms_posts, :panda_core_users, column: :author_id, primary_key: :id
36
+ end
37
+
38
+ # Posts user_id (legacy column)
39
+ if column_exists?(:panda_cms_posts, :user_id)
40
+ remove_foreign_key :panda_cms_posts, column: :user_id if foreign_key_exists?(:panda_cms_posts, :panda_cms_users, column: :user_id)
41
+ add_foreign_key :panda_cms_posts, :panda_core_users, column: :user_id, primary_key: :id
42
+ end
43
+
44
+ # Visits user_id
45
+ if column_exists?(:panda_cms_visits, :user_id)
46
+ remove_foreign_key :panda_cms_visits, column: :user_id if foreign_key_exists?(:panda_cms_visits, :panda_cms_users, column: :user_id)
47
+ add_foreign_key :panda_cms_visits, :panda_core_users, column: :user_id, primary_key: :id
48
+ end
49
+
50
+ # Drop the old table
51
+ drop_table :panda_cms_users
52
+ end
53
+ end
54
+
55
+ def down
56
+ # Recreate panda_cms_users table
57
+ create_table :panda_cms_users, id: :uuid do |t|
58
+ t.string :name
59
+ t.string :firstname
60
+ t.string :lastname
61
+ t.string :email, null: false
62
+ t.string :image_url
63
+ t.boolean :admin, default: false
64
+ t.string :current_theme, default: "default"
65
+ t.timestamps
66
+ end
67
+
68
+ add_index :panda_cms_users, :email, unique: true
69
+
70
+ # Migrate data back
71
+ if table_exists?(:panda_core_users) && table_exists?(:panda_cms_users)
72
+ execute <<-SQL
73
+ INSERT INTO panda_cms_users (
74
+ id, firstname, lastname, name, email, image_url, admin, current_theme, created_at, updated_at
75
+ )
76
+ SELECT
77
+ id,
78
+ split_part(name, ' ', 1),
79
+ CASE
80
+ WHEN position(' ' IN name) > 0
81
+ THEN substring(name FROM position(' ' IN name) + 1)
82
+ ELSE ''
83
+ END,
84
+ name,
85
+ email,
86
+ image_url,
87
+ is_admin,
88
+ 'default',
89
+ created_at,
90
+ updated_at
91
+ FROM panda_core_users
92
+ SQL
93
+
94
+ # Restore foreign keys to panda_cms_users
95
+ if column_exists?(:panda_cms_posts, :author_id)
96
+ remove_foreign_key :panda_cms_posts, column: :author_id if foreign_key_exists?(:panda_cms_posts, column: :author_id)
97
+ add_foreign_key :panda_cms_posts, :panda_cms_users, column: :author_id, primary_key: :id
98
+ end
99
+
100
+ if column_exists?(:panda_cms_posts, :user_id)
101
+ remove_foreign_key :panda_cms_posts, column: :user_id if foreign_key_exists?(:panda_cms_posts, column: :user_id)
102
+ add_foreign_key :panda_cms_posts, :panda_cms_users, column: :user_id, primary_key: :id
103
+ end
104
+
105
+ if column_exists?(:panda_cms_visits, :user_id)
106
+ remove_foreign_key :panda_cms_visits, column: :user_id if foreign_key_exists?(:panda_cms_visits, column: :user_id)
107
+ add_foreign_key :panda_cms_visits, :panda_cms_users, column: :user_id, primary_key: :id
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MakePostUserReferencesNullable < ActiveRecord::Migration[8.0]
4
+ def change
5
+ # Make user_id and author_id nullable for posts
6
+ # This is needed because fixtures can't reference users from another gem
7
+ # Tests that need users will set them programmatically
8
+ change_column_null :panda_cms_posts, :user_id, true
9
+ change_column_null :panda_cms_posts, :author_id, true
10
+ end
11
+ end
@@ -111,15 +111,15 @@ module Panda
111
111
  version = asset_version
112
112
  js_url = "/panda-cms-assets/panda-cms-#{version}.js"
113
113
  css_url = "/panda-cms-assets/panda-cms-#{version}.css"
114
-
114
+
115
115
  tags = []
116
-
116
+
117
117
  # JavaScript tag
118
118
  tags << content_tag(:script, "", {
119
119
  src: js_url,
120
120
  defer: true
121
121
  })
122
-
122
+
123
123
  # CSS tag if exists
124
124
  if cached_asset_exists?(css_url)
125
125
  tags << tag(:link, {
@@ -127,7 +127,7 @@ module Panda
127
127
  href: css_url
128
128
  })
129
129
  end
130
-
130
+
131
131
  tags.join("\n").html_safe
132
132
  else
133
133
  # In development, just use a simple script tag