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.
- checksums.yaml +4 -4
- data/app/components/panda/cms/admin/user_activity_component.html.erb +2 -2
- data/app/components/panda/cms/admin/user_activity_component.rb +2 -2
- data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
- data/app/components/panda/cms/admin/user_display_component.rb +2 -2
- data/app/components/panda/cms/rich_text_component.rb +5 -5
- data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +5 -4
- data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
- data/app/controllers/panda/cms/admin/forms_controller.rb +3 -4
- data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
- data/app/controllers/panda/cms/admin/pages_controller.rb +7 -7
- data/app/controllers/panda/cms/admin/posts_controller.rb +9 -10
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +4 -4
- data/app/controllers/panda/cms/admin/settings_controller.rb +2 -3
- data/app/controllers/panda/cms/application_controller.rb +13 -5
- data/app/controllers/panda/cms/pages_controller.rb +2 -2
- data/app/helpers/panda/cms/application_helper.rb +3 -3
- data/app/helpers/panda/cms/asset_helper.rb +14 -1
- data/app/javascript/panda/cms/application_panda_cms.js +2 -34
- data/app/javascript/panda/cms/controllers/index.js +5 -15
- data/app/models/panda/cms/block_content.rb +1 -1
- data/app/models/panda/cms/current.rb +3 -12
- data/app/models/panda/cms/post.rb +3 -3
- data/app/models/panda/cms/visit.rb +1 -1
- data/app/views/layouts/panda/cms/application.html.erb +1 -1
- data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
- data/app/views/panda/cms/admin/files/index.html.erb +1 -1
- data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
- data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
- data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
- data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
- data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
- data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
- data/app/views/panda/cms/admin/pages/new.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
- data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
- data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/index.html.erb +3 -3
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
- data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
- data/config/initializers/panda/cms.rb +6 -3
- data/config/routes.rb +8 -16
- data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +3 -1
- data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +2 -0
- data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
- data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -0
- data/lib/panda/cms/asset_loader.rb +4 -4
- data/lib/panda/cms/engine.rb +42 -98
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +50 -40
- data/lib/tasks/assets.rake +162 -122
- data/lib/tasks/panda/cms/migrations.rake +13 -0
- metadata +19 -36
- data/app/builders/panda/cms/form_builder.rb +0 -225
- data/app/components/panda/cms/admin/button_component.rb +0 -70
- data/app/components/panda/cms/admin/container_component.rb +0 -13
- data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
- data/app/components/panda/cms/admin/heading_component.rb +0 -46
- data/app/components/panda/cms/admin/panel_component.rb +0 -13
- data/app/components/panda/cms/admin/table_component.rb +0 -46
- data/app/components/panda/cms/admin/tag_component.rb +0 -35
- data/app/constraints/panda/cms/admin_constraint.rb +0 -21
- data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -44
- data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -92
- data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -25
- data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
- data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
- data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
- data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
- data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
- data/app/models/panda/cms/breadcrumb.rb +0 -14
- data/app/models/panda/cms/user.rb +0 -33
- data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -195
- data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
- data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
- data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -7
- data/lib/panda/cms/editor_js/blocks/alert.rb +0 -36
- data/lib/panda/cms/editor_js/blocks/base.rb +0 -35
- data/lib/panda/cms/editor_js/blocks/header.rb +0 -17
- data/lib/panda/cms/editor_js/blocks/image.rb +0 -39
- data/lib/panda/cms/editor_js/blocks/list.rb +0 -34
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -18
- data/lib/panda/cms/editor_js/blocks/quote.rb +0 -44
- data/lib/panda/cms/editor_js/blocks/table.rb +0 -52
- data/lib/panda/cms/editor_js/renderer.rb +0 -127
- data/lib/panda/cms/editor_js.rb +0 -18
- data/lib/panda/cms/editor_js_content.rb +0 -61
data/lib/panda/cms/engine.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "rubygems"
|
4
4
|
require "panda/core"
|
5
5
|
require "panda/core/engine"
|
6
|
+
require "panda/editor"
|
7
|
+
require "panda/editor/engine"
|
6
8
|
require "panda/cms/railtie"
|
7
9
|
|
8
10
|
require "invisible_captcha"
|
@@ -18,7 +20,7 @@ module Panda
|
|
18
20
|
]
|
19
21
|
|
20
22
|
# Basic session setup only
|
21
|
-
initializer "
|
23
|
+
initializer "panda.cms.session", before: :load_config_initializers do |app|
|
22
24
|
app.config.middleware = app.config.middleware.dup if app.config.middleware.frozen?
|
23
25
|
|
24
26
|
app.config.session_store :cookie_store, key: "_panda_cms_session"
|
@@ -50,7 +52,7 @@ module Panda
|
|
50
52
|
# Custom error handling
|
51
53
|
# config.exceptions_app = Panda::CMS::ExceptionsApp.new(exceptions_app: routes)
|
52
54
|
|
53
|
-
initializer "
|
55
|
+
initializer "panda.cms.assets" do |app|
|
54
56
|
if Rails.configuration.respond_to?(:assets)
|
55
57
|
# Add JavaScript paths
|
56
58
|
app.config.assets.paths << root.join("app/javascript")
|
@@ -68,7 +70,7 @@ module Panda
|
|
68
70
|
end
|
69
71
|
|
70
72
|
# Add importmap paths from the engine
|
71
|
-
initializer "
|
73
|
+
initializer "panda.cms.importmap", before: "importmap" do |app|
|
72
74
|
if app.config.respond_to?(:importmap)
|
73
75
|
# Create a new array if frozen
|
74
76
|
app.config.importmap.paths = app.config.importmap.paths.dup if app.config.importmap.paths.frozen?
|
@@ -124,111 +126,53 @@ module Panda
|
|
124
126
|
end
|
125
127
|
|
126
128
|
# Set up ViewComponent
|
127
|
-
initializer "
|
129
|
+
initializer "panda.cms.view_component" do |app|
|
128
130
|
app.config.view_component.preview_paths ||= []
|
129
131
|
app.config.view_component.preview_paths << root.join("spec/components/previews")
|
130
132
|
app.config.view_component.generate.sidecar = true
|
131
133
|
app.config.view_component.generate.preview = true
|
132
134
|
end
|
133
135
|
|
134
|
-
#
|
135
|
-
initializer "panda_cms.omniauth", before: "omniauth" do |app|
|
136
|
-
app.config.session_store :cookie_store, key: "_panda_cms_session"
|
137
|
-
app.config.middleware.use ActionDispatch::Cookies
|
138
|
-
app.config.middleware.use ActionDispatch::Session::CookieStore, app.config.session_options
|
139
|
-
|
140
|
-
OmniAuth.config.logger = Rails.logger
|
141
|
-
|
142
|
-
# TODO: Move this to somewhere more sensible?
|
143
|
-
# Define the mapping of our provider "names" to the OmniAuth strategies and configuration
|
144
|
-
auth_path = "#{Panda::CMS.route_namespace}/auth"
|
145
|
-
callback_path = "/callback"
|
146
|
-
available_providers = {
|
147
|
-
microsoft: {
|
148
|
-
strategy: :microsoft_graph,
|
149
|
-
defaults: {
|
150
|
-
name: "microsoft",
|
151
|
-
# Setup at the following URL:
|
152
|
-
# https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
|
153
|
-
client_id: Rails.application.credentials.dig(:microsoft, :client_id),
|
154
|
-
client_secret: Rails.application.credentials.dig(:microsoft, :client_secret),
|
155
|
-
# Don't change this or the sky will fall on your head
|
156
|
-
# https://github.com/synth/omniauth-microsoft_graph/tree/main?tab=readme-ov-file#domain-verification
|
157
|
-
skip_domain_verification: false,
|
158
|
-
# If your application is single-tenanted, replace "common" with your tenant (directory) ID
|
159
|
-
# from https://portal.azure.com/#settings/directory, otherwise you'll likely want to leave
|
160
|
-
# these settings unchanged
|
161
|
-
client_options: {
|
162
|
-
site: "https://login.microsoftonline.com/",
|
163
|
-
token_url: "common/oauth2/v2.0/token",
|
164
|
-
authorize_url: "common/oauth2/v2.0/authorize"
|
165
|
-
},
|
166
|
-
# If you assign specific users or groups, you will likely want to set this to
|
167
|
-
# true to enable auto-provisioning
|
168
|
-
create_account_on_first_login: false,
|
169
|
-
create_admin_account_on_first_login: false,
|
170
|
-
# Don't hide this provider from the login page
|
171
|
-
hidden: false
|
172
|
-
}
|
173
|
-
},
|
174
|
-
google: {
|
175
|
-
strategy: :google_oauth2,
|
176
|
-
defaults: {
|
177
|
-
name: "google",
|
178
|
-
# Setup at the following URL: https://console.developers.google.com/
|
179
|
-
client_id: Rails.application.credentials.dig(:google, :client_id),
|
180
|
-
client_secret: Rails.application.credentials.dig(:google, :client_secret),
|
181
|
-
# If you assign specific users or groups, you will likely want to set this to
|
182
|
-
# true to enable auto-provisioning
|
183
|
-
create_account_on_first_login: false,
|
184
|
-
create_admin_account_on_first_login: false,
|
185
|
-
# Options we need
|
186
|
-
scope: "email, profile",
|
187
|
-
image_aspect_ratio: "square",
|
188
|
-
image_size: 150,
|
189
|
-
# Worth setting select_account as default, as many people have multiple Google accounts now:
|
190
|
-
prompt: "select_account",
|
191
|
-
# You should probably also set the 'hd' option, huh?,
|
192
|
-
# Don't hide this provider from the login page
|
193
|
-
hidden: false
|
194
|
-
}
|
195
|
-
},
|
196
|
-
github: {
|
197
|
-
strategy: :github,
|
198
|
-
defaults: {
|
199
|
-
name: "github",
|
200
|
-
# Setup at the following URL: https://github.com/settings/applications/new
|
201
|
-
# with a callback of
|
202
|
-
# In the meantime, as long as you're set to /admin as your login path, and on
|
203
|
-
# http://localhost:3000, you can use these for a first login :)
|
204
|
-
client_id: Rails.application.credentials.dig(:github, :client_id),
|
205
|
-
client_secret: Rails.application.credentials.dig(:github, :client_secret),
|
206
|
-
scope: "user:email,read:user",
|
207
|
-
create_account_on_first_login: false,
|
208
|
-
create_admin_account_on_first_login: false,
|
209
|
-
# Don't hide this provider from the login page
|
210
|
-
hidden: false
|
211
|
-
}
|
212
|
-
}
|
213
|
-
}
|
136
|
+
# Authentication is now handled by Panda::Core::Engine
|
214
137
|
|
215
|
-
|
216
|
-
|
138
|
+
# Configure Core for CMS
|
139
|
+
initializer "panda.cms.configure_core" do |app|
|
140
|
+
Panda::Core.configure do |config|
|
141
|
+
# Customize login page
|
142
|
+
config.login_logo_path = "/panda-cms-assets/panda-nav.png"
|
143
|
+
config.login_page_title = "Panda CMS Admin"
|
217
144
|
|
218
|
-
|
219
|
-
|
145
|
+
# Set dashboard redirect path to CMS dashboard (using Core's admin_path)
|
146
|
+
config.dashboard_redirect_path = "#{Panda::Core.configuration.admin_path}/cms"
|
220
147
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
"#{Panda::
|
225
|
-
|
226
|
-
|
227
|
-
provider_config = options[:defaults].merge(Panda::CMS.config.authentication[provider])
|
148
|
+
# Customize initial breadcrumb
|
149
|
+
config.initial_admin_breadcrumb = ->(controller) {
|
150
|
+
# Use CMS dashboard path - just use the string path
|
151
|
+
["Admin", "#{Panda::Core.configuration.admin_path}/cms"]
|
152
|
+
}
|
228
153
|
|
229
|
-
|
230
|
-
|
231
|
-
|
154
|
+
# Dashboard widgets
|
155
|
+
config.admin_dashboard_widgets = ->(user) {
|
156
|
+
widgets = []
|
157
|
+
|
158
|
+
# Add CMS statistics widgets if CMS is available
|
159
|
+
if defined?(Panda::CMS)
|
160
|
+
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
161
|
+
metric: "Views Today",
|
162
|
+
value: Panda::CMS::Visit.group_by_day(:visited_at, last: 1).count.values.first || 0
|
163
|
+
)
|
164
|
+
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
165
|
+
metric: "Views Last Week",
|
166
|
+
value: Panda::CMS::Visit.group_by_week(:visited_at, last: 1).count.values.first || 0
|
167
|
+
)
|
168
|
+
widgets << Panda::CMS::Admin::StatisticsComponent.new(
|
169
|
+
metric: "Views Last Month",
|
170
|
+
value: Panda::CMS::Visit.group_by_month(:visited_at, last: 1).count.values.first || 0
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
widgets
|
175
|
+
}
|
232
176
|
end
|
233
177
|
end
|
234
178
|
|
data/lib/panda-cms/version.rb
CHANGED
data/lib/panda-cms.rb
CHANGED
@@ -7,44 +7,64 @@ require "view_component"
|
|
7
7
|
|
8
8
|
module Panda
|
9
9
|
module CMS
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
10
|
+
class Configuration
|
11
|
+
attr_accessor :title, :require_login_to_view, :authentication,
|
12
|
+
:posts, :url, :editor_js_tools,
|
13
|
+
:editor_js_tool_config, :instagram, :analytics
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@title = "Demo Site"
|
17
|
+
@require_login_to_view = false
|
18
|
+
@authentication = {}
|
19
|
+
@posts = {enabled: true, prefix: "blog"}
|
20
|
+
@url = nil
|
21
|
+
@editor_js_tools = []
|
22
|
+
@editor_js_tool_config = {}
|
23
|
+
@instagram = {
|
24
|
+
enabled: false,
|
25
|
+
username: nil,
|
26
|
+
access_token: nil
|
27
|
+
}
|
28
|
+
@analytics = {
|
29
|
+
google_analytics: {
|
30
|
+
enabled: false,
|
31
|
+
tracking_id: nil
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
attr_writer :configuration
|
39
|
+
|
40
|
+
def configuration
|
41
|
+
@configuration ||= Configuration.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def config
|
45
|
+
configuration
|
46
|
+
end
|
47
|
+
|
48
|
+
def configure
|
49
|
+
yield configuration if block_given?
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset_configuration!
|
53
|
+
@configuration = Configuration.new
|
54
|
+
end
|
55
|
+
end
|
34
56
|
|
35
57
|
def self.root_path
|
36
|
-
|
58
|
+
# Delegate to Panda::Core's admin_path configuration
|
59
|
+
Panda::Core.configuration.admin_path
|
37
60
|
end
|
38
61
|
|
39
62
|
class << self
|
40
63
|
attr_accessor :loader
|
41
64
|
|
42
65
|
def route_namespace
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def configure
|
47
|
-
yield config if block_given?
|
66
|
+
# Delegate to Panda::Core's admin_path configuration
|
67
|
+
Panda::Core.configuration.admin_path
|
48
68
|
end
|
49
69
|
end
|
50
70
|
end
|
@@ -75,16 +95,6 @@ require_relative "panda/cms/exceptions_app"
|
|
75
95
|
require_relative "panda/cms/engine"
|
76
96
|
require_relative "panda/cms/demo_site_generator"
|
77
97
|
require_relative "panda/cms/asset_loader"
|
78
|
-
require_relative "panda/cms/editor_js"
|
79
|
-
require_relative "panda/cms/editor_js_content"
|
80
|
-
require_relative "panda/cms/editor_js/renderer"
|
81
|
-
require_relative "panda/cms/editor_js/blocks/alert"
|
82
|
-
require_relative "panda/cms/editor_js/blocks/header"
|
83
|
-
require_relative "panda/cms/editor_js/blocks/image"
|
84
|
-
require_relative "panda/cms/editor_js/blocks/list"
|
85
|
-
require_relative "panda/cms/editor_js/blocks/paragraph"
|
86
|
-
require_relative "panda/cms/editor_js/blocks/quote"
|
87
|
-
require_relative "panda/cms/editor_js/blocks/table"
|
88
98
|
require_relative "panda/cms/slug"
|
89
99
|
|
90
100
|
Panda::CMS.loader.setup
|
data/lib/tasks/assets.rake
CHANGED
@@ -1,154 +1,156 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
namespace :
|
4
|
-
namespace :
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
3
|
+
namespace :panda do
|
4
|
+
namespace :cms do
|
5
|
+
namespace :assets do
|
6
|
+
desc "Compile Panda CMS assets for GitHub release distribution"
|
7
|
+
task :compile do
|
8
|
+
puts "🐼 Compiling Panda CMS assets..."
|
9
|
+
puts "Rails.root: #{Rails.root}"
|
10
|
+
puts "Working directory: #{Dir.pwd}"
|
11
|
+
|
12
|
+
# Create output directory
|
13
|
+
output_dir = Rails.root.join("tmp", "panda_cms_assets")
|
14
|
+
FileUtils.mkdir_p(output_dir)
|
15
|
+
|
16
|
+
version = Panda::CMS::VERSION
|
17
|
+
puts "Version: #{version}"
|
18
|
+
puts "Output directory: #{output_dir}"
|
19
|
+
|
20
|
+
# Compile JavaScript bundle
|
21
|
+
js_bundle = compile_javascript_bundle(version)
|
22
|
+
js_file = output_dir.join("panda-cms-#{version}.js")
|
23
|
+
File.write(js_file, js_bundle)
|
24
|
+
puts "✅ JavaScript compiled: #{js_file} (#{File.size(js_file)} bytes)"
|
25
|
+
|
26
|
+
# Compile CSS bundle (if any CSS files exist)
|
27
|
+
css_bundle = compile_css_bundle(version)
|
28
|
+
if css_bundle && !css_bundle.strip.empty?
|
29
|
+
css_file = output_dir.join("panda-cms-#{version}.css")
|
30
|
+
File.write(css_file, css_bundle)
|
31
|
+
puts "✅ CSS compiled: #{css_file} (#{File.size(css_file)} bytes)"
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
# Create manifest file
|
35
|
+
manifest = create_asset_manifest(version)
|
36
|
+
manifest_file = output_dir.join("manifest.json")
|
37
|
+
File.write(manifest_file, JSON.pretty_generate(manifest))
|
38
|
+
puts "✅ Manifest created: #{manifest_file}"
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
# Copy assets to test environment location for consistent testing
|
41
|
+
# Rails.root is the dummy app, so we need to go to its public directory
|
42
|
+
test_asset_dir = Rails.root.join("public", "panda-cms-assets")
|
43
|
+
FileUtils.mkdir_p(test_asset_dir)
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
js_file_name = "panda-cms-#{version}.js"
|
46
|
+
css_file_name = "panda-cms-#{version}.css"
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
# Copy JavaScript file
|
49
|
+
if File.exist?(output_dir.join(js_file_name))
|
50
|
+
FileUtils.cp(output_dir.join(js_file_name), test_asset_dir.join(js_file_name))
|
51
|
+
puts "✅ Copied JavaScript to test location: #{test_asset_dir.join(js_file_name)}"
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
# Copy CSS file
|
55
|
+
if File.exist?(output_dir.join(css_file_name))
|
56
|
+
FileUtils.cp(output_dir.join(css_file_name), test_asset_dir.join(css_file_name))
|
57
|
+
puts "✅ Copied CSS to test location: #{test_asset_dir.join(css_file_name)}"
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
# Copy manifest
|
61
|
+
if File.exist?(output_dir.join("manifest.json"))
|
62
|
+
FileUtils.cp(output_dir.join("manifest.json"), test_asset_dir.join("manifest.json"))
|
63
|
+
puts "✅ Copied manifest to test location: #{test_asset_dir.join("manifest.json")}"
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
puts "🎉 Asset compilation complete!"
|
67
|
+
puts "📁 Output directory: #{output_dir}"
|
68
|
+
puts "📁 Test assets directory: #{test_asset_dir}"
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
desc "Upload compiled assets to GitHub release"
|
72
|
+
task upload: :compile do
|
73
|
+
version = Panda::CMS::VERSION
|
74
|
+
output_dir = Rails.root.join("tmp", "panda_cms_assets")
|
74
75
|
|
75
|
-
|
76
|
+
puts "📤 Uploading assets to GitHub release v#{version}..."
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
# Check if gh CLI is available
|
79
|
+
unless system("gh --version > /dev/null 2>&1")
|
80
|
+
puts "❌ GitHub CLI (gh) not found. Please install: https://cli.github.com/"
|
81
|
+
exit 1
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
# Check if release exists
|
85
|
+
unless system("gh release view v#{version} > /dev/null 2>&1")
|
86
|
+
puts "❌ Release v#{version} not found. Create it first with: gh release create v#{version}"
|
87
|
+
exit 1
|
88
|
+
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
# Upload each asset file
|
91
|
+
Dir.glob(output_dir.join("*")).each do |file|
|
92
|
+
filename = File.basename(file)
|
93
|
+
puts "Uploading #{filename}..."
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
if system("gh release upload v#{version} #{file} --clobber")
|
96
|
+
puts "✅ Uploaded: #{filename}"
|
97
|
+
else
|
98
|
+
puts "❌ Failed to upload: #{filename}"
|
99
|
+
exit 1
|
100
|
+
end
|
99
101
|
end
|
100
|
-
end
|
101
102
|
|
102
|
-
|
103
|
-
|
103
|
+
puts "🎉 All assets uploaded successfully!"
|
104
|
+
end
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
desc "Download assets from GitHub release for local development"
|
107
|
+
task :download do
|
108
|
+
version = Panda::CMS::VERSION
|
109
|
+
output_dir = Rails.root.join("public", "panda-cms-assets", version)
|
110
|
+
FileUtils.mkdir_p(output_dir)
|
110
111
|
|
111
|
-
|
112
|
+
puts "📥 Downloading assets from GitHub release v#{version}..."
|
112
113
|
|
113
|
-
|
114
|
-
|
114
|
+
# Download manifest first to know what files to get
|
115
|
+
manifest_url = "https://github.com/pandacms/panda-cms/releases/download/v#{version}/manifest.json"
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
begin
|
118
|
+
require "net/http"
|
119
|
+
require "uri"
|
119
120
|
|
120
|
-
|
121
|
-
|
121
|
+
uri = URI(manifest_url)
|
122
|
+
response = Net::HTTP.get_response(uri)
|
122
123
|
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
if response.code == "200"
|
125
|
+
manifest = JSON.parse(response.body)
|
126
|
+
puts "✅ Downloaded manifest"
|
126
127
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
128
|
+
# Download each file listed in manifest
|
129
|
+
manifest["files"].each do |file_info|
|
130
|
+
filename = file_info["filename"]
|
131
|
+
file_url = "https://github.com/pandacms/panda-cms/releases/download/v#{version}/#{filename}"
|
131
132
|
|
132
|
-
|
133
|
-
|
134
|
-
|
133
|
+
puts "Downloading #{filename}..."
|
134
|
+
file_uri = URI(file_url)
|
135
|
+
file_response = Net::HTTP.get_response(file_uri)
|
135
136
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
if file_response.code == "200"
|
138
|
+
File.write(output_dir.join(filename), file_response.body)
|
139
|
+
puts "✅ Downloaded: #{filename}"
|
140
|
+
else
|
141
|
+
puts "❌ Failed to download: #{filename}"
|
142
|
+
end
|
141
143
|
end
|
142
|
-
end
|
143
144
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
145
|
+
puts "🎉 Assets downloaded to: #{output_dir}"
|
146
|
+
else
|
147
|
+
puts "❌ Failed to download manifest from: #{manifest_url}"
|
148
|
+
puts "Response: #{response.code} #{response.message}"
|
149
|
+
end
|
150
|
+
rescue => e
|
151
|
+
puts "❌ Error downloading assets: #{e.message}"
|
152
|
+
puts "Falling back to local development mode..."
|
148
153
|
end
|
149
|
-
rescue => e
|
150
|
-
puts "❌ Error downloading assets: #{e.message}"
|
151
|
-
puts "Falling back to local development mode..."
|
152
154
|
end
|
153
155
|
end
|
154
156
|
end
|
@@ -307,10 +309,48 @@ def create_tailwind_components
|
|
307
309
|
" }",
|
308
310
|
"};",
|
309
311
|
"",
|
312
|
+
"const Toggle = {",
|
313
|
+
" static: {",
|
314
|
+
" values: { open: { type: Boolean, default: false } },",
|
315
|
+
" targets: ['toggleable']",
|
316
|
+
" },",
|
317
|
+
" connect() {",
|
318
|
+
" console.log('[Panda CMS] Toggle controller connected');",
|
319
|
+
" this.openValue = false;",
|
320
|
+
" // Find toggleable elements",
|
321
|
+
" this.toggleableTargets = Array.from(this.element.querySelectorAll('[data-toggle-target=\"toggleable\"]'));",
|
322
|
+
" if (this.toggleableTargets.length === 0) {",
|
323
|
+
" // For slideover, the toggleable element might be a sibling",
|
324
|
+
" const slideover = document.querySelector('#slideover');",
|
325
|
+
" if (slideover) {",
|
326
|
+
" this.toggleableTargets = [slideover];",
|
327
|
+
" }",
|
328
|
+
" }",
|
329
|
+
" },",
|
330
|
+
" toggle(event) {",
|
331
|
+
" event.preventDefault();",
|
332
|
+
" console.log('[Panda CMS] Toggle action triggered');",
|
333
|
+
" this.openValue = !this.openValue;",
|
334
|
+
" this.animate();",
|
335
|
+
" },",
|
336
|
+
" animate() {",
|
337
|
+
" this.toggleableTargets.forEach(element => {",
|
338
|
+
" if (this.openValue) {",
|
339
|
+
" element.classList.remove('hidden');",
|
340
|
+
" element.style.display = 'block';",
|
341
|
+
" } else {",
|
342
|
+
" element.classList.add('hidden');",
|
343
|
+
" element.style.display = 'none';",
|
344
|
+
" }",
|
345
|
+
" });",
|
346
|
+
" }",
|
347
|
+
"};",
|
348
|
+
"",
|
310
349
|
"// Register TailwindCSS components",
|
311
350
|
"Stimulus.register('alert', Alert);",
|
312
351
|
"Stimulus.register('dropdown', Dropdown);",
|
313
352
|
"Stimulus.register('modal', Modal);",
|
353
|
+
"Stimulus.register('toggle', Toggle);",
|
314
354
|
""
|
315
355
|
].join("\n")
|
316
356
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :panda do
|
4
|
+
namespace :cms do
|
5
|
+
namespace :install do
|
6
|
+
desc "Copy migrations from panda_cms to application"
|
7
|
+
task :migrations do
|
8
|
+
# Delegate to the auto-generated task
|
9
|
+
Rake::Task["panda_cms:install:migrations"].invoke
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|