panda_cms 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of panda_cms might be problematic. Click here for more details.

Files changed (144) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/builds/panda_cms.css +1 -0
  5. data/app/assets/config/panda_cms_manifest.js +1 -0
  6. data/app/assets/stylesheets/panda_cms/application.tailwind.css +30 -0
  7. data/app/builders/panda_cms/form_builder.rb +118 -0
  8. data/app/components/panda_cms/admin/button_component.rb +65 -0
  9. data/app/components/panda_cms/admin/container_component.html.erb +13 -0
  10. data/app/components/panda_cms/admin/container_component.rb +11 -0
  11. data/app/components/panda_cms/admin/flash_message_component.html.erb +30 -0
  12. data/app/components/panda_cms/admin/flash_message_component.rb +44 -0
  13. data/app/components/panda_cms/admin/heading_component.rb +38 -0
  14. data/app/components/panda_cms/admin/slideover_component.html.erb +9 -0
  15. data/app/components/panda_cms/admin/slideover_component.rb +13 -0
  16. data/app/components/panda_cms/admin/tab_bar_component.html.erb +35 -0
  17. data/app/components/panda_cms/admin/tab_bar_component.rb +13 -0
  18. data/app/components/panda_cms/grid_component.html.erb +6 -0
  19. data/app/components/panda_cms/grid_component.rb +13 -0
  20. data/app/components/panda_cms/menu_component.html.erb +3 -0
  21. data/app/components/panda_cms/menu_component.rb +18 -0
  22. data/app/components/panda_cms/page_menu_component.html.erb +24 -0
  23. data/app/components/panda_cms/page_menu_component.rb +24 -0
  24. data/app/components/panda_cms/rich_text_component.html.erb +40 -0
  25. data/app/components/panda_cms/rich_text_component.rb +35 -0
  26. data/app/components/panda_cms/text_component.rb +63 -0
  27. data/app/constraints/panda_cms/admin_constraint.rb +16 -0
  28. data/app/controllers/panda_cms/admin/block_contents_controller.rb +42 -0
  29. data/app/controllers/panda_cms/admin/dashboard_controller.rb +15 -0
  30. data/app/controllers/panda_cms/admin/files_controller.rb +17 -0
  31. data/app/controllers/panda_cms/admin/menus_controller.rb +81 -0
  32. data/app/controllers/panda_cms/admin/pages_controller.rb +88 -0
  33. data/app/controllers/panda_cms/admin/sessions_controller.rb +72 -0
  34. data/app/controllers/panda_cms/application_controller.rb +51 -0
  35. data/app/controllers/panda_cms/errors_controller.rb +31 -0
  36. data/app/controllers/panda_cms/pages_controller.rb +33 -0
  37. data/app/helpers/panda_cms/admin/files_helper.rb +4 -0
  38. data/app/helpers/panda_cms/admin/pages_helper.rb +4 -0
  39. data/app/helpers/panda_cms/application_helper.rb +91 -0
  40. data/app/helpers/panda_cms/pages_helper.rb +4 -0
  41. data/app/helpers/panda_cms/theme_helper.rb +16 -0
  42. data/app/javascript/panda_cms/base.js +37 -0
  43. data/app/javascript/panda_cms/controllers/menu_controller.js +19 -0
  44. data/app/javascript/panda_cms/controllers/rich_text_editor_controller.js +59 -0
  45. data/app/javascript/panda_cms/controllers/text_field_update_controller.js +23 -0
  46. data/app/javascript/panda_cms/vendor/stimulus-components-rails-nested-form.js +2 -0
  47. data/app/javascript/panda_cms/vendor/tailwindcss-stimulus-components.js +2 -0
  48. data/app/jobs/panda_cms/application_job.rb +4 -0
  49. data/app/lib/panda_cms/demo_site_generator.rb +70 -0
  50. data/app/lib/panda_cms/slug.rb +21 -0
  51. data/app/mailers/panda_cms/application_mailer.rb +6 -0
  52. data/app/models/panda_cms/application_record.rb +5 -0
  53. data/app/models/panda_cms/block.rb +32 -0
  54. data/app/models/panda_cms/block_content.rb +16 -0
  55. data/app/models/panda_cms/block_content_version.rb +6 -0
  56. data/app/models/panda_cms/breadcrumb.rb +10 -0
  57. data/app/models/panda_cms/current.rb +15 -0
  58. data/app/models/panda_cms/menu.rb +50 -0
  59. data/app/models/panda_cms/menu_item.rb +56 -0
  60. data/app/models/panda_cms/page.rb +86 -0
  61. data/app/models/panda_cms/page_version.rb +6 -0
  62. data/app/models/panda_cms/redirect.rb +9 -0
  63. data/app/models/panda_cms/template.rb +44 -0
  64. data/app/models/panda_cms/template_version.rb +6 -0
  65. data/app/models/panda_cms/user.rb +11 -0
  66. data/app/models/panda_cms/version.rb +6 -0
  67. data/app/models/panda_cms/visit.rb +7 -0
  68. data/app/views/layouts/panda_cms/application.html.erb +68 -0
  69. data/app/views/layouts/panda_cms/public.html.erb +3 -0
  70. data/app/views/panda_cms/admin/dashboard/show.html.erb +8 -0
  71. data/app/views/panda_cms/admin/files/index.html.erb +124 -0
  72. data/app/views/panda_cms/admin/files/show.html.erb +2 -0
  73. data/app/views/panda_cms/admin/menus/_form.html.erb +21 -0
  74. data/app/views/panda_cms/admin/menus/_menu_item_fields.html.erb +7 -0
  75. data/app/views/panda_cms/admin/menus/edit.html.erb +58 -0
  76. data/app/views/panda_cms/admin/menus/index.html.erb +32 -0
  77. data/app/views/panda_cms/admin/menus/new.html.erb +5 -0
  78. data/app/views/panda_cms/admin/pages/edit.html.erb +35 -0
  79. data/app/views/panda_cms/admin/pages/index.html.erb +46 -0
  80. data/app/views/panda_cms/admin/pages/new.html.erb +16 -0
  81. data/app/views/panda_cms/admin/pages/show.html.erb +1 -0
  82. data/app/views/panda_cms/admin/sessions/new.html.erb +39 -0
  83. data/app/views/panda_cms/admin/shared/_breadcrumbs.html.erb +25 -0
  84. data/app/views/panda_cms/admin/shared/_flash.html.erb +5 -0
  85. data/app/views/panda_cms/admin/shared/_sidebar.html.erb +29 -0
  86. data/app/views/panda_cms/shared/_favicons.html.erb +9 -0
  87. data/app/views/panda_cms/shared/_footer.html.erb +2 -0
  88. data/app/views/panda_cms/shared/_header.html.erb +19 -0
  89. data/config/importmap.rb +7 -0
  90. data/config/initializers/panda_cms/form_errors.rb +38 -0
  91. data/config/initializers/panda_cms.rb +42 -0
  92. data/config/locales/en.yml +13 -0
  93. data/config/routes.rb +26 -0
  94. data/config/tailwind.config.js +31 -0
  95. data/config/tailwind.embed.config.js +20 -0
  96. data/db/migrate/20240205223709_create_panda_cms_pages.rb +9 -0
  97. data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +14 -0
  98. data/db/migrate/20240303002805_create_panda_cms_templates.rb +11 -0
  99. data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +14 -0
  100. data/db/migrate/20240303022441_create_panda_cms_blocks.rb +13 -0
  101. data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +10 -0
  102. data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +14 -0
  103. data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +10 -0
  104. data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +12 -0
  105. data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +5 -0
  106. data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +16 -0
  107. data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +6 -0
  108. data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +5 -0
  109. data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +24 -0
  110. data/db/migrate/20240317010532_create_panda_cms_users.rb +12 -0
  111. data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +7 -0
  112. data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +5 -0
  113. data/db/migrate/20240317214827_create_panda_cms_redirects.rb +15 -0
  114. data/db/migrate/20240317230622_create_panda_cms_visits.rb +13 -0
  115. data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +58 -0
  116. data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +5 -0
  117. data/db/seeds.rb +4 -0
  118. data/lib/generators/panda_cms/install_generator.rb +32 -0
  119. data/lib/panda_cms/engine.rb +172 -0
  120. data/lib/panda_cms/exceptions_app.rb +24 -0
  121. data/lib/panda_cms/version.rb +3 -0
  122. data/lib/panda_cms.rb +14 -0
  123. data/lib/tasks/panda_cms.rake +67 -0
  124. data/lib/templates/erb/scaffold/_form.html.erb.tt +43 -0
  125. data/lib/templates/erb/scaffold/edit.html.erb.tt +8 -0
  126. data/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
  127. data/lib/templates/erb/scaffold/new.html.erb.tt +7 -0
  128. data/lib/templates/erb/scaffold/partial.html.erb.tt +22 -0
  129. data/lib/templates/erb/scaffold/show.html.erb.tt +15 -0
  130. data/public/panda-cms-assets/android-chrome-192x192.png +0 -0
  131. data/public/panda-cms-assets/android-chrome-512x512.png +0 -0
  132. data/public/panda-cms-assets/apple-touch-icon.png +0 -0
  133. data/public/panda-cms-assets/browserconfig.xml +9 -0
  134. data/public/panda-cms-assets/editable.js +212 -0
  135. data/public/panda-cms-assets/favicon-16x16.png +0 -0
  136. data/public/panda-cms-assets/favicon-32x32.png +0 -0
  137. data/public/panda-cms-assets/favicon.ico +0 -0
  138. data/public/panda-cms-assets/mstile-150x150.png +0 -0
  139. data/public/panda-cms-assets/panda-logo-screenprint.png +0 -0
  140. data/public/panda-cms-assets/panda-nav.png +0 -0
  141. data/public/panda-cms-assets/safari-pinned-tab.svg +61 -0
  142. data/public/panda-cms-assets/site.webmanifest +14 -0
  143. data/public/panda-cms-assets/stimulus-loading.js +113 -0
  144. metadata +845 -0
@@ -0,0 +1,39 @@
1
+ <div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8 text-center">
2
+ <div class="sm:mx-auto sm:w-full sm:max-w-sm text-center">
3
+ <img src="/panda-cms-assets/panda-nav.png" class="py-2 h-32 mx-auto w-auto">
4
+ <h2 class="mt-10 mb-6 text-center text-2xl font-bold text-white">Sign in to your account</h2>
5
+ </div>
6
+
7
+ <% if PandaCms.authentication.dig(:google, :enabled) %>
8
+ <div class="mt-4 text-center sm:mx-auto sm:w-full sm:max-w-sm">
9
+ <%= form_tag "#{PandaCms.admin_path}/auth/google", method: "post", data: {turbo: false} do %>
10
+ <button type="submit" class="min-w-56 mx-auto mb-4 inline-flex items-center gap-x-2 rounded-md bg-white px-3.5 py-2.5 border border-neutral-400">
11
+ <i class="fa-brands fa-google text-xl mr-1"></i>
12
+ Sign in with Google
13
+ </button>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+
18
+ <% if PandaCms.authentication.dig(:microsoft, :enabled) %>
19
+ <div class="mt-4 text-center sm:mx-auto sm:w-full sm:max-w-sm">
20
+ <%= form_tag "#{PandaCms.admin_path}/auth/microsoft", method: "post", data: {turbo: false} do %>
21
+ <button type="submit" class="min-w-56 mx-auto mb-4 inline-flex items-center gap-x-2 rounded-md bg-white px-3.5 py-2.5 border border-neutral-400">
22
+ <i class="fa-brands fa-microsoft text-xl mr-1"></i>
23
+ Sign in with Microsoft
24
+ </button>
25
+ <% end %>
26
+ </div>
27
+ <% end %>
28
+
29
+ <% if PandaCms.authentication.dig(:github, :enabled) %>
30
+ <div class="mt-4 text-center sm:mx-auto sm:w-full sm:max-w-sm">
31
+ <%= form_tag "#{PandaCms.admin_path}/auth/github", method: "post", data: {turbo: false} do %>
32
+ <button type="submit" id="button-sign-in-github" class="min-w-56 mx-auto mb-4 inline-flex items-center gap-x-2 rounded-md bg-white px-3.5 py-2.5 border border-neutral-400">
33
+ <i class="fa-brands fa-github text-xl mr-1"></i>
34
+ Sign in with GitHub
35
+ </button>
36
+ <% end %>
37
+ </div>
38
+ <% end %>
39
+ </div>
@@ -0,0 +1,25 @@
1
+
2
+ <nav class="flex bg-white border-b border-gray-200" aria-label="Breadcrumb" id="panda-breadcrumbs">
3
+ <ol role="list" class="flex px-4 space-x-2 w-full sm:px-6">
4
+ <li class="flex">
5
+ <div class="flex items-center">
6
+ <a href="/admin" class="text-gray-400 hover:text-gray-500">
7
+ <svg class="flex-shrink-0 w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
8
+ <path fill-rule="evenodd" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
9
+ </svg>
10
+ <span class="sr-only">Home</span>
11
+ </a>
12
+ </div>
13
+ </li>
14
+ <% breadcrumbs.each do |crumb| %>
15
+ <li class="flex">
16
+ <div class="flex items-center">
17
+ <svg class="flex-shrink-0 w-6 h-full text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" fill="currentColor" aria-hidden="true">
18
+ <path d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
19
+ </svg>
20
+ <a href="<%= crumb.path %>" class="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700"><%= crumb.name %></a>
21
+ </div>
22
+ </li>
23
+ <% end %>
24
+ </ol>
25
+ </nav>
@@ -0,0 +1,5 @@
1
+ <% if flash.any? %>
2
+ <% flash.each do |kind, message| %>
3
+ <%= render PandaCms::Admin::FlashMessageComponent.new(kind: kind, message: message) %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <nav class="flex flex-1 flex-col">
2
+ <ul role="list" class="flex flex-1 flex-col">
3
+ <li>
4
+ <img src="/panda-cms-assets/panda-nav.png" class="lg:h-32 h-16 py-2 w-auto inline">
5
+ <a class="text-white font-medium text-lg ml-2 lg:hidden"><%= PandaCms.title %></a>
6
+ <a class="text-white right-0 absolute text-lg mt-4 mr-5 lg:hidden" href="#" data-action="click->panda-cms-menu#toggle touch->panda-cms-menu#toggle"><i class="fa-sharp fa-solid fa-bars text-white mr-2"></i> Menu</a>
7
+ </li>
8
+
9
+ <% [
10
+ { path: admin_dashboard_path, text: "Dashboard", icon: "fa-house", matcher: :exact },
11
+ { path: admin_pages_path, text: "Pages", icon: "fa-page", matcher: :starts_with },
12
+ { path: admin_menus_path, text: "Menus", icon: "fa-list-dropdown", matcher: :starts_with },
13
+ { path: "#", text: "Settings", icon: "fa-cog" },
14
+ ].each do |link| %>
15
+ <li>
16
+ <%= 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
+ <span class="text-center"><i class="fa-solid <%= link[:icon] %> text-xl fa-fw"></i></span>
18
+ <span><%= link[:text] %></span>
19
+ <% end %>
20
+ </li>
21
+ <% end %>
22
+ <li>
23
+ <%= button_to admin_logout_path, method: :delete, data: { turbo: false }, class: nav_highlight_colour_classes(request) + " w-full" do %>
24
+ <span class="text-center"><i class="fa-solid fa-door-open text-xl fa-fw"></i></span>
25
+ <span>Sign Out</span>
26
+ <% end %>
27
+ </li>
28
+ </ul>
29
+ </nav>
@@ -0,0 +1,9 @@
1
+ <link rel="apple-touch-icon" sizes="180x180" href="/panda-cms-assets/apple-touch-icon.png">
2
+ <link rel="icon" type="image/png" sizes="32x32" href="/panda-cms-assets/favicon-32x32.png">
3
+ <link rel="icon" type="image/png" sizes="16x16" href="/panda-cms-assets/favicon-16x16.png">
4
+ <link rel="manifest" href="/panda-cms-assets/site.webmanifest">
5
+ <link rel="mask-icon" href="/panda-cms-assets/safari-pinned-tab.svg" color="#5bbad5">
6
+ <link rel="shortcut icon" href="/panda-cms-assets/favicon.ico">
7
+ <meta name="msapplication-TileColor" content="#b91d47">
8
+ <meta name="msapplication-config" content="/panda-cms-assets/browserconfig.xml">
9
+ <meta name="theme-color" content="#ffffff">
@@ -0,0 +1,2 @@
1
+ </body>
2
+ </html>
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html class="<%= html_class ||= 'bg-gray-50' %>">
3
+ <head>
4
+ <title>Panda CMS</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ <script src="https://kit.fontawesome.com/4da51334eb.js" crossorigin="anonymous"></script>
8
+ <link rel="stylesheet" href="https://use.typekit.net/him4vgh.css">
9
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js" crossorigin="anonymous"></script>
10
+ <%= stylesheet_link_tag "panda_cms", "data-turbo-track": "reload" %>
11
+ <script type="importmap" data-turbo-track="reload"> <%# erblint:disable AllowedScriptType %>
12
+ <%= raw(PandaCms::Engine.importmap.to_json(resolver: self)) %>
13
+ </script>
14
+ <%= javascript_import_module_tag "panda_cms/base" %>
15
+ <%= render "panda_cms/shared/favicons" %>
16
+ </head>
17
+
18
+ <body class="overflow-hidden h-full">
19
+ <%= render "panda_cms/admin/shared/flash" %>
@@ -0,0 +1,7 @@
1
+ pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:stimulus@3.2.2/dist/stimulus.js"
2
+ # TODO: Pin to a URL on our CDN
3
+ pin "@hotwired/stimulus-loading", to: "/panda-cms-assets/stimulus-loading.js" # 3.2.2
4
+
5
+ pin_all_from PandaCms::Engine.root.join("app/javascript/panda_cms/vendor"), under: "vendor", to: "panda_cms/vendor"
6
+ pin_all_from PandaCms::Engine.root.join("app/javascript/panda_cms"), under: "panda_cms", to: "panda_cms"
7
+ pin_all_from PandaCms::Engine.root.join("app/javascript/panda_cms/controllers"), under: "panda_cms/controllers", to: "panda_cms/controllers"
@@ -0,0 +1,38 @@
1
+ ActionView::Base.field_error_proc = proc do |html_tag, instance|
2
+ html = ""
3
+ form_fields = %w[input select textarea trix-editor label].join(", ")
4
+ error_class = "text-red-600 dark:text-red-500 bg-red-50 border-red-500 border-1 box-shadow-red-500 focus:ring-red-500 focus:border-red-500 dark:bg-red-900 dark:border-red-500 dark:focus:ring-red-500 dark:focus:border-red-500"
5
+ message_class = "block w-full text-sm font-bold p-0 m-0 mt-1 text-red-700"
6
+ autofocused = false
7
+
8
+ Nokogiri::HTML::DocumentFragment.parse(html_tag).css(form_fields).each do |element|
9
+ if form_fields.include?(element.node_name)
10
+ if !autofocused
11
+ # element.attribute("autofocus", "true")
12
+ autofocused = true
13
+ end
14
+
15
+ message = "#{instance.object.class.human_attribute_name(instance.send(:sanitized_method_name))} "
16
+ message += if instance.error_message.respond_to?(:each)
17
+ "#{instance.error_message.uniq.to_sentence}."
18
+ else
19
+ "#{instance.error_message}."
20
+ end
21
+
22
+ if element.node_name.eql?("label")
23
+ html = element.to_s
24
+ else
25
+ element.add_class(error_class)
26
+ html = if element.get_attribute("data-prefix")
27
+ "#{element}</div><div class=\"#{message_class}\">#{message}"
28
+ elsif element.get_attribute("type") != "checkbox"
29
+ "#{element}<div class=\"#{message_class}\">#{message}</div>"
30
+ else
31
+ element.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ html.html_safe
38
+ end
@@ -0,0 +1,42 @@
1
+ # The main title of your website
2
+ # The default is "Demo Site"
3
+ PandaCms.title = "Demo Site"
4
+ # The path to the administration panel, which must start with a /
5
+ # The default is "/admin"
6
+ PandaCms.admin_path = "/admin"
7
+ # Is login required to access the site?
8
+ PandaCms.require_login_to_view = false
9
+ # Authentication providers
10
+ PandaCms.authentication = {
11
+ microsoft: {
12
+ enabled: false,
13
+ # Setup at the following URL:
14
+ # https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
15
+ client_id: Rails.application.credentials.dig(:microsoft, :client_id),
16
+ client_secret: Rails.application.credentials.dig(:microsoft, :client_secret),
17
+ # Don't change this or the sky will fall on your head
18
+ # https://github.com/synth/omniauth-microsoft_graph/tree/main?tab=readme-ov-file#domain-verification
19
+ skip_domain_verification: false,
20
+ # If your application is single-tenanted, replace "common" with your tenant (directory) ID
21
+ # from https://portal.azure.com/#settings/directory, otherwise you'll likely want to leave
22
+ # these settings unchanged
23
+ client_options: {
24
+ site: "https://login.microsoftonline.com/",
25
+ token_url: "common/oauth2/v2.0/token",
26
+ authorize_url: "common/oauth2/v2.0/authorize"
27
+ },
28
+ # If you assign specific users or groups, you will likely want to set this to true to enable
29
+ # auto-provisioning
30
+ create_account_on_first_login: true
31
+ },
32
+ google: {
33
+ enabled: false,
34
+ create_account_on_first_login: true,
35
+ create_admin_account_on_first_login: false
36
+ },
37
+ github: {
38
+ enabled: true,
39
+ create_account_on_first_login: true,
40
+ create_admin_account_on_first_login: false
41
+ }
42
+ }
@@ -0,0 +1,13 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ panda_cms/page:
5
+ title: Title
6
+ path: Path
7
+ panda_cms_template_id: Template
8
+ panda_cms/menu:
9
+ name: Menu Name
10
+ panda_cms/menu_item:
11
+ text: Menu Item
12
+ external_url: External URL
13
+ panda_cms_page_id: Page
data/config/routes.rb ADDED
@@ -0,0 +1,26 @@
1
+ require_relative "../app/constraints/panda_cms/admin_constraint"
2
+
3
+ PandaCms::Engine.routes.draw do
4
+ constraints PandaCms::AdminConstraint.new { |user| user.present? } do
5
+ namespace PandaCms.admin_path_symbol.to_sym, as: :admin, module: :admin do
6
+ resources :files
7
+ resources :pages do
8
+ resources :block_contents, only: %i[update]
9
+ end
10
+ resources :menus
11
+ end
12
+
13
+ get PandaCms.admin_path, to: "admin/dashboard#show", as: :admin_dashboard
14
+ end
15
+
16
+ ### PUBLIC ROUTES ###
17
+
18
+ # Authentication routes
19
+ get PandaCms.admin_path, to: "admin/sessions#new", as: :admin_login
20
+ match "#{PandaCms.admin_path}/auth/:provider/callback", to: "admin/sessions#create", as: :admin_login_callback, via: %i[get post]
21
+ delete PandaCms.admin_path, to: "admin/sessions#destroy", as: :admin_logout
22
+
23
+ ### INHERITED ROUTES ###
24
+
25
+ # See lib/panda_cms/engine.rb
26
+ end
@@ -0,0 +1,31 @@
1
+ module.exports = {
2
+ content: {
3
+ relative: true,
4
+ files: [
5
+ "../public/*.html",
6
+ "../app/views/**/*.html.erb",
7
+ "../app/components/panda_cms/**/*.html.erb",
8
+ "../app/components/panda_cms/**/*.rb",
9
+ "../app/helpers/panda_cms/**/*.rb",
10
+ "../app/javascript/panda_cms/**/*.js",
11
+ "../vendor/javascript/**/*.js",
12
+ ],
13
+ },
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "panda-cms": {
18
+ light: "#C8F5FF",
19
+ dark: "#282E32",
20
+ alternate: "#FB61BD",
21
+ },
22
+ },
23
+ },
24
+ },
25
+ plugins: [
26
+ require("@tailwindcss/forms"),
27
+ require("@tailwindcss/aspect-ratio"),
28
+ require("@tailwindcss/typography"),
29
+ require("@tailwindcss/container-queries"),
30
+ ],
31
+ };
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ content: [
3
+ "./app/helpers/**/*.rb",
4
+ "./app/components/**/*.rb",
5
+ "./app/components/**/*.html.erb",
6
+ ],
7
+ theme: {
8
+ extend: {
9
+ colors: {
10
+ panda: {
11
+ light: "#86a9db",
12
+ dark: "#404c5c",
13
+ alternate: "#ea565d"
14
+ }
15
+ }
16
+ },
17
+ },
18
+ plugins: [],
19
+ prefix: "panda-cms-"
20
+ }
@@ -0,0 +1,9 @@
1
+ class CreatePandaCmsPages < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_pages, id: :uuid do |t|
4
+ t.string :title
5
+ t.string :path
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePandaCmsPageVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_page_versions, id: :uuid do |t|
4
+ t.string :item_type, null: false
5
+ t.string :item_id, null: false
6
+ t.string :event, null: false
7
+ t.string :whodunnit
8
+ t.jsonb :object
9
+ t.jsonb :object_changes
10
+ t.datetime :created_at
11
+ end
12
+ add_index :panda_cms_page_versions, %i[item_type item_id]
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ class CreatePandaCmsTemplates < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_templates, id: :uuid do |t|
4
+ t.string :name
5
+ t.string :file_path
6
+ t.timestamps
7
+ end
8
+
9
+ add_reference :panda_cms_pages, :panda_cms_template, type: :uuid, foreign_key: {to_table: :panda_cms_templates}, null: false
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePandaCmsTemplateVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_template_versions, id: :uuid do |t|
4
+ t.string :item_type, null: false
5
+ t.string :item_id, null: false
6
+ t.string :event, null: false
7
+ t.string :whodunnit
8
+ t.jsonb :object
9
+ t.jsonb :object_changes
10
+ t.datetime :created_at
11
+ end
12
+ add_index :panda_cms_template_versions, %i[item_type item_id]
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePandaCmsBlocks < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_enum :panda_cms_block_kind, ["plain_text", "rich_text", "image", "video", "audio", "file", "code", "iframe", "quote", "list", "table", "form"]
4
+
5
+ create_table :panda_cms_blocks, id: :uuid do |t|
6
+ t.enum :kind, enum_type: :panda_cms_block_kind, default: "plain_text", null: false
7
+ t.string :name
8
+ t.string :key
9
+ t.references :panda_cms_template, type: :uuid, foreign_key: {to_table: :panda_cms_templates}, null: false
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class CreatePandaCmsBlockContents < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_block_contents, id: :uuid do |t|
4
+ t.references :panda_cms_page, null: false, foreign_key: true, type: :uuid
5
+ t.references :panda_cms_block, null: false, foreign_key: true, type: :uuid
6
+ t.jsonb :content, null: false, default: {}
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePandaCmsBlockContentVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_block_content_versions, id: :uuid do |t|
4
+ t.string :item_type, null: false
5
+ t.string :item_id, null: false
6
+ t.string :event, null: false
7
+ t.string :whodunnit
8
+ t.jsonb :object
9
+ t.jsonb :object_changes
10
+ t.datetime :created_at
11
+ end
12
+ add_index :panda_cms_block_content_versions, %i[item_type item_id]
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ class AddPandaCmsMenuTable < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_menus, id: :uuid do |t|
4
+ t.string :name, null: false
5
+ t.timestamps
6
+ end
7
+
8
+ add_index :panda_cms_menus, :name, unique: true
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ class AddPandaCmsMenuItemTable < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_menu_items, id: :uuid do |t|
4
+ t.string :text, null: false
5
+ t.references :panda_cms_menu, null: false, foreign_key: true, type: :uuid
6
+ t.references :panda_cms_page, null: true, foreign_key: true, type: :uuid
7
+ t.string :external_url, null: true
8
+ t.integer :sort_order, default: 1
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ class AddParentIdToPandaCmsPages < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_reference :panda_cms_pages, :parent, type: :uuid, foreign_key: {to_table: :panda_cms_pages}
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class AddNestedSetsToPandaCmsPages < ActiveRecord::Migration[7.1]
2
+ def self.up
3
+ PandaCms::Page.where(parent_id: 0).update_all(parent_id: nil)
4
+ add_column :panda_cms_pages, :lft, :integer
5
+ add_column :panda_cms_pages, :rgt, :integer
6
+
7
+ # This is necessary to update :lft and :rgt columns
8
+ PandaCms::Page.reset_column_information
9
+ PandaCms::Page.rebuild!
10
+ end
11
+
12
+ def self.down
13
+ remove_column :panda_cms_pages, :lft
14
+ remove_column :panda_cms_pages, :rgt
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ class AddKindToPandaCmsMenus < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_enum :panda_cms_menu_kind, ["static", "auto"]
4
+ add_column :panda_cms_menus, :kind, :enum, enum_type: :panda_cms_menu_kind, default: "static", null: false
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddStartPageToPandaCmsMenus < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_reference :panda_cms_menus, :start_page, type: :uuid, foreign_key: {to_table: :panda_cms_pages}
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ class AddNestedToPandaCmsMenuItems < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :panda_cms_menu_items, :parent_id, :uuid
4
+ add_column :panda_cms_menu_items, :lft, :integer
5
+ add_column :panda_cms_menu_items, :rgt, :integer
6
+ add_column :panda_cms_menu_items, :depth, :integer
7
+ add_column :panda_cms_menu_items, :children_count, :integer, null: false, default: 0
8
+
9
+ add_index :panda_cms_menu_items, :lft
10
+ add_index :panda_cms_menu_items, :rgt
11
+
12
+ PandaCms::MenuItem.reset_column_information
13
+ PandaCms::MenuItem.rebuild!
14
+
15
+ # Update pages whilst we're at it
16
+ add_column :panda_cms_pages, :depth, :integer
17
+ add_column :panda_cms_pages, :children_count, :integer, null: false, default: 0
18
+ add_index :panda_cms_pages, :lft
19
+ add_index :panda_cms_pages, :rgt
20
+
21
+ PandaCms::Page.reset_column_information
22
+ PandaCms::Page.rebuild!
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ class CreatePandaCmsUsers < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_users, id: :uuid do |t|
4
+ t.string :firstname
5
+ t.string :lastname
6
+ t.string :email
7
+ t.string :image_url
8
+ t.boolean :admin
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ class AddMaxUsesToPandaCmsTemplate < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :panda_cms_templates, :max_uses, :integer, null: true, default: nil
4
+ add_column :panda_cms_templates, :pages_count, :integer, default: 0
5
+ PandaCms::Template.find_by(name: "Homepage")&.update(max_uses: 1)
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class ResetCounterCacheOnPandaCmsTemplate < ActiveRecord::Migration[7.1]
2
+ def change
3
+ PandaCms::Template.find_each { |t| PandaCms::Template.reset_counters(t.id, :pages) }
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class CreatePandaCmsRedirects < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_redirects, id: :uuid do |t|
4
+ t.string :origin_path, null: true
5
+ t.string :destination_path, null: true
6
+ t.references :origin_panda_cms_page, null: true, foreign_key: {to_table: :panda_cms_pages}, type: :uuid
7
+ t.references :destination_panda_cms_page, null: true, foreign_key: {to_table: :panda_cms_pages}, type: :uuid
8
+ t.integer :status_code, default: 301, null: false
9
+ t.integer :visits, default: 0, null: false
10
+ t.datetime :last_visited_at, null: true
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePandaCmsVisits < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_visits, id: :uuid do |t|
4
+ t.string :ip_address, null: true
5
+ t.string :user_agent, null: true
6
+
7
+ t.references :panda_cms_page, null: true, foreign_key: true, type: :uuid
8
+ t.references :panda_cms_redirect, null: true, foreign_key: true, type: :uuid
9
+ t.references :panda_cms_user, null: true, foreign_key: true, type: :uuid
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # This migration comes from active_storage (originally 20170806125915)
2
+ class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
3
+ def change
4
+ # Use Active Record's configured type for primary and foreign keys
5
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
6
+
7
+ create_table :active_storage_blobs, id: primary_key_type do |t|
8
+ t.string :key, null: false
9
+ t.string :filename, null: false
10
+ t.string :content_type
11
+ t.text :metadata
12
+ t.string :service_name, null: false
13
+ t.bigint :byte_size, null: false
14
+ t.string :checksum
15
+
16
+ if connection.supports_datetime_with_precision?
17
+ t.datetime :created_at, precision: 6, null: false
18
+ else
19
+ t.datetime :created_at, null: false
20
+ end
21
+
22
+ t.index [:key], unique: true
23
+ end
24
+
25
+ create_table :active_storage_attachments, id: primary_key_type do |t|
26
+ t.string :name, null: false
27
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28
+ t.references :blob, null: false, type: foreign_key_type
29
+
30
+ if connection.supports_datetime_with_precision?
31
+ t.datetime :created_at, precision: 6, null: false
32
+ else
33
+ t.datetime :created_at, null: false
34
+ end
35
+
36
+ t.index [:record_type, :record_id, :name, :blob_id], name: :index_active_storage_attachments_uniqueness, unique: true
37
+ t.foreign_key :active_storage_blobs, column: :blob_id
38
+ end
39
+
40
+ create_table :active_storage_variant_records, id: primary_key_type do |t|
41
+ t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42
+ t.string :variation_digest, null: false
43
+
44
+ t.index [:blob_id, :variation_digest], name: :index_active_storage_variant_records_uniqueness, unique: true
45
+ t.foreign_key :active_storage_blobs, column: :blob_id
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def primary_and_foreign_key_types
52
+ config = Rails.configuration.generators
53
+ setting = config.options[config.orm][:primary_key_type]
54
+ primary_key_type = setting || :primary_key
55
+ foreign_key_type = setting || :bigint
56
+ [primary_key_type, foreign_key_type]
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ class DefaultPandaCmsUsersAdminToFalse < ActiveRecord::Migration[7.1]
2
+ def change
3
+ change_column :panda_cms_users, :admin, :boolean, default: false
4
+ end
5
+ end
data/db/seeds.rb ADDED
@@ -0,0 +1,4 @@
1
+ generator = PandaCms::DemoSiteGenerator.new
2
+ generator.create_templates
3
+ generator.create_pages
4
+ generator.create_menus