panda-cms 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/app/assets/builds/panda.cms.css +2808 -0
- data/app/assets/config/panda_cms_manifest.js +4 -0
- data/app/assets/stylesheets/panda/cms/application.tailwind.css +162 -0
- data/app/assets/stylesheets/panda/cms/editor.css +120 -0
- data/app/builders/panda/cms/form_builder.rb +234 -0
- data/app/components/panda/cms/admin/button_component.rb +70 -0
- data/app/components/panda/cms/admin/container_component.html.erb +13 -0
- data/app/components/panda/cms/admin/container_component.rb +13 -0
- data/app/components/panda/cms/admin/flash_message_component.html.erb +31 -0
- data/app/components/panda/cms/admin/flash_message_component.rb +47 -0
- data/app/components/panda/cms/admin/heading_component.rb +45 -0
- data/app/components/panda/cms/admin/panel_component.html.erb +7 -0
- data/app/components/panda/cms/admin/panel_component.rb +13 -0
- data/app/components/panda/cms/admin/slideover_component.html.erb +9 -0
- data/app/components/panda/cms/admin/slideover_component.rb +15 -0
- data/app/components/panda/cms/admin/statistics_component.html.erb +4 -0
- data/app/components/panda/cms/admin/statistics_component.rb +17 -0
- data/app/components/panda/cms/admin/tab_bar_component.html.erb +35 -0
- data/app/components/panda/cms/admin/tab_bar_component.rb +15 -0
- data/app/components/panda/cms/admin/table_component.html.erb +29 -0
- data/app/components/panda/cms/admin/table_component.rb +46 -0
- data/app/components/panda/cms/admin/tag_component.rb +35 -0
- data/app/components/panda/cms/admin/user_activity_component.html.erb +5 -0
- data/app/components/panda/cms/admin/user_activity_component.rb +33 -0
- data/app/components/panda/cms/admin/user_display_component.html.erb +17 -0
- data/app/components/panda/cms/admin/user_display_component.rb +21 -0
- data/app/components/panda/cms/code_component.rb +64 -0
- data/app/components/panda/cms/grid_component.html.erb +6 -0
- data/app/components/panda/cms/grid_component.rb +15 -0
- data/app/components/panda/cms/menu_component.html.erb +6 -0
- data/app/components/panda/cms/menu_component.rb +58 -0
- data/app/components/panda/cms/page_menu_component.html.erb +21 -0
- data/app/components/panda/cms/page_menu_component.rb +38 -0
- data/app/components/panda/cms/rich_text_component.html.erb +6 -0
- data/app/components/panda/cms/rich_text_component.rb +84 -0
- data/app/components/panda/cms/text_component.rb +72 -0
- data/app/constraints/panda/cms/admin_constraint.rb +18 -0
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +52 -0
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +20 -0
- data/app/controllers/panda/cms/admin/files_controller.rb +21 -0
- data/app/controllers/panda/cms/admin/forms_controller.rb +53 -0
- data/app/controllers/panda/cms/admin/menus_controller.rb +30 -0
- data/app/controllers/panda/cms/admin/pages_controller.rb +91 -0
- data/app/controllers/panda/cms/admin/posts_controller.rb +146 -0
- data/app/controllers/panda/cms/admin/sessions_controller.rb +94 -0
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +37 -0
- data/app/controllers/panda/cms/admin/settings_controller.rb +20 -0
- data/app/controllers/panda/cms/application_controller.rb +57 -0
- data/app/controllers/panda/cms/errors_controller.rb +33 -0
- data/app/controllers/panda/cms/form_submissions_controller.rb +23 -0
- data/app/controllers/panda/cms/pages_controller.rb +72 -0
- data/app/controllers/panda/cms/posts_controller.rb +13 -0
- data/app/helpers/panda/cms/admin/files_helper.rb +6 -0
- data/app/helpers/panda/cms/admin/pages_helper.rb +6 -0
- data/app/helpers/panda/cms/admin/posts_helper.rb +48 -0
- data/app/helpers/panda/cms/application_helper.rb +120 -0
- data/app/helpers/panda/cms/pages_helper.rb +6 -0
- data/app/helpers/panda/cms/theme_helper.rb +18 -0
- data/app/javascript/panda/cms/@editorjs--editorjs.js +2577 -0
- data/app/javascript/panda/cms/@hotwired--stimulus.js +4 -0
- data/app/javascript/panda/cms/@hotwired--turbo.js +160 -0
- data/app/javascript/panda/cms/@rails--actioncable--src.js +4 -0
- data/app/javascript/panda/cms/application_panda_cms.js +39 -0
- data/app/javascript/panda/cms/controllers/dashboard_controller.js +7 -0
- data/app/javascript/panda/cms/controllers/editor_form_controller.js +77 -0
- data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +320 -0
- data/app/javascript/panda/cms/controllers/index.js +48 -0
- data/app/javascript/panda/cms/controllers/slug_controller.js +87 -0
- data/app/javascript/panda/cms/editor/css_extractor.js +80 -0
- data/app/javascript/panda/cms/editor/editor_js_config.js +177 -0
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +285 -0
- data/app/javascript/panda/cms/editor/plain_text_editor.js +110 -0
- data/app/javascript/panda/cms/editor/resource_loader.js +115 -0
- data/app/javascript/panda/cms/tailwindcss-stimulus-components.js +4 -0
- data/app/jobs/panda/cms/application_job.rb +6 -0
- data/app/jobs/panda/cms/record_visit_job.rb +31 -0
- data/app/mailers/panda/cms/application_mailer.rb +8 -0
- data/app/mailers/panda/cms/form_mailer.rb +21 -0
- data/app/models/action_text/rich_text_version.rb +6 -0
- data/app/models/panda/cms/application_record.rb +7 -0
- data/app/models/panda/cms/block.rb +34 -0
- data/app/models/panda/cms/block_content.rb +18 -0
- data/app/models/panda/cms/block_content_version.rb +8 -0
- data/app/models/panda/cms/breadcrumb.rb +12 -0
- data/app/models/panda/cms/current.rb +17 -0
- data/app/models/panda/cms/form.rb +9 -0
- data/app/models/panda/cms/form_submission.rb +7 -0
- data/app/models/panda/cms/menu.rb +52 -0
- data/app/models/panda/cms/menu_item.rb +58 -0
- data/app/models/panda/cms/page.rb +96 -0
- data/app/models/panda/cms/page_version.rb +8 -0
- data/app/models/panda/cms/post.rb +60 -0
- data/app/models/panda/cms/post_version.rb +8 -0
- data/app/models/panda/cms/redirect.rb +11 -0
- data/app/models/panda/cms/template.rb +124 -0
- data/app/models/panda/cms/template_version.rb +8 -0
- data/app/models/panda/cms/user.rb +31 -0
- data/app/models/panda/cms/version.rb +8 -0
- data/app/models/panda/cms/visit.rb +9 -0
- data/app/services/panda/cms/html_to_editor_js_converter.rb +200 -0
- data/app/views/active_storage/blobs/blobs/_blob.html.erb +14 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/panda/cms/application.html.erb +41 -0
- data/app/views/layouts/panda/cms/public.html.erb +3 -0
- data/app/views/panda/cms/admin/dashboard/show.html.erb +12 -0
- data/app/views/panda/cms/admin/files/index.html.erb +124 -0
- data/app/views/panda/cms/admin/files/show.html.erb +2 -0
- data/app/views/panda/cms/admin/forms/edit.html.erb +0 -0
- data/app/views/panda/cms/admin/forms/index.html.erb +13 -0
- data/app/views/panda/cms/admin/forms/new.html.erb +15 -0
- data/app/views/panda/cms/admin/forms/show.html.erb +35 -0
- data/app/views/panda/cms/admin/menus/index.html.erb +8 -0
- data/app/views/panda/cms/admin/pages/edit.html.erb +36 -0
- data/app/views/panda/cms/admin/pages/index.html.erb +22 -0
- data/app/views/panda/cms/admin/pages/new.html.erb +15 -0
- data/app/views/panda/cms/admin/pages/show.html.erb +1 -0
- data/app/views/panda/cms/admin/posts/_form.html.erb +29 -0
- data/app/views/panda/cms/admin/posts/edit.html.erb +6 -0
- data/app/views/panda/cms/admin/posts/index.html.erb +18 -0
- data/app/views/panda/cms/admin/posts/new.html.erb +6 -0
- data/app/views/panda/cms/admin/sessions/new.html.erb +17 -0
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +68 -0
- data/app/views/panda/cms/admin/settings/index.html.erb +21 -0
- data/app/views/panda/cms/admin/settings/insta.html +4 -0
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +28 -0
- data/app/views/panda/cms/admin/shared/_flash.html.erb +5 -0
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +41 -0
- data/app/views/panda/cms/form_mailer/notification_email.html.erb +11 -0
- data/app/views/panda/cms/shared/_editor.html.erb +0 -0
- data/app/views/panda/cms/shared/_favicons.html.erb +9 -0
- data/app/views/panda/cms/shared/_footer.html.erb +2 -0
- data/app/views/panda/cms/shared/_header.html.erb +15 -0
- data/app/views/panda/cms/shared/_importmap.html.erb +33 -0
- data/config/importmap.rb +13 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/initializers/panda/cms/form_errors.rb +38 -0
- data/config/initializers/panda/cms/healthcheck_log_silencer.rb +11 -0
- data/config/initializers/panda/cms/paper_trail.rb +7 -0
- data/config/initializers/panda/cms.rb +10 -0
- data/config/initializers/zeitwork.rb +3 -0
- data/config/locales/en.yml +49 -0
- data/config/puma/test.rb +9 -0
- data/config/routes.rb +48 -0
- data/config/tailwind.config.js +37 -0
- data/db/migrate/20240205223709_create_panda_cms_pages.rb +9 -0
- data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +14 -0
- data/db/migrate/20240303002805_create_panda_cms_templates.rb +11 -0
- data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +14 -0
- data/db/migrate/20240303022441_create_panda_cms_blocks.rb +13 -0
- data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +10 -0
- data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +14 -0
- data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +10 -0
- data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +12 -0
- data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +5 -0
- data/db/migrate/20240305000000_convert_html_content_to_editor_js.rb +82 -0
- data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +9 -0
- data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +16 -0
- data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +6 -0
- data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +5 -0
- data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +24 -0
- data/db/migrate/20240317010532_create_panda_cms_users.rb +12 -0
- data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +7 -0
- data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +5 -0
- data/db/migrate/20240317214827_create_panda_cms_redirects.rb +14 -0
- data/db/migrate/20240317230622_create_panda_cms_visits.rb +13 -0
- data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +58 -0
- data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +5 -0
- data/db/migrate/20240701225422_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
- data/db/migrate/20240701225423_create_active_storage_variant_records.active_storage.rb +28 -0
- data/db/migrate/20240701225424_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +8 -0
- data/db/migrate/20240804235210_create_panda_cms_forms.rb +11 -0
- data/db/migrate/20240805013612_create_panda_cms_form_submissions.rb +9 -0
- data/db/migrate/20240805121123_create_panda_cms_posts.rb +27 -0
- data/db/migrate/20240805123104_create_panda_cms_post_versions.rb +14 -0
- data/db/migrate/20240806112735_fix_panda_cms_visits_column_names.rb +13 -0
- data/db/migrate/20240806204412_add_completion_path_to_panda_cms_forms.rb +5 -0
- data/db/migrate/20240820081917_change_form_submissions_to_submission_count.rb +5 -0
- data/db/migrate/20240904200605_create_action_text_tables.action_text.rb +24 -0
- data/db/migrate/20240923234535_add_depth_to_panda_cms_menus.rb +11 -0
- data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +5 -0
- data/db/migrate/20241119214548_convert_post_content_to_editor_js.rb +35 -0
- data/db/migrate/20241119214549_remove_action_text_from_posts.rb +9 -0
- data/db/migrate/20241120000419_remove_post_tag_references.rb +19 -0
- data/db/migrate/20241120110943_add_editor_js_to_posts.rb +27 -0
- data/db/migrate/20241120113859_add_cached_content_to_panda_cms_posts.rb +5 -0
- data/db/migrate/20241123234140_remove_post_tag_id_from_posts.rb +5 -0
- data/db/migrate/migrate +1 -0
- data/db/seeds.rb +5 -0
- data/lib/generators/panda/cms/install_generator.rb +29 -0
- data/lib/panda/cms/bulk_editor.rb +171 -0
- data/lib/panda/cms/demo_site_generator.rb +67 -0
- data/lib/panda/cms/editor_js/blocks/alert.rb +34 -0
- data/lib/panda/cms/editor_js/blocks/base.rb +33 -0
- data/lib/panda/cms/editor_js/blocks/header.rb +15 -0
- data/lib/panda/cms/editor_js/blocks/image.rb +36 -0
- data/lib/panda/cms/editor_js/blocks/list.rb +32 -0
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +15 -0
- data/lib/panda/cms/editor_js/blocks/quote.rb +41 -0
- data/lib/panda/cms/editor_js/blocks/table.rb +50 -0
- data/lib/panda/cms/editor_js/renderer.rb +124 -0
- data/lib/panda/cms/editor_js.rb +16 -0
- data/lib/panda/cms/editor_js_content.rb +21 -0
- data/lib/panda/cms/engine.rb +257 -0
- data/lib/panda/cms/exceptions_app.rb +26 -0
- data/lib/panda/cms/railtie.rb +11 -0
- data/lib/panda/cms/slug.rb +24 -0
- data/lib/panda/cms.rb +0 -0
- data/lib/panda-cms/version.rb +5 -0
- data/lib/panda-cms.rb +81 -0
- data/lib/tasks/panda_cms.rake +54 -0
- data/lib/templates/erb/scaffold/_form.html.erb.tt +43 -0
- data/lib/templates/erb/scaffold/edit.html.erb.tt +8 -0
- data/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
- data/lib/templates/erb/scaffold/new.html.erb.tt +7 -0
- data/lib/templates/erb/scaffold/partial.html.erb.tt +22 -0
- data/lib/templates/erb/scaffold/show.html.erb.tt +15 -0
- data/public/panda-cms-assets/favicons/android-chrome-192x192.png +0 -0
- data/public/panda-cms-assets/favicons/android-chrome-512x512.png +0 -0
- data/public/panda-cms-assets/favicons/apple-touch-icon.png +0 -0
- data/public/panda-cms-assets/favicons/browserconfig.xml +9 -0
- data/public/panda-cms-assets/favicons/favicon-16x16.png +0 -0
- data/public/panda-cms-assets/favicons/favicon-32x32.png +0 -0
- data/public/panda-cms-assets/favicons/favicon.ico +0 -0
- data/public/panda-cms-assets/favicons/mstile-150x150.png +0 -0
- data/public/panda-cms-assets/favicons/safari-pinned-tab.svg +61 -0
- data/public/panda-cms-assets/favicons/site.webmanifest +14 -0
- data/public/panda-cms-assets/panda-logo-screenprint.png +0 -0
- data/public/panda-cms-assets/panda-nav.png +0 -0
- data/public/panda-cms-assets/rich_text_editor.css +568 -0
- metadata +654 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative "editor_js/blocks/base"
|
|
2
|
+
require_relative "editor_js/blocks/alert"
|
|
3
|
+
require_relative "editor_js/blocks/header"
|
|
4
|
+
require_relative "editor_js/blocks/image"
|
|
5
|
+
require_relative "editor_js/blocks/list"
|
|
6
|
+
require_relative "editor_js/blocks/paragraph"
|
|
7
|
+
require_relative "editor_js/blocks/quote"
|
|
8
|
+
require_relative "editor_js/blocks/table"
|
|
9
|
+
require_relative "editor_js/renderer"
|
|
10
|
+
|
|
11
|
+
module Panda
|
|
12
|
+
module CMS
|
|
13
|
+
module EditorJs
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module Panda::CMS::EditorJsContent
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
include ActiveModel::Validations
|
|
8
|
+
include ActiveModel::Callbacks
|
|
9
|
+
|
|
10
|
+
before_save :generate_cached_content
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def generate_cached_content
|
|
14
|
+
if content.is_a?(String)
|
|
15
|
+
self.cached_content = content
|
|
16
|
+
elsif content.is_a?(Hash) && content["blocks"].present?
|
|
17
|
+
# Process EditorJS content
|
|
18
|
+
self.cached_content = Panda::CMS::EditorJs::Renderer.new(content).render
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
require "importmap-rails"
|
|
2
|
+
require "turbo-rails"
|
|
3
|
+
require "stimulus-rails"
|
|
4
|
+
require "view_component"
|
|
5
|
+
|
|
6
|
+
require "panda/cms/railtie"
|
|
7
|
+
|
|
8
|
+
module Panda
|
|
9
|
+
module CMS
|
|
10
|
+
class Engine < ::Rails::Engine
|
|
11
|
+
isolate_namespace Panda::CMS
|
|
12
|
+
engine_name "panda_cms"
|
|
13
|
+
|
|
14
|
+
# Add services directory to autoload paths
|
|
15
|
+
config.autoload_paths += %W[
|
|
16
|
+
#{root}/app/services
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Basic session setup only
|
|
20
|
+
initializer "panda.cms.session", before: :load_config_initializers do |app|
|
|
21
|
+
if app.config.middleware.frozen?
|
|
22
|
+
app.config.middleware = app.config.middleware.dup
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
app.config.session_store :cookie_store, key: "_panda_cms_session"
|
|
26
|
+
app.config.middleware.use ActionDispatch::Cookies
|
|
27
|
+
app.config.middleware.use ActionDispatch::Session::CookieStore, app.config.session_options
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
config.to_prepare do
|
|
31
|
+
ApplicationController.helper(::ApplicationHelper)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Set our generators
|
|
35
|
+
config.generators do |g|
|
|
36
|
+
g.orm :active_record, primary_key_type: :uuid
|
|
37
|
+
g.test_framework :rspec, fixture: true
|
|
38
|
+
g.fixture_replacement :factory_bot, dir: "spec/factories"
|
|
39
|
+
g.view_specs false
|
|
40
|
+
g.templates.unshift File.expand_path("../../templates", __FILE__)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Make files in public available to the main app (e.g. /panda-cms-assets/favicon.ico)
|
|
44
|
+
config.app_middleware.use(
|
|
45
|
+
Rack::Static,
|
|
46
|
+
urls: ["/panda-cms-assets"],
|
|
47
|
+
root: Panda::CMS::Engine.root.join("public")
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Custom error handling
|
|
51
|
+
# config.exceptions_app = Panda::CMS::ExceptionsApp.new(exceptions_app: routes)
|
|
52
|
+
|
|
53
|
+
initializer "panda.cms.assets" do |app|
|
|
54
|
+
if Rails.configuration.respond_to?(:assets)
|
|
55
|
+
# Add JavaScript paths
|
|
56
|
+
app.config.assets.paths << root.join("app/javascript")
|
|
57
|
+
app.config.assets.paths << root.join("app/javascript/panda")
|
|
58
|
+
app.config.assets.paths << root.join("app/javascript/panda/cms")
|
|
59
|
+
app.config.assets.paths << root.join("app/javascript/panda/cms/controllers")
|
|
60
|
+
|
|
61
|
+
# Make sure these files are precompiled
|
|
62
|
+
app.config.assets.precompile += %w[
|
|
63
|
+
panda_cms_manifest.js
|
|
64
|
+
panda/cms/controllers/**/*.js
|
|
65
|
+
panda/cms/application_panda_cms.js
|
|
66
|
+
]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Add importmap paths from the engine
|
|
71
|
+
initializer "panda.cms.importmap", before: "importmap" do |app|
|
|
72
|
+
if app.config.respond_to?(:importmap)
|
|
73
|
+
# Create a new array if frozen
|
|
74
|
+
if app.config.importmap.paths.frozen?
|
|
75
|
+
app.config.importmap.paths = app.config.importmap.paths.dup
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Add our paths
|
|
79
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
80
|
+
|
|
81
|
+
# Handle cache sweepers similarly
|
|
82
|
+
if app.config.importmap.cache_sweepers.frozen?
|
|
83
|
+
app.config.importmap.cache_sweepers = app.config.importmap.cache_sweepers.dup
|
|
84
|
+
end
|
|
85
|
+
app.config.importmap.cache_sweepers << root.join("app/javascript")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
config.after_initialize do |app|
|
|
90
|
+
# Append routes to the routes file
|
|
91
|
+
app.routes.append do
|
|
92
|
+
mount Panda::CMS::Engine => "/", :as => "panda_cms"
|
|
93
|
+
post "/_forms/:id", to: "panda/cms/form_submissions#create", as: :panda_cms_form_submit
|
|
94
|
+
get "/_maintenance", to: "panda/cms/errors#error_503", as: :panda_cms_maintenance
|
|
95
|
+
get "/*path", to: "panda/cms/pages#show", as: :panda_cms_page
|
|
96
|
+
root to: "panda/cms/pages#root"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
initializer "#{engine_name}.backtrace_cleaner" do |app|
|
|
101
|
+
engine_root_regex = Regexp.escape(root.to_s + File::SEPARATOR)
|
|
102
|
+
|
|
103
|
+
# Clean those ERB lines, we don't need the internal autogenerated
|
|
104
|
+
# ERB method, what we do need (line number in ERB file) is already there
|
|
105
|
+
Rails.backtrace_cleaner.add_filter do |line|
|
|
106
|
+
line.sub(/(\.erb:\d+):in `__.*$/, "\\1")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Remove our own engine's path prefix, even if it's
|
|
110
|
+
# being used from a local path rather than the gem directory.
|
|
111
|
+
Rails.backtrace_cleaner.add_filter do |line|
|
|
112
|
+
line.sub(/^#{engine_root_regex}/, "#{engine_name} ")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Keep Umlaut's own stacktrace in the backtrace -- we have to remove Rails
|
|
116
|
+
# silencers and re-add them how we want.
|
|
117
|
+
Rails.backtrace_cleaner.remove_silencers!
|
|
118
|
+
|
|
119
|
+
# Silence what Rails silenced, UNLESS it looks like
|
|
120
|
+
# it's from Umlaut engine
|
|
121
|
+
Rails.backtrace_cleaner.add_silencer do |line|
|
|
122
|
+
(line !~ Rails::BacktraceCleaner::APP_DIRS_PATTERN) &&
|
|
123
|
+
(line !~ /^#{engine_root_regex}/) &&
|
|
124
|
+
(line !~ /^#{engine_name} /)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Set up ViewComponent and Lookbook
|
|
129
|
+
# config.view_component.component_parent_class = "Panda::CMS::BaseComponent"
|
|
130
|
+
# config.view_component.view_component_path = Panda::CMS::Engine.root.join("lib/components").to_s
|
|
131
|
+
# config.eager_load_paths << Panda::CMS::Engine.root.join("lib/components").to_s
|
|
132
|
+
# config.view_component.generate.sidecar = true
|
|
133
|
+
# config.view_component.generate.preview = truexw
|
|
134
|
+
# config.view_component.preview_paths ||= []
|
|
135
|
+
# config.view_component.preview_paths << Panda::CMS::Engine.root.join("lib/component_previews").to_s
|
|
136
|
+
# config.view_component.generate.preview_path = "lib/component_previews"
|
|
137
|
+
|
|
138
|
+
# Set up authentication
|
|
139
|
+
initializer "panda_cms.omniauth", before: "omniauth" do |app|
|
|
140
|
+
app.config.session_store :cookie_store, key: "_panda_cms_session"
|
|
141
|
+
app.config.middleware.use ActionDispatch::Cookies
|
|
142
|
+
app.config.middleware.use ActionDispatch::Session::CookieStore, app.config.session_options
|
|
143
|
+
|
|
144
|
+
OmniAuth.config.logger = Rails.logger
|
|
145
|
+
|
|
146
|
+
# TODO: Move this to somewhere more sensible?
|
|
147
|
+
# Define the mapping of our provider "names" to the OmniAuth strategies and configuration
|
|
148
|
+
auth_path = "#{Panda::CMS.route_namespace}/auth"
|
|
149
|
+
callback_path = "/callback"
|
|
150
|
+
available_providers = {
|
|
151
|
+
microsoft: {
|
|
152
|
+
strategy: :microsoft_graph,
|
|
153
|
+
defaults: {
|
|
154
|
+
name: "microsoft",
|
|
155
|
+
# Setup at the following URL:
|
|
156
|
+
# https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
|
|
157
|
+
client_id: Rails.application.credentials.dig(:microsoft, :client_id),
|
|
158
|
+
client_secret: Rails.application.credentials.dig(:microsoft, :client_secret),
|
|
159
|
+
# Don't change this or the sky will fall on your head
|
|
160
|
+
# https://github.com/synth/omniauth-microsoft_graph/tree/main?tab=readme-ov-file#domain-verification
|
|
161
|
+
skip_domain_verification: false,
|
|
162
|
+
# If your application is single-tenanted, replace "common" with your tenant (directory) ID
|
|
163
|
+
# from https://portal.azure.com/#settings/directory, otherwise you'll likely want to leave
|
|
164
|
+
# these settings unchanged
|
|
165
|
+
client_options: {
|
|
166
|
+
site: "https://login.microsoftonline.com/",
|
|
167
|
+
token_url: "common/oauth2/v2.0/token",
|
|
168
|
+
authorize_url: "common/oauth2/v2.0/authorize"
|
|
169
|
+
},
|
|
170
|
+
# If you assign specific users or groups, you will likely want to set this to
|
|
171
|
+
# true to enable auto-provisioning
|
|
172
|
+
create_account_on_first_login: false,
|
|
173
|
+
create_admin_account_on_first_login: false,
|
|
174
|
+
# Don't hide this provider from the login page
|
|
175
|
+
hidden: false
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
google: {
|
|
179
|
+
strategy: :google_oauth2,
|
|
180
|
+
defaults: {
|
|
181
|
+
name: "google",
|
|
182
|
+
# Setup at the following URL: https://console.developers.google.com/
|
|
183
|
+
client_id: Rails.application.credentials.dig(:google, :client_id),
|
|
184
|
+
client_secret: Rails.application.credentials.dig(:google, :client_secret),
|
|
185
|
+
# If you assign specific users or groups, you will likely want to set this to
|
|
186
|
+
# true to enable auto-provisioning
|
|
187
|
+
create_account_on_first_login: false,
|
|
188
|
+
create_admin_account_on_first_login: false,
|
|
189
|
+
# Options we need
|
|
190
|
+
scope: "email, profile",
|
|
191
|
+
image_aspect_ratio: "square",
|
|
192
|
+
image_size: 150,
|
|
193
|
+
# Worth setting select_account as default, as many people have multiple Google accounts now:
|
|
194
|
+
prompt: "select_account",
|
|
195
|
+
# You should probably also set the 'hd' option, huh?,
|
|
196
|
+
# Don't hide this provider from the login page
|
|
197
|
+
hidden: false
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
github: {
|
|
201
|
+
strategy: :github,
|
|
202
|
+
defaults: {
|
|
203
|
+
name: "github",
|
|
204
|
+
# Setup at the following URL: https://github.com/settings/applications/new
|
|
205
|
+
# with a callback of
|
|
206
|
+
# In the meantime, as long as you're set to /admin as your login path, and on
|
|
207
|
+
# http://localhost:3000, you can use these for a first login :)
|
|
208
|
+
client_id: Rails.application.credentials.dig(:github, :client_id),
|
|
209
|
+
client_secret: Rails.application.credentials.dig(:github, :client_secret),
|
|
210
|
+
scope: "user:email,read:user",
|
|
211
|
+
create_account_on_first_login: false,
|
|
212
|
+
create_admin_account_on_first_login: false,
|
|
213
|
+
# Don't hide this provider from the login page
|
|
214
|
+
hidden: false
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
available_providers.each do |provider, options|
|
|
220
|
+
if Panda::CMS.config.authentication.dig(provider, :enabled)
|
|
221
|
+
auth_path = auth_path.starts_with?("/") ? auth_path : "/#{auth_path}"
|
|
222
|
+
options[:defaults][:path_prefix] = auth_path
|
|
223
|
+
|
|
224
|
+
options[:defaults][:redirect_uri] = if Rails.env.test?
|
|
225
|
+
"#{Capybara.app_host}#{auth_path}/#{provider}#{callback_path}"
|
|
226
|
+
else
|
|
227
|
+
"#{Panda::CMS.config.url}#{auth_path}/#{provider}#{callback_path}"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
provider_config = options[:defaults].merge(Panda::CMS.config.authentication[provider])
|
|
231
|
+
|
|
232
|
+
app.config.middleware.use OmniAuth::Builder do
|
|
233
|
+
provider options[:strategy], provider_config
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
config.before_initialize do |app|
|
|
240
|
+
# Default configuration
|
|
241
|
+
Panda::CMS.configure do |config|
|
|
242
|
+
# Array of additional EditorJS tools to load
|
|
243
|
+
# Example: [{ url: "https://cdn.jsdelivr.net/npm/@editorjs/image@latest" }]
|
|
244
|
+
config.editor_js_tools ||= []
|
|
245
|
+
|
|
246
|
+
# Hash of EditorJS tool configurations
|
|
247
|
+
# Example: { image: { class: 'ImageTool', config: { ... } } }
|
|
248
|
+
config.editor_js_tool_config ||= {}
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
class MissingBlockError < StandardError; end
|
|
254
|
+
|
|
255
|
+
class BlockError < StandardError; end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# https://guides.rubyonrails.org/configuring.html#config-exceptions-app
|
|
2
|
+
module Panda
|
|
3
|
+
module CMS
|
|
4
|
+
class ExceptionsApp
|
|
5
|
+
def initialize(exceptions_app:)
|
|
6
|
+
@exceptions_app = exceptions_app
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
request = ActionDispatch::Request.new(env)
|
|
11
|
+
|
|
12
|
+
fallback_to_html_format_if_invalid_mime_type(request)
|
|
13
|
+
|
|
14
|
+
@exceptions_app.call(env)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def fallback_to_html_format_if_invalid_mime_type(request)
|
|
20
|
+
request.formats
|
|
21
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
|
22
|
+
request.set_header "CONTENT_TYPE", "text/html"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Panda
|
|
2
|
+
module CMS
|
|
3
|
+
class Slug
|
|
4
|
+
#
|
|
5
|
+
# Generates a slug from a provided string
|
|
6
|
+
#
|
|
7
|
+
# @param string [String] The provided string to turn into a slug
|
|
8
|
+
# @return string Generated slug
|
|
9
|
+
# @see slug_controller.js should also implement this logic
|
|
10
|
+
def self.generate(string)
|
|
11
|
+
# Trim whitespace and downcase the string
|
|
12
|
+
string = string.to_s.strip.downcase
|
|
13
|
+
# Replace & with "and"
|
|
14
|
+
string = string.gsub("&", "and")
|
|
15
|
+
# Remove special characters
|
|
16
|
+
string = string.gsub(/[\!\@\£\$\%\^\&\*\(\)\+\=\{\}\[\]\:\;\"\'\|\\\`\<\>\?\,\.\/]+/, "")
|
|
17
|
+
# Replace any whitespace character with -
|
|
18
|
+
string = string.gsub(/[^\w\s-]/, "-")
|
|
19
|
+
# Replace multiple occurences of _ and - with -
|
|
20
|
+
string.gsub(/[\s_-]+/, "-")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/panda/cms.rb
ADDED
|
File without changes
|
data/lib/panda-cms.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require "dry-configurable"
|
|
2
|
+
require "importmap-rails"
|
|
3
|
+
require "lookbook"
|
|
4
|
+
require "omniauth"
|
|
5
|
+
require "omniauth/rails_csrf_protection"
|
|
6
|
+
require "omniauth/strategies/microsoft_graph"
|
|
7
|
+
require "omniauth/strategies/google_oauth2"
|
|
8
|
+
require "omniauth/strategies/github"
|
|
9
|
+
require "paper_trail"
|
|
10
|
+
require "view_component"
|
|
11
|
+
require "zeitwerk"
|
|
12
|
+
|
|
13
|
+
module Panda
|
|
14
|
+
module CMS
|
|
15
|
+
extend Dry::Configurable
|
|
16
|
+
|
|
17
|
+
setting :title, default: "Demo Site"
|
|
18
|
+
setting :admin_path, default: "/admin"
|
|
19
|
+
setting :require_login_to_view, default: false
|
|
20
|
+
setting :authentication, default: {}
|
|
21
|
+
setting :posts, default: {enabled: true, prefix: "blog"}
|
|
22
|
+
setting :route_namespace, default: "/admin"
|
|
23
|
+
setting :url
|
|
24
|
+
setting :editor_js_tools, default: []
|
|
25
|
+
setting :editor_js_tool_config, default: {}
|
|
26
|
+
|
|
27
|
+
def self.root_path
|
|
28
|
+
config.admin_path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
attr_accessor :loader
|
|
33
|
+
|
|
34
|
+
def route_namespace
|
|
35
|
+
config.admin_path
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def configure
|
|
39
|
+
yield config if block_given?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Set up autoloading for the gem's internals
|
|
46
|
+
Panda::CMS.loader = Zeitwerk::Loader.new
|
|
47
|
+
Panda::CMS.loader.tag = "panda-cms"
|
|
48
|
+
|
|
49
|
+
# Ignore the panda-cms directory
|
|
50
|
+
Panda::CMS.loader.ignore("#{__dir__}/panda-cms")
|
|
51
|
+
|
|
52
|
+
# Only autoload the panda/cms directory
|
|
53
|
+
Panda::CMS.loader.push_dir(File.expand_path("panda/cms", __dir__), namespace: Panda::CMS)
|
|
54
|
+
|
|
55
|
+
# Ignore both lib and lib/panda directories to prevent Rails autoloader conflicts
|
|
56
|
+
Panda::CMS.loader.ignore(__dir__.to_s)
|
|
57
|
+
Panda::CMS.loader.ignore(File.expand_path("panda", __dir__))
|
|
58
|
+
|
|
59
|
+
# Configure Zeitwerk inflections
|
|
60
|
+
Panda::CMS.loader.inflector.inflect(
|
|
61
|
+
"cms" => "CMS"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Manually require files from panda-cms directory
|
|
65
|
+
require_relative "panda-cms/version"
|
|
66
|
+
require_relative "panda/cms/exceptions_app"
|
|
67
|
+
require_relative "panda/cms/engine"
|
|
68
|
+
require_relative "panda/cms/demo_site_generator"
|
|
69
|
+
require_relative "panda/cms/editor_js"
|
|
70
|
+
require_relative "panda/cms/editor_js_content"
|
|
71
|
+
require_relative "panda/cms/editor_js/renderer"
|
|
72
|
+
require_relative "panda/cms/editor_js/blocks/alert"
|
|
73
|
+
require_relative "panda/cms/editor_js/blocks/header"
|
|
74
|
+
require_relative "panda/cms/editor_js/blocks/image"
|
|
75
|
+
require_relative "panda/cms/editor_js/blocks/list"
|
|
76
|
+
require_relative "panda/cms/editor_js/blocks/paragraph"
|
|
77
|
+
require_relative "panda/cms/editor_js/blocks/quote"
|
|
78
|
+
require_relative "panda/cms/editor_js/blocks/table"
|
|
79
|
+
require_relative "panda/cms/slug"
|
|
80
|
+
|
|
81
|
+
Panda::CMS.loader.setup
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "tailwindcss-rails"
|
|
2
|
+
require "tailwindcss/ruby"
|
|
3
|
+
require "shellwords"
|
|
4
|
+
|
|
5
|
+
require "panda/cms/engine"
|
|
6
|
+
|
|
7
|
+
ENV["TAILWIND_PATH"] ||= Tailwindcss::Engine.root.join("exe/tailwindcss").to_s
|
|
8
|
+
|
|
9
|
+
namespace :panda do
|
|
10
|
+
namespace :cms do
|
|
11
|
+
desc "Watch admin assets for Panda CMS"
|
|
12
|
+
# We only care about this in development
|
|
13
|
+
task :watch_admin do
|
|
14
|
+
run_tailwind(
|
|
15
|
+
root_path: Panda::CMS::Engine.root,
|
|
16
|
+
input_path: "app/assets/stylesheets/panda/cms/application.tailwind.css",
|
|
17
|
+
output_path: "app/assets/builds/panda.cms.css",
|
|
18
|
+
watch: true,
|
|
19
|
+
minify: false
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Generate missing blocks from template files"
|
|
24
|
+
task generate_missing_blocks: [:environment] do
|
|
25
|
+
Panda::CMS::Template.generate_missing_blocks
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
namespace :export do
|
|
29
|
+
desc "Generate a .json export and output to stdout"
|
|
30
|
+
task json: [:environment] do
|
|
31
|
+
puts Panda::CMS::BulkEditor.export
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
task default: [:spec, :panda, :cms]
|
|
38
|
+
|
|
39
|
+
def run_tailwind(root_path:, input_path: nil, output_path: nil, config_path: nil, watch: false, minify: true)
|
|
40
|
+
config_path ||= root_path.join("config/tailwind.config.js")
|
|
41
|
+
|
|
42
|
+
command = [
|
|
43
|
+
Tailwindcss::Ruby.executable,
|
|
44
|
+
"-i #{root_path.join(input_path)}",
|
|
45
|
+
"-o #{root_path.join(output_path)}",
|
|
46
|
+
"-c #{root_path.join(config_path)}"
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
command << "-w" if watch
|
|
50
|
+
command << "-m" if minify
|
|
51
|
+
|
|
52
|
+
command = command.join(" ")
|
|
53
|
+
system command
|
|
54
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<%%= form_with(model: <%= model_resource_name %>, class: "contents") do |form| %>
|
|
2
|
+
<%% if <%= singular_table_name %>.errors.any? %>
|
|
3
|
+
<div id="error_explanation" class="py-2 px-3 mt-3 font-medium text-red-500 bg-red-50 rounded-lg">
|
|
4
|
+
<h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
|
|
5
|
+
|
|
6
|
+
<ul>
|
|
7
|
+
<%% <%= singular_table_name %>.errors.each do |error| %>
|
|
8
|
+
<li><%%= error.full_message %></li>
|
|
9
|
+
<%% end %>
|
|
10
|
+
</ul>
|
|
11
|
+
</div>
|
|
12
|
+
<%% end %>
|
|
13
|
+
|
|
14
|
+
<% attributes.each do |attribute| -%>
|
|
15
|
+
<div class="my-5">
|
|
16
|
+
<% if attribute.password_digest? -%>
|
|
17
|
+
<%%= form.label :password %>
|
|
18
|
+
<%%= form.password_field :password, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="my-5">
|
|
22
|
+
<%%= form.label :password_confirmation %>
|
|
23
|
+
<%%= form.password_field :password_confirmation, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
|
|
24
|
+
<% elsif attribute.attachments? -%>
|
|
25
|
+
<%%= form.label :<%= attribute.column_name %> %>
|
|
26
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
|
|
27
|
+
<% else -%>
|
|
28
|
+
<%%= form.label :<%= attribute.column_name %> %>
|
|
29
|
+
<% if attribute.field_type == :text_area -%>
|
|
30
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
|
|
31
|
+
<% elsif attribute.field_type == :check_box -%>
|
|
32
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "block mt-2 h-5 w-5" %>
|
|
33
|
+
<% else -%>
|
|
34
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
|
|
35
|
+
<% end -%>
|
|
36
|
+
<% end -%>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<% end -%>
|
|
40
|
+
<div class="inline">
|
|
41
|
+
<%%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
42
|
+
</div>
|
|
43
|
+
<%% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div class="mx-auto w-full md:w-2/3">
|
|
2
|
+
<h1 class="text-4xl font-bold">Editing <%= human_name.downcase %></h1>
|
|
3
|
+
|
|
4
|
+
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
|
|
5
|
+
|
|
6
|
+
<%%= link_to "Show this <%= human_name.downcase %>", @<%= singular_table_name %>, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
7
|
+
<%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper %>_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<div class="w-full">
|
|
2
|
+
<%% if notice.present? %>
|
|
3
|
+
<p class="inline-block py-2 px-3 mb-5 font-medium text-green-500 bg-green-50 rounded-lg" id="notice"><%%= notice %></p>
|
|
4
|
+
<%% end %>
|
|
5
|
+
|
|
6
|
+
<div class="flex justify-between items-center">
|
|
7
|
+
<h1 class="text-4xl font-bold"><%= human_name.pluralize %></h1>
|
|
8
|
+
<%%= link_to "New <%= human_name.downcase %>", new_<%= singular_route_name %>_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div id="<%= plural_table_name %>" class="min-w-full">
|
|
12
|
+
<%%= render @<%= plural_table_name %> %>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<div class="mx-auto w-full md:w-2/3">
|
|
2
|
+
<h1 class="text-4xl font-bold">New <%= human_name.downcase %></h1>
|
|
3
|
+
|
|
4
|
+
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
|
|
5
|
+
|
|
6
|
+
<%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper %>_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<div id="<%%= dom_id <%= singular_name %> %>">
|
|
2
|
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
|
|
3
|
+
<p class="my-5">
|
|
4
|
+
<strong class="block mb-1 font-medium"><%= attribute.human_name %>:</strong>
|
|
5
|
+
<% if attribute.attachment? -%>
|
|
6
|
+
<%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
|
|
7
|
+
<% elsif attribute.attachments? -%>
|
|
8
|
+
<%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
|
|
9
|
+
<div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
|
|
10
|
+
<%% end %>
|
|
11
|
+
<% else -%>
|
|
12
|
+
<%%= <%= singular_name %>.<%= attribute.column_name %> %>
|
|
13
|
+
<% end -%>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<% end -%>
|
|
17
|
+
<%% if action_name != "show" %>
|
|
18
|
+
<%%= link_to "Show this <%= human_name.downcase %>", <%= singular_name %>, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
19
|
+
<%%= link_to "Edit this <%= human_name.downcase %>", edit_<%= singular_name %>_path(<%= singular_name %>), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %>
|
|
20
|
+
<hr class="mt-6">
|
|
21
|
+
<%% end %>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<div class="flex mx-auto w-full md:w-2/3">
|
|
2
|
+
<div class="mx-auto">
|
|
3
|
+
<%% if notice.present? %>
|
|
4
|
+
<p class="inline-block py-2 px-3 mb-5 font-medium text-green-500 bg-green-50 rounded-lg" id="notice"><%%= notice %></p>
|
|
5
|
+
<%% end %>
|
|
6
|
+
|
|
7
|
+
<%%= render @<%= singular_table_name %> %>
|
|
8
|
+
|
|
9
|
+
<%%= link_to "Edit this <%= singular_table_name %>", edit_<%= singular_table_name %>_path(@<%= singular_table_name %>), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
10
|
+
<div class="inline-block ml-2">
|
|
11
|
+
<%%= button_to "Destroy this <%= singular_table_name %>", <%= singular_table_name %>_path(@<%= singular_table_name %>), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
|
|
12
|
+
</div>
|
|
13
|
+
<%%= link_to "Back to <%= plural_table_name %>", <%= index_helper %>_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|