lean_cms 0.2.12
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/CHANGELOG.md +235 -0
- data/LICENSE +21 -0
- data/README.md +107 -0
- data/app/assets/images/lean_cms/sloth-404.png +0 -0
- data/app/assets/images/lean_cms/sloth-500.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-16.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-32.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-64.png +0 -0
- data/app/assets/images/lean_cms/sloth-logo.png +0 -0
- data/app/assets/lean_cms/actiontext.css +440 -0
- data/app/assets/lean_cms/cms_edit_controls.css +548 -0
- data/app/assets/tailwind/lean_cms/engine.css +14 -0
- data/app/components/lean_cms/base_component.rb +61 -0
- data/app/components/lean_cms/bullets_section_component.html.erb +23 -0
- data/app/components/lean_cms/bullets_section_component.rb +54 -0
- data/app/components/lean_cms/cards_section_component.html.erb +237 -0
- data/app/components/lean_cms/cards_section_component.rb +71 -0
- data/app/components/lean_cms/editable_content_component.html.erb +15 -0
- data/app/components/lean_cms/editable_content_component.rb +53 -0
- data/app/components/lean_cms/section_component.html.erb +18 -0
- data/app/components/lean_cms/section_component.rb +35 -0
- data/app/controllers/concerns/lean_cms/authentication.rb +60 -0
- data/app/controllers/concerns/lean_cms/authorization.rb +60 -0
- data/app/controllers/lean_cms/activity_controller.rb +16 -0
- data/app/controllers/lean_cms/application_controller.rb +48 -0
- data/app/controllers/lean_cms/dashboard_controller.rb +13 -0
- data/app/controllers/lean_cms/form_submissions_controller.rb +37 -0
- data/app/controllers/lean_cms/notification_settings_controller.rb +145 -0
- data/app/controllers/lean_cms/notifications_controller.rb +26 -0
- data/app/controllers/lean_cms/page_contents_controller.rb +403 -0
- data/app/controllers/lean_cms/password_setup_controller.rb +65 -0
- data/app/controllers/lean_cms/passwords_controller.rb +42 -0
- data/app/controllers/lean_cms/posts_controller.rb +78 -0
- data/app/controllers/lean_cms/sessions_controller.rb +50 -0
- data/app/controllers/lean_cms/settings_controller.rb +124 -0
- data/app/controllers/lean_cms/users_controller.rb +113 -0
- data/app/helpers/lean_cms/activity_helper.rb +190 -0
- data/app/helpers/lean_cms/application_helper.rb +43 -0
- data/app/helpers/lean_cms/content_helper.rb +34 -0
- data/app/helpers/lean_cms/page_content_helper.rb +359 -0
- data/app/javascript/controllers/cards_editor_controller.js +317 -0
- data/app/javascript/controllers/cms_sticky_overlay_controller.js +59 -0
- data/app/javascript/controllers/field_editor_form_controller.js +68 -0
- data/app/javascript/controllers/field_editor_modal_controller.js +79 -0
- data/app/javascript/controllers/inline_edit_controller.js +414 -0
- data/app/javascript/controllers/inline_edit_toggle_controller.js +81 -0
- data/app/javascript/controllers/notifications_controller.js +19 -0
- data/app/javascript/controllers/settings_inline_edit_sync_controller.js +38 -0
- data/app/javascript/controllers/settings_override_controller.js +45 -0
- data/app/mailers/lean_cms/application_mailer.rb +6 -0
- data/app/mailers/lean_cms/passwords_mailer.rb +8 -0
- data/app/mailers/lean_cms/users_mailer.rb +39 -0
- data/app/models/lean_cms/current.rb +6 -0
- data/app/models/lean_cms/form_submission.rb +45 -0
- data/app/models/lean_cms/magic_link.rb +76 -0
- data/app/models/lean_cms/meta_tag.rb +30 -0
- data/app/models/lean_cms/notification_setting.rb +69 -0
- data/app/models/lean_cms/page.rb +23 -0
- data/app/models/lean_cms/page_content.rb +245 -0
- data/app/models/lean_cms/post.rb +65 -0
- data/app/models/lean_cms/session.rb +7 -0
- data/app/models/lean_cms/setting.rb +156 -0
- data/app/policies/lean_cms/application_policy.rb +35 -0
- data/app/policies/lean_cms/page_content_policy.rb +31 -0
- data/app/policies/lean_cms/post_policy.rb +37 -0
- data/app/policies/lean_cms/setting_policy.rb +17 -0
- data/app/views/layouts/lean_cms/application.html.erb +114 -0
- data/app/views/layouts/lean_cms/auth.html.erb +200 -0
- data/app/views/lean_cms/activity/index.html.erb +79 -0
- data/app/views/lean_cms/dashboard/index.html.erb +180 -0
- data/app/views/lean_cms/form_submissions/index.html.erb +104 -0
- data/app/views/lean_cms/form_submissions/show.html.erb +157 -0
- data/app/views/lean_cms/notification_settings/edit.html.erb +192 -0
- data/app/views/lean_cms/notifications/index.html.erb +72 -0
- data/app/views/lean_cms/notifications/show.html.erb +39 -0
- data/app/views/lean_cms/page_contents/_field_editor.html.erb +174 -0
- data/app/views/lean_cms/page_contents/edit.html.erb +428 -0
- data/app/views/lean_cms/page_contents/index.html.erb +113 -0
- data/app/views/lean_cms/password_setup/show.html.erb +35 -0
- data/app/views/lean_cms/passwords/edit.html.erb +26 -0
- data/app/views/lean_cms/passwords/new.html.erb +21 -0
- data/app/views/lean_cms/passwords_mailer/reset.html.erb +6 -0
- data/app/views/lean_cms/passwords_mailer/reset.text.erb +4 -0
- data/app/views/lean_cms/posts/_form.html.erb +118 -0
- data/app/views/lean_cms/posts/edit.html.erb +31 -0
- data/app/views/lean_cms/posts/index.html.erb +100 -0
- data/app/views/lean_cms/posts/new.html.erb +16 -0
- data/app/views/lean_cms/sessions/new.html.erb +28 -0
- data/app/views/lean_cms/settings/edit.html.erb +384 -0
- data/app/views/lean_cms/shared/_admin_bar.html.erb +85 -0
- data/app/views/lean_cms/shared/_header.html.erb +86 -0
- data/app/views/lean_cms/shared/_notifications_bell.html.erb +84 -0
- data/app/views/lean_cms/shared/_sidebar.html.erb +102 -0
- data/app/views/lean_cms/users/_form.html.erb +105 -0
- data/app/views/lean_cms/users/edit.html.erb +8 -0
- data/app/views/lean_cms/users/index.html.erb +99 -0
- data/app/views/lean_cms/users/new.html.erb +8 -0
- data/app/views/lean_cms/users_mailer/admin_triggered_password_reset.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/admin_triggered_password_reset.text.erb +11 -0
- data/app/views/lean_cms/users_mailer/invitation.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/invitation.text.erb +11 -0
- data/app/views/lean_cms/users_mailer/reactivation.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/reactivation.text.erb +11 -0
- data/config/importmap.rb +8 -0
- data/config/routes.rb +78 -0
- data/db/migrate/20251112034030_create_lean_cms_tables.rb +131 -0
- data/db/migrate/20260513000001_create_lean_cms_auth_tables.rb +31 -0
- data/db/migrate/20260514000001_create_paper_trail_versions.rb +16 -0
- data/db/migrate/20260514000002_create_action_text_tables.rb +18 -0
- data/db/migrate/20260514000003_create_active_storage_tables.rb +45 -0
- data/db/migrate/20260514000004_create_noticed_tables.rb +27 -0
- data/lib/generators/lean_cms/demo/demo_generator.rb +54 -0
- data/lib/generators/lean_cms/demo/templates/lean_cms_structure.yml +129 -0
- data/lib/generators/lean_cms/demo/templates/pages_controller.rb +30 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/about.html.erb +40 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/contact.html.erb +55 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/home.html.erb +31 -0
- data/lib/generators/lean_cms/install/install_generator.rb +317 -0
- data/lib/generators/lean_cms/install/templates/add_lean_cms_columns_to_users.rb.tt +7 -0
- data/lib/generators/lean_cms/install/templates/lean_cms.rb +11 -0
- data/lib/generators/lean_cms/install/templates/lean_cms_structure.yml +29 -0
- data/lib/lean_cms/configuration.rb +32 -0
- data/lib/lean_cms/engine.rb +93 -0
- data/lib/lean_cms/loader.rb +217 -0
- data/lib/lean_cms/sync_helper.rb +182 -0
- data/lib/lean_cms/version.rb +3 -0
- data/lib/lean_cms.rb +26 -0
- data/lib/tasks/lean_cms.rake +390 -0
- metadata +313 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/migration"
|
|
3
|
+
|
|
4
|
+
module LeanCms
|
|
5
|
+
module Generators
|
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
|
7
|
+
include Rails::Generators::Migration
|
|
8
|
+
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
desc "Installs Lean CMS into the host Rails application"
|
|
12
|
+
|
|
13
|
+
class_option :user_class, type: :string, default: "User",
|
|
14
|
+
desc: "Name of the user model class (default: User). " \
|
|
15
|
+
"Use --user=Admin or similar if your auth gem (Devise, etc.) uses a different name."
|
|
16
|
+
|
|
17
|
+
def self.next_migration_number(dirname)
|
|
18
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
19
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Stop before any side effects if the host doesn't have a user model
|
|
23
|
+
# with a matching table yet, or the existing table is missing the base
|
|
24
|
+
# columns (email_address, password_digest) Lean CMS hard-codes against.
|
|
25
|
+
def check_user_model
|
|
26
|
+
user_class = options[:user_class]
|
|
27
|
+
table_name = user_class.tableize
|
|
28
|
+
|
|
29
|
+
unless ActiveRecord::Base.connection.table_exists?(table_name)
|
|
30
|
+
say "\n#{"=" * 64}", :red
|
|
31
|
+
say "Lean CMS install can't continue.", :red
|
|
32
|
+
say "=" * 64, :red
|
|
33
|
+
say ""
|
|
34
|
+
say "No `#{table_name}` table found in your database."
|
|
35
|
+
say ""
|
|
36
|
+
say "Lean CMS expects an existing #{user_class} model. Set one up first:"
|
|
37
|
+
say ""
|
|
38
|
+
say " - Rails 8 built-in auth: bin/rails generate authentication"
|
|
39
|
+
say " - Devise / Clearance / etc.: install per that gem's instructions"
|
|
40
|
+
say " - Custom model: bin/rails generate model #{user_class} ...", :yellow
|
|
41
|
+
say ""
|
|
42
|
+
say "Then run bin/rails db:migrate and re-run this generator."
|
|
43
|
+
say ""
|
|
44
|
+
say "If your user model isn't named #{user_class}, pass --user=ClassName:"
|
|
45
|
+
say " bin/rails generate lean_cms:install --user=Admin"
|
|
46
|
+
say ""
|
|
47
|
+
exit 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
existing = ActiveRecord::Base.connection.columns(table_name).map(&:name)
|
|
51
|
+
required_base = %w[email_address password_digest]
|
|
52
|
+
missing_base = required_base - existing
|
|
53
|
+
|
|
54
|
+
return if missing_base.empty?
|
|
55
|
+
|
|
56
|
+
say "\n#{"=" * 64}", :red
|
|
57
|
+
say "Lean CMS install can't continue.", :red
|
|
58
|
+
say "=" * 64, :red
|
|
59
|
+
say ""
|
|
60
|
+
say "The `#{table_name}` table is missing required columns: #{missing_base.join(", ")}."
|
|
61
|
+
say ""
|
|
62
|
+
say "Lean CMS authenticates with `#{user_class}.authenticate_by(email_address:, password:)`,"
|
|
63
|
+
say "so both `email_address` and `password_digest` columns are required."
|
|
64
|
+
say ""
|
|
65
|
+
say "Add them yourself (rename `email` -> `email_address` if you have it, etc.) and re-run."
|
|
66
|
+
say ""
|
|
67
|
+
exit 1
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Detect Rails 8's built-in auth (`bin/rails generate authentication`)
|
|
71
|
+
# and warn — it conflicts with Lean CMS's Authentication concern.
|
|
72
|
+
# Both define `before_action :require_authentication`, and the
|
|
73
|
+
# last-included wins. Without manual cleanup, /lean-cms protected
|
|
74
|
+
# routes redirect to /session/new (Rails 8's) instead of /lean-cms/login.
|
|
75
|
+
def check_for_rails_auth_conflict
|
|
76
|
+
ac_path = File.join(destination_root, "app", "controllers", "application_controller.rb")
|
|
77
|
+
return unless File.exist?(ac_path)
|
|
78
|
+
return unless File.read(ac_path).match?(/^\s*include\s+Authentication\s*$/)
|
|
79
|
+
|
|
80
|
+
say "\n#{"=" * 64}", :yellow
|
|
81
|
+
say "WARNING: Rails 8 authentication detected.", :yellow
|
|
82
|
+
say "=" * 64, :yellow
|
|
83
|
+
say ""
|
|
84
|
+
say "app/controllers/application_controller.rb already includes Rails 8's"
|
|
85
|
+
say "built-in Authentication concern. That conflicts with Lean CMS auth:"
|
|
86
|
+
say "both define `before_action :require_authentication`, and the"
|
|
87
|
+
say "last-included one wins. As-is, /lean-cms admin routes will redirect"
|
|
88
|
+
say "to /session/new (Rails 8's login) instead of /lean-cms/login."
|
|
89
|
+
say ""
|
|
90
|
+
say "After this install completes, clean up Rails 8 auth so Lean CMS owns auth:"
|
|
91
|
+
say ""
|
|
92
|
+
say " 1. In app/controllers/application_controller.rb:"
|
|
93
|
+
say " Remove: include Authentication", :red
|
|
94
|
+
say " Keep: include LeanCms::Authentication", :green
|
|
95
|
+
say ""
|
|
96
|
+
say " 2. Delete the Rails-8-generated auth files (Lean CMS replaces them):"
|
|
97
|
+
say " app/controllers/sessions_controller.rb", :cyan
|
|
98
|
+
say " app/controllers/passwords_controller.rb", :cyan
|
|
99
|
+
say " app/controllers/concerns/authentication.rb", :cyan
|
|
100
|
+
say " app/models/session.rb", :cyan
|
|
101
|
+
say " app/models/current.rb", :cyan
|
|
102
|
+
say " app/views/sessions/", :cyan
|
|
103
|
+
say " app/views/passwords/", :cyan
|
|
104
|
+
say " app/views/passwords_mailer/", :cyan
|
|
105
|
+
say " app/mailers/passwords_mailer.rb", :cyan
|
|
106
|
+
say " test/controllers/sessions_controller_test.rb", :cyan
|
|
107
|
+
say " test/controllers/passwords_controller_test.rb", :cyan
|
|
108
|
+
say ""
|
|
109
|
+
say " 3. Remove the Rails 8 auth routes from config/routes.rb:"
|
|
110
|
+
say " resource :session", :red
|
|
111
|
+
say " resources :passwords, param: :token", :red
|
|
112
|
+
say ""
|
|
113
|
+
say "Lean CMS provides all of this functionality under /lean-cms/."
|
|
114
|
+
say "Continuing the install — you'll see this WARNING again at the end."
|
|
115
|
+
say "#{"=" * 64}", :yellow
|
|
116
|
+
say ""
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Generate a migration that adds the Lean CMS-specific columns the host
|
|
120
|
+
# user table doesn't already have (name, active, permission flags, …).
|
|
121
|
+
# Silently skips if everything's already in place. The migration runs
|
|
122
|
+
# as part of `db:migrate` in `run_migrations` below.
|
|
123
|
+
def add_missing_user_columns
|
|
124
|
+
user_class = options[:user_class]
|
|
125
|
+
@user_table = user_class.tableize
|
|
126
|
+
existing = ActiveRecord::Base.connection.columns(@user_table).map(&:name)
|
|
127
|
+
|
|
128
|
+
required = [
|
|
129
|
+
[:name, :string, {}],
|
|
130
|
+
[:active, :boolean, { default: true, null: false }],
|
|
131
|
+
[:must_change_password, :boolean, { default: false, null: false }],
|
|
132
|
+
[:last_login_at, :datetime, {}],
|
|
133
|
+
[:is_super_admin, :boolean, { default: false, null: false }],
|
|
134
|
+
[:can_edit_pages, :boolean, { default: false, null: false }],
|
|
135
|
+
[:can_edit_blog, :boolean, { default: false, null: false }],
|
|
136
|
+
[:can_manage_users, :boolean, { default: false, null: false }],
|
|
137
|
+
[:can_access_settings, :boolean, { default: false, null: false }]
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
@missing_columns = required.reject { |name, _, _| existing.include?(name.to_s) }
|
|
141
|
+
return if @missing_columns.empty?
|
|
142
|
+
|
|
143
|
+
say "Generating migration to add Lean CMS columns to `#{@user_table}` " \
|
|
144
|
+
"(#{@missing_columns.map(&:first).join(", ")})...", :yellow
|
|
145
|
+
migration_template "add_lean_cms_columns_to_users.rb.tt",
|
|
146
|
+
"db/migrate/add_lean_cms_columns_to_#{@user_table}.rb"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def copy_initializer
|
|
150
|
+
@user_class = options[:user_class]
|
|
151
|
+
template "lean_cms.rb", "config/initializers/lean_cms.rb"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Inject the engine's Tailwind sources into the host's Tailwind input file
|
|
155
|
+
# so utilities referenced from gem views/controllers actually get emitted.
|
|
156
|
+
# tailwindcss-rails' `tailwindcss:engines` task generates
|
|
157
|
+
# app/assets/builds/tailwind/lean_cms.css (a thin wrapper that @imports the
|
|
158
|
+
# gem's engine.css with absolute paths), but the host has to opt in by
|
|
159
|
+
# @import-ing that bundle file from its own application.css.
|
|
160
|
+
def wire_tailwind
|
|
161
|
+
tailwind_input = File.join(destination_root, "app/assets/tailwind/application.css")
|
|
162
|
+
unless File.exist?(tailwind_input)
|
|
163
|
+
say "Skipping Tailwind wire-up — no app/assets/tailwind/application.css found.", :yellow
|
|
164
|
+
say " If you use Tailwind, add this line to your Tailwind input file:", :yellow
|
|
165
|
+
say " @import \"../builds/tailwind/lean_cms.css\";", :cyan
|
|
166
|
+
return
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
contents = File.read(tailwind_input)
|
|
170
|
+
if contents.include?("builds/tailwind/lean_cms.css")
|
|
171
|
+
say "Tailwind input already imports lean_cms engine — skipping.", :cyan
|
|
172
|
+
return
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
say "Adding lean_cms engine @import to app/assets/tailwind/application.css", :green
|
|
176
|
+
append_to_file tailwind_input, <<~CSS
|
|
177
|
+
|
|
178
|
+
/* Lean CMS engine Tailwind sources (auto-generated by tailwindcss:engines). */
|
|
179
|
+
@import "../builds/tailwind/lean_cms.css";
|
|
180
|
+
CSS
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Add `import "trix"` + `import "@rails/actiontext"` to the host's
|
|
184
|
+
# application.js so the field-editor modal's Trix editor loads for
|
|
185
|
+
# rich_text fields. The gem's own importmap.rb already pins them;
|
|
186
|
+
# this step is just the import line in the host entry point.
|
|
187
|
+
def wire_actiontext_imports
|
|
188
|
+
app_js = File.join(destination_root, "app/javascript/application.js")
|
|
189
|
+
unless File.exist?(app_js)
|
|
190
|
+
say "Skipping Action Text JS wire-up — no app/javascript/application.js found.", :yellow
|
|
191
|
+
say " If you use importmap-rails, add these lines to your application.js:", :yellow
|
|
192
|
+
say " import \"trix\"", :cyan
|
|
193
|
+
say " import \"@rails/actiontext\"", :cyan
|
|
194
|
+
return
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
contents = File.read(app_js)
|
|
198
|
+
if contents.include?('import "trix"') || contents.include?("import 'trix'")
|
|
199
|
+
say "application.js already imports trix — skipping.", :cyan
|
|
200
|
+
return
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
say "Adding trix + @rails/actiontext imports to app/javascript/application.js", :green
|
|
204
|
+
append_to_file app_js, <<~JS
|
|
205
|
+
|
|
206
|
+
// Lean CMS uses Trix in the field-editor modal for rich_text fields.
|
|
207
|
+
import "trix"
|
|
208
|
+
import "@rails/actiontext"
|
|
209
|
+
JS
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def run_migrations
|
|
213
|
+
rake "db:migrate"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def create_structure_file
|
|
217
|
+
target = File.join(destination_root, "config", "lean_cms_structure.yml")
|
|
218
|
+
return if File.exist?(target)
|
|
219
|
+
template "lean_cms_structure.yml", "config/lean_cms_structure.yml"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def print_instructions
|
|
223
|
+
say "\n#{"=" * 64}", :green
|
|
224
|
+
say "Lean CMS installed!", :green
|
|
225
|
+
say "=" * 64, :green
|
|
226
|
+
say ""
|
|
227
|
+
say "1. Configure your site", :yellow
|
|
228
|
+
say " Edit config/initializers/lean_cms.rb — site name, logo, colors,"
|
|
229
|
+
say " admin path, mailer_from."
|
|
230
|
+
say ""
|
|
231
|
+
say "2. Wire up your User model", :yellow
|
|
232
|
+
say " app/models/user.rb needs:"
|
|
233
|
+
say ""
|
|
234
|
+
say " class User < ApplicationRecord"
|
|
235
|
+
say " has_secure_password"
|
|
236
|
+
say ""
|
|
237
|
+
say " # Permission predicates that fall back to is_super_admin?:"
|
|
238
|
+
say " def can_edit_pages?; is_super_admin? || can_edit_pages; end"
|
|
239
|
+
say " def can_edit_blog?; is_super_admin? || can_edit_blog; end"
|
|
240
|
+
say " def can_manage_users?; is_super_admin? || can_manage_users; end"
|
|
241
|
+
say " def can_access_settings?; is_super_admin? || can_access_settings; end"
|
|
242
|
+
say " def has_any_cms_permission?"
|
|
243
|
+
say " can_edit_pages? || can_edit_blog? || can_manage_users? || can_access_settings?"
|
|
244
|
+
say " end"
|
|
245
|
+
say " def record_login!; update_column(:last_login_at, Time.current); end"
|
|
246
|
+
say " def active?; active; end"
|
|
247
|
+
say " def must_change_password?; must_change_password; end"
|
|
248
|
+
say ""
|
|
249
|
+
say " # Optional, but the admin nicely uses them when present:"
|
|
250
|
+
say " has_many :notifications, as: :recipient, dependent: :destroy,"
|
|
251
|
+
say " class_name: \"Noticed::Notification\""
|
|
252
|
+
say " def display_name; name.presence || email_address.split(\"@\").first; end"
|
|
253
|
+
say " def permissions_summary"
|
|
254
|
+
say " return \"Super Admin\" if is_super_admin?"
|
|
255
|
+
say " perms = []"
|
|
256
|
+
say " perms << \"Pages\" if can_edit_pages"
|
|
257
|
+
say " perms << \"Blog\" if can_edit_blog"
|
|
258
|
+
say " perms << \"Users\" if can_manage_users"
|
|
259
|
+
say " perms << \"Settings\" if can_access_settings"
|
|
260
|
+
say " perms.empty? ? \"No permissions\" : perms.join(\", \")"
|
|
261
|
+
say " end"
|
|
262
|
+
say " end"
|
|
263
|
+
say ""
|
|
264
|
+
say " Lean CMS uses LeanCms::Session and LeanCms::MagicLink directly — it does"
|
|
265
|
+
say " NOT require has_many :sessions or :magic_links on your User. That keeps"
|
|
266
|
+
say " it compatible with Rails 8's built-in auth and other auth gems."
|
|
267
|
+
say ""
|
|
268
|
+
say " Required columns on the users table: email_address (string, indexed unique),"
|
|
269
|
+
say " password_digest, name, active (boolean), must_change_password (boolean),"
|
|
270
|
+
say " last_login_at (datetime), and permission flags is_super_admin, can_edit_pages,"
|
|
271
|
+
say " can_edit_blog, can_manage_users, can_access_settings (all booleans)."
|
|
272
|
+
say ""
|
|
273
|
+
say "3. Include Lean CMS in ApplicationController", :yellow
|
|
274
|
+
say " class ApplicationController < ActionController::Base"
|
|
275
|
+
say " include LeanCms::Authentication"
|
|
276
|
+
say " end"
|
|
277
|
+
say ""
|
|
278
|
+
say " (Pundit is included automatically by the gem's admin controllers."
|
|
279
|
+
say " Add `include Pundit::Authorization` to your own ApplicationController"
|
|
280
|
+
say " only if you want `authorize` / `policy_scope` available in your"
|
|
281
|
+
say " non-CMS controllers too.)"
|
|
282
|
+
say ""
|
|
283
|
+
say "4. Include the helper in ApplicationHelper", :yellow
|
|
284
|
+
say " module ApplicationHelper"
|
|
285
|
+
say " include LeanCms::PageContentHelper"
|
|
286
|
+
say " end"
|
|
287
|
+
say ""
|
|
288
|
+
say "5. Add the admin bar to your public layout (optional but recommended)", :yellow
|
|
289
|
+
say " In app/views/layouts/application.html.erb:"
|
|
290
|
+
say ""
|
|
291
|
+
say " <body class=\"<%= 'pt-10' if current_user&.has_any_cms_permission? %>\">"
|
|
292
|
+
say " <%= cms_admin_bar %>"
|
|
293
|
+
say " <!-- your header / content -->"
|
|
294
|
+
say " </body>"
|
|
295
|
+
say ""
|
|
296
|
+
say " Gives signed-in editors a fixed strip with Inline Editing toggle,"
|
|
297
|
+
say " Help, Admin Dashboard, and Sign Out. Renders nothing for public visitors."
|
|
298
|
+
say ""
|
|
299
|
+
say "6. Seed your site structure", :yellow
|
|
300
|
+
say " Edit config/lean_cms_structure.yml, then:"
|
|
301
|
+
say " bin/rails lean_cms:load_structure"
|
|
302
|
+
say ""
|
|
303
|
+
say "7. Create your first admin", :yellow
|
|
304
|
+
say " bin/rails runner 'User.create!(email_address: \"admin@example.com\", password: \"change-me\", name: \"Admin\", active: true, is_super_admin: true)'"
|
|
305
|
+
say ""
|
|
306
|
+
say "8. Start the server and log in", :yellow
|
|
307
|
+
say " bin/dev (or rails server)"
|
|
308
|
+
say " Visit /lean-cms/login"
|
|
309
|
+
say ""
|
|
310
|
+
say "Optional: install demo pages — bin/rails generate lean_cms:demo", :cyan
|
|
311
|
+
say ""
|
|
312
|
+
say "Full docs: https://leancms.dev/docs/getting-started/", :cyan
|
|
313
|
+
say ""
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class AddLeanCmsColumnsTo<%= @user_table.camelize %> < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
<% @missing_columns.each do |name, type, opts| -%>
|
|
4
|
+
add_column :<%= @user_table %>, :<%= name %>, :<%= type %><% if opts.any? %>, <%= opts.map { |k, v| "#{k}: #{v.inspect}" }.join(", ") %><% end %>
|
|
5
|
+
<% end -%>
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LeanCms.configure do |config|
|
|
2
|
+
config.site_name = "My Site"
|
|
3
|
+
config.site_logo_path = nil # e.g. "logo.png" from app/assets/images
|
|
4
|
+
config.primary_color = "#2563eb"
|
|
5
|
+
config.secondary_color = "#1e40af"
|
|
6
|
+
config.admin_path = "/lean-cms"
|
|
7
|
+
config.user_class = "<%= @user_class || 'User' %>"
|
|
8
|
+
config.posts_per_page = 10 # blog index pagination
|
|
9
|
+
config.portfolio_enabled = true # show the /portfolio routes + admin tab
|
|
10
|
+
config.mailer_from = "noreply@example.com"
|
|
11
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
pages:
|
|
2
|
+
home:
|
|
3
|
+
display_title: "Home"
|
|
4
|
+
page_order: 1
|
|
5
|
+
sections:
|
|
6
|
+
hero:
|
|
7
|
+
display_title: "Hero"
|
|
8
|
+
section_order: 1
|
|
9
|
+
fields:
|
|
10
|
+
heading:
|
|
11
|
+
label: "Headline"
|
|
12
|
+
type: text
|
|
13
|
+
default: "Welcome to My Site"
|
|
14
|
+
subheading:
|
|
15
|
+
label: "Subheadline"
|
|
16
|
+
type: text
|
|
17
|
+
default: "A great place to start"
|
|
18
|
+
body:
|
|
19
|
+
label: "Body Text"
|
|
20
|
+
type: rich_text
|
|
21
|
+
default: "<p>Tell visitors what makes your business special.</p>"
|
|
22
|
+
cta_text:
|
|
23
|
+
label: "CTA Button Text"
|
|
24
|
+
type: text
|
|
25
|
+
default: "Get Started"
|
|
26
|
+
cta_url:
|
|
27
|
+
label: "CTA Button URL"
|
|
28
|
+
type: url
|
|
29
|
+
default: "/contact"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module LeanCms
|
|
2
|
+
class << self
|
|
3
|
+
attr_accessor :site_name,
|
|
4
|
+
:site_logo_path,
|
|
5
|
+
:primary_color,
|
|
6
|
+
:secondary_color,
|
|
7
|
+
:admin_path,
|
|
8
|
+
:user_class,
|
|
9
|
+
:posts_per_page,
|
|
10
|
+
:portfolio_enabled,
|
|
11
|
+
:mailer_from,
|
|
12
|
+
:docs_url
|
|
13
|
+
|
|
14
|
+
def configure
|
|
15
|
+
yield self
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Defaults
|
|
20
|
+
self.site_name = "My Site"
|
|
21
|
+
self.site_logo_path = nil
|
|
22
|
+
self.primary_color = "#2563eb"
|
|
23
|
+
self.secondary_color = "#1e40af"
|
|
24
|
+
self.admin_path = "/lean-cms"
|
|
25
|
+
self.user_class = "User"
|
|
26
|
+
self.posts_per_page = 10
|
|
27
|
+
self.portfolio_enabled = true
|
|
28
|
+
self.mailer_from = "noreply@example.com"
|
|
29
|
+
# Where the "?" icon in the admin header sends editors. Override in your
|
|
30
|
+
# initializer to point at your internal handbook instead of the public docs.
|
|
31
|
+
self.docs_url = "https://leancms.dev/docs/"
|
|
32
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module LeanCms
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
# Note: isolate_namespace is intentionally omitted so that all lean_cms_* route
|
|
4
|
+
# helpers remain accessible in both the engine and host app without renaming views.
|
|
5
|
+
|
|
6
|
+
# Override Rails' default engine_name derivation ("lean_cms_engine") so that
|
|
7
|
+
# tailwindcss-rails' built-in engine discovery
|
|
8
|
+
# (Tailwindcss::Engines.bundle, run before every tailwindcss:build) finds
|
|
9
|
+
# our Tailwind sources at app/assets/tailwind/lean_cms/engine.css.
|
|
10
|
+
engine_name "lean_cms"
|
|
11
|
+
|
|
12
|
+
config.generators do |g|
|
|
13
|
+
g.test_framework :rspec
|
|
14
|
+
g.fixture_replacement :factory_bot
|
|
15
|
+
g.factory_bot dir: "spec/factories"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Add gem's JS to Propshaft's load path so Stimulus controllers can be served.
|
|
19
|
+
# CSS (app/assets/lean_cms/) is discovered automatically via app/assets registration.
|
|
20
|
+
# Guarded — hosts without Propshaft (e.g. our test dummy app) don't expose
|
|
21
|
+
# config.assets at all.
|
|
22
|
+
initializer "lean_cms.assets" do |app|
|
|
23
|
+
app.config.assets.paths << root.join("app/javascript") if app.config.respond_to?(:assets)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Register the gem's Stimulus controllers with the host app's importmap
|
|
27
|
+
initializer "lean_cms.importmap", before: "importmap" do |app|
|
|
28
|
+
if app.config.respond_to?(:importmap)
|
|
29
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Make the gem's migrations available to the host app
|
|
34
|
+
initializer "lean_cms.migrations" do |app|
|
|
35
|
+
unless app.root.to_s == root.to_s
|
|
36
|
+
config.paths["db/migrate"].expanded.each do |path|
|
|
37
|
+
app.config.paths["db/migrate"] << path
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# NOTE: We deliberately do NOT generate app/assets/builds/tailwind/lean_cms.css
|
|
43
|
+
# ourselves. That's tailwindcss-rails' job: Tailwindcss::Engines.bundle (run as
|
|
44
|
+
# the `tailwindcss:engines` task, which is a prereq of every `tailwindcss:build`
|
|
45
|
+
# and `tailwindcss:watch`) walks Rails::Engine.subclasses, finds ours by
|
|
46
|
+
# engine_name "lean_cms" (set above), and writes the bundle file containing
|
|
47
|
+
# @import "<gem>/app/assets/tailwind/lean_cms/engine.css". Hosts then pull
|
|
48
|
+
# this in from their own app/assets/tailwind/application.css — see the
|
|
49
|
+
# install generator's `wire_tailwind` step.
|
|
50
|
+
|
|
51
|
+
# Ensure host app always has a resolvable edit-controls stylesheet path.
|
|
52
|
+
# Place it under app/assets/stylesheets so stylesheet logical path resolution
|
|
53
|
+
# matches `stylesheet_link_tag "lean_cms/cms_edit_controls"`.
|
|
54
|
+
initializer "lean_cms.edit_controls_css" do |app|
|
|
55
|
+
next if app.root.to_s == root.to_s
|
|
56
|
+
|
|
57
|
+
host_css = app.root.join("app/assets/stylesheets/lean_cms/cms_edit_controls.css")
|
|
58
|
+
next if host_css.exist?
|
|
59
|
+
|
|
60
|
+
require "fileutils"
|
|
61
|
+
source_css = root.join("app/assets/lean_cms/cms_edit_controls.css")
|
|
62
|
+
next unless source_css.exist?
|
|
63
|
+
|
|
64
|
+
FileUtils.mkdir_p(host_css.dirname)
|
|
65
|
+
File.write(host_css, source_css.read)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Action Text ships a CSS file via `bin/rails action_text:install`, but
|
|
69
|
+
# the gem's admin layout references it via `stylesheet_link_tag "actiontext"`
|
|
70
|
+
# before that step would normally run. Drop a default copy into the host's
|
|
71
|
+
# app/assets/stylesheets/ so a fresh install boots without a
|
|
72
|
+
# Propshaft::MissingAssetError. Hosts can edit / replace the file freely;
|
|
73
|
+
# we only write it once.
|
|
74
|
+
initializer "lean_cms.actiontext_css" do |app|
|
|
75
|
+
next if app.root.to_s == root.to_s
|
|
76
|
+
|
|
77
|
+
host_css = app.root.join("app/assets/stylesheets/actiontext.css")
|
|
78
|
+
next if host_css.exist?
|
|
79
|
+
|
|
80
|
+
require "fileutils"
|
|
81
|
+
source_css = root.join("app/assets/lean_cms/actiontext.css")
|
|
82
|
+
next unless source_css.exist?
|
|
83
|
+
|
|
84
|
+
FileUtils.mkdir_p(host_css.dirname)
|
|
85
|
+
File.write(host_css, source_css.read)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Load rake tasks
|
|
89
|
+
rake_tasks do
|
|
90
|
+
load LeanCms::Engine.root.join("lib/tasks/lean_cms.rake")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|