railspress-engine 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +20 -0
  3. data/README.md +193 -25
  4. data/app/assets/javascripts/railspress/admin.js +39 -0
  5. data/app/assets/javascripts/railspress/markdown_mode.js +343 -0
  6. data/app/assets/stylesheets/application.css +0 -0
  7. data/app/assets/stylesheets/railspress/admin/badges.css +70 -0
  8. data/app/assets/stylesheets/railspress/admin/base.css +25 -0
  9. data/app/assets/stylesheets/railspress/admin/buttons.css +140 -0
  10. data/app/assets/stylesheets/railspress/admin/cards.css +52 -0
  11. data/app/assets/stylesheets/railspress/admin/components/exports.css +55 -0
  12. data/app/assets/stylesheets/railspress/admin/components/focal_point.css +801 -0
  13. data/app/assets/stylesheets/railspress/admin/components/imports.css +144 -0
  14. data/app/assets/stylesheets/railspress/admin/components/lexxy.css +145 -0
  15. data/app/assets/stylesheets/railspress/admin/filters.css +73 -0
  16. data/app/assets/stylesheets/railspress/admin/flash.css +26 -0
  17. data/app/assets/stylesheets/railspress/admin/forms.css +459 -0
  18. data/app/assets/stylesheets/railspress/admin/layout.css +256 -0
  19. data/app/assets/stylesheets/railspress/admin/lists.css +24 -0
  20. data/app/assets/stylesheets/railspress/admin/page.css +111 -0
  21. data/app/assets/stylesheets/railspress/admin/responsive.css +174 -0
  22. data/app/assets/stylesheets/railspress/admin/stats.css +43 -0
  23. data/app/assets/stylesheets/railspress/admin/tables.css +163 -0
  24. data/app/assets/stylesheets/railspress/admin/utilities.css +202 -0
  25. data/app/assets/stylesheets/railspress/admin/variables.css +58 -0
  26. data/app/assets/stylesheets/railspress/application.css +44 -13
  27. data/app/controllers/railspress/admin/base_controller.rb +6 -3
  28. data/app/controllers/railspress/admin/categories_controller.rb +1 -1
  29. data/app/controllers/railspress/admin/cms_transfers_controller.rb +49 -0
  30. data/app/controllers/railspress/admin/content_element_versions_controller.rb +12 -0
  31. data/app/controllers/railspress/admin/content_elements_controller.rb +143 -0
  32. data/app/controllers/railspress/admin/content_groups_controller.rb +69 -0
  33. data/app/controllers/railspress/admin/dashboard_controller.rb +6 -0
  34. data/app/controllers/railspress/admin/entities_controller.rb +157 -0
  35. data/app/controllers/railspress/admin/exports_controller.rb +55 -0
  36. data/app/controllers/railspress/admin/focal_points_controller.rb +100 -0
  37. data/app/controllers/railspress/admin/imports_controller.rb +63 -0
  38. data/app/controllers/railspress/admin/posts_controller.rb +58 -4
  39. data/app/controllers/railspress/admin/prototypes_controller.rb +30 -0
  40. data/app/controllers/railspress/admin/tags_controller.rb +1 -1
  41. data/app/controllers/railspress/application_controller.rb +1 -0
  42. data/app/helpers/railspress/admin_helper.rb +733 -0
  43. data/app/helpers/railspress/application_helper.rb +23 -0
  44. data/app/helpers/railspress/cms_helper.rb +338 -0
  45. data/app/javascript/railspress/controllers/cms_inline_editor_controller.js +130 -0
  46. data/app/javascript/railspress/controllers/content_element_form_controller.js +15 -0
  47. data/app/javascript/railspress/controllers/crop_controller.js +224 -0
  48. data/app/javascript/railspress/controllers/dropzone_controller.js +261 -0
  49. data/app/javascript/railspress/controllers/focal_point_controller.js +124 -0
  50. data/app/javascript/railspress/controllers/image_section_controller.js +94 -0
  51. data/app/javascript/railspress/controllers/index.js +37 -0
  52. data/app/javascript/railspress/index.js +59 -0
  53. data/app/jobs/railspress/export_posts_job.rb +16 -0
  54. data/app/jobs/railspress/import_posts_job.rb +44 -0
  55. data/app/models/concerns/railspress/has_focal_point.rb +242 -0
  56. data/app/models/concerns/railspress/soft_deletable.rb +23 -0
  57. data/app/models/concerns/railspress/taggable.rb +23 -0
  58. data/app/models/railspress/content_element.rb +103 -0
  59. data/app/models/railspress/content_element_version.rb +32 -0
  60. data/app/models/railspress/content_group.rb +39 -0
  61. data/app/models/railspress/export.rb +67 -0
  62. data/app/models/railspress/focal_point.rb +70 -0
  63. data/app/models/railspress/import.rb +65 -0
  64. data/app/models/railspress/post.rb +102 -15
  65. data/app/models/railspress/post_export_processor.rb +162 -0
  66. data/app/models/railspress/post_import_processor.rb +382 -0
  67. data/app/models/railspress/tag.rb +7 -2
  68. data/app/models/railspress/tagging.rb +11 -0
  69. data/app/services/railspress/content_export_service.rb +122 -0
  70. data/app/services/railspress/content_import_service.rb +228 -0
  71. data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
  72. data/app/views/active_storage/blobs/_blob.html.erb +1 -1
  73. data/app/views/layouts/railspress/admin.html.erb +3 -1
  74. data/app/views/railspress/admin/categories/index.html.erb +11 -15
  75. data/app/views/railspress/admin/cms_transfers/show.html.erb +167 -0
  76. data/app/views/railspress/admin/content_element_versions/show.html.erb +42 -0
  77. data/app/views/railspress/admin/content_elements/_form.html.erb +71 -0
  78. data/app/views/railspress/admin/content_elements/_inline_form.html.erb +32 -0
  79. data/app/views/railspress/admin/content_elements/_inline_form_frame.html.erb +6 -0
  80. data/app/views/railspress/admin/content_elements/edit.html.erb +6 -0
  81. data/app/views/railspress/admin/content_elements/index.html.erb +74 -0
  82. data/app/views/railspress/admin/content_elements/new.html.erb +6 -0
  83. data/app/views/railspress/admin/content_elements/show.html.erb +124 -0
  84. data/app/views/railspress/admin/content_groups/_form.html.erb +9 -0
  85. data/app/views/railspress/admin/content_groups/edit.html.erb +6 -0
  86. data/app/views/railspress/admin/content_groups/index.html.erb +42 -0
  87. data/app/views/railspress/admin/content_groups/new.html.erb +6 -0
  88. data/app/views/railspress/admin/content_groups/show.html.erb +92 -0
  89. data/app/views/railspress/admin/dashboard/index.html.erb +36 -1
  90. data/app/views/railspress/admin/entities/_form.html.erb +53 -0
  91. data/app/views/railspress/admin/entities/edit.html.erb +4 -0
  92. data/app/views/railspress/admin/entities/index.html.erb +74 -0
  93. data/app/views/railspress/admin/entities/new.html.erb +4 -0
  94. data/app/views/railspress/admin/entities/show.html.erb +117 -0
  95. data/app/views/railspress/admin/exports/show.html.erb +62 -0
  96. data/app/views/railspress/admin/imports/_instructions.html.erb +56 -0
  97. data/app/views/railspress/admin/imports/show.html.erb +137 -0
  98. data/app/views/railspress/admin/posts/_form.html.erb +102 -28
  99. data/app/views/railspress/admin/posts/_post_row.html.erb +40 -0
  100. data/app/views/railspress/admin/posts/index.html.erb +47 -36
  101. data/app/views/railspress/admin/posts/show.html.erb +55 -19
  102. data/app/views/railspress/admin/prototypes/image_section.html.erb +42 -0
  103. data/app/views/railspress/admin/shared/_dropzone.html.erb +84 -0
  104. data/app/views/railspress/admin/shared/_focal_point_editor.html.erb +102 -0
  105. data/app/views/railspress/admin/shared/_image_section.html.erb +159 -0
  106. data/app/views/railspress/admin/shared/_image_section_compact.html.erb +90 -0
  107. data/app/views/railspress/admin/shared/_image_section_editor.html.erb +171 -0
  108. data/app/views/railspress/admin/shared/_image_section_v2.html.erb +205 -0
  109. data/app/views/railspress/admin/shared/_sidebar.html.erb +73 -5
  110. data/app/views/railspress/admin/tags/index.html.erb +12 -16
  111. data/config/brakeman.ignore +18 -0
  112. data/config/importmap.rb +20 -0
  113. data/config/routes.rb +62 -1
  114. data/db/migrate/20241218000004_create_railspress_post_tags.rb +1 -1
  115. data/db/migrate/20241218000005_create_railspress_imports.rb +21 -0
  116. data/db/migrate/20241218000006_create_railspress_exports.rb +20 -0
  117. data/db/migrate/20241218000007_create_railspress_taggings.rb +20 -0
  118. data/db/migrate/20241218000008_drop_railspress_post_tags.rb +14 -0
  119. data/db/migrate/20241218000010_add_reading_time_to_railspress_posts.rb +5 -0
  120. data/db/migrate/20250105000002_create_railspress_focal_points.rb +20 -0
  121. data/db/migrate/20260206000001_create_railspress_content_groups.rb +18 -0
  122. data/db/migrate/20260206000002_create_railspress_content_elements.rb +21 -0
  123. data/db/migrate/20260206000003_create_railspress_content_element_versions.rb +20 -0
  124. data/db/migrate/20260207000001_add_unique_index_to_content_elements.rb +11 -0
  125. data/db/migrate/20260211112812_add_image_hint_to_railspress_content_elements.rb +7 -0
  126. data/db/migrate/20260211154040_add_required_to_railspress_content_elements.rb +5 -0
  127. data/lib/generators/railspress/entity/entity_generator.rb +89 -0
  128. data/lib/generators/railspress/entity/templates/migration.rb.tt +13 -0
  129. data/lib/generators/railspress/entity/templates/model.rb.tt +21 -0
  130. data/lib/generators/railspress/install/install_generator.rb +11 -20
  131. data/lib/generators/railspress/install/templates/initializer.rb +29 -0
  132. data/lib/railspress/engine.rb +38 -0
  133. data/lib/railspress/entity.rb +239 -0
  134. data/lib/railspress/version.rb +1 -1
  135. data/lib/railspress.rb +198 -8
  136. data/lib/tasks/railspress_tasks.rake +49 -4
  137. metadata +203 -14
  138. data/MIT-LICENSE +0 -20
  139. data/app/assets/stylesheets/railspress/admin.css +0 -1207
  140. data/app/models/railspress/post_tag.rb +0 -8
@@ -0,0 +1,21 @@
1
+ class CreateRailspressImports < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :railspress_imports do |t|
4
+ t.string :import_type, null: false
5
+ t.string :filename
6
+ t.string :content_type
7
+ t.string :status, default: "pending", null: false
8
+ t.integer :total_count, default: 0
9
+ t.integer :success_count, default: 0
10
+ t.integer :error_count, default: 0
11
+ t.text :error_messages
12
+ t.bigint :user_id
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :railspress_imports, :import_type
18
+ add_index :railspress_imports, :status
19
+ add_index :railspress_imports, :user_id
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ class CreateRailspressExports < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :railspress_exports do |t|
4
+ t.string :export_type, null: false
5
+ t.string :filename
6
+ t.string :status, default: "pending", null: false
7
+ t.integer :total_count, default: 0
8
+ t.integer :success_count, default: 0
9
+ t.integer :error_count, default: 0
10
+ t.text :error_messages
11
+ t.bigint :user_id
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :railspress_exports, :export_type
17
+ add_index :railspress_exports, :status
18
+ add_index :railspress_exports, :user_id
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class CreateRailspressTaggings < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :railspress_taggings do |t|
4
+ t.references :tag, null: false, foreign_key: { to_table: :railspress_tags }
5
+ t.references :taggable, polymorphic: true, null: false
6
+ t.timestamps
7
+ end
8
+
9
+ # Unique constraint: one tag per taggable
10
+ add_index :railspress_taggings,
11
+ [ :tag_id, :taggable_type, :taggable_id ],
12
+ unique: true,
13
+ name: "index_taggings_unique"
14
+
15
+ # For "find all entities with tag X" queries
16
+ add_index :railspress_taggings,
17
+ [ :taggable_type, :taggable_id ],
18
+ name: "index_taggings_on_taggable"
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ class DropRailspressPostTags < ActiveRecord::Migration[8.0]
2
+ def up
3
+ drop_table :railspress_post_tags
4
+ end
5
+
6
+ def down
7
+ create_table :railspress_post_tags do |t|
8
+ t.references :post, null: false, foreign_key: { to_table: :railspress_posts }
9
+ t.references :tag, null: false, foreign_key: { to_table: :railspress_tags }
10
+ t.timestamps
11
+ end
12
+ add_index :railspress_post_tags, [ :post_id, :tag_id ], unique: true
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class AddReadingTimeToRailspressPosts < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :railspress_posts, :reading_time, :integer
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRailspressFocalPoints < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :railspress_focal_points do |t|
6
+ t.references :record, polymorphic: true, null: false, index: false
7
+ t.string :attachment_name, null: false
8
+ t.decimal :focal_x, precision: 5, scale: 4, default: 0.5, null: false
9
+ t.decimal :focal_y, precision: 5, scale: 4, default: 0.5, null: false
10
+ t.json :overrides, default: {}
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :railspress_focal_points,
16
+ [ :record_type, :record_id, :attachment_name ],
17
+ unique: true,
18
+ name: "idx_focal_points_record_attachment"
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRailspressContentGroups < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :railspress_content_groups do |t|
6
+ t.string :name, null: false
7
+ t.text :description
8
+ t.bigint :author_id
9
+ t.datetime :deleted_at
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :railspress_content_groups, :name, unique: true
15
+ add_index :railspress_content_groups, :deleted_at
16
+ add_index :railspress_content_groups, :author_id
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRailspressContentElements < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :railspress_content_elements do |t|
6
+ t.string :name, null: false
7
+ t.integer :content_type, default: 0, null: false
8
+ t.text :text_content
9
+ t.references :content_group, null: false, foreign_key: { to_table: :railspress_content_groups }
10
+ t.bigint :author_id
11
+ t.integer :position
12
+ t.datetime :deleted_at
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :railspress_content_elements, :deleted_at
18
+ add_index :railspress_content_elements, :author_id
19
+ add_index :railspress_content_elements, :content_type
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRailspressContentElementVersions < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :railspress_content_element_versions do |t|
6
+ t.references :content_element, null: false, foreign_key: { to_table: :railspress_content_elements }
7
+ t.bigint :author_id
8
+ t.text :text_content
9
+ t.integer :version_number, null: false
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :railspress_content_element_versions,
15
+ [ :content_element_id, :version_number ],
16
+ unique: true,
17
+ name: "idx_content_element_versions_unique"
18
+ add_index :railspress_content_element_versions, :author_id
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddUniqueIndexToContentElements < ActiveRecord::Migration[8.1]
4
+ def change
5
+ add_index :railspress_content_elements,
6
+ [ :content_group_id, :name ],
7
+ unique: true,
8
+ where: "deleted_at IS NULL",
9
+ name: "idx_content_elements_unique_name_per_group"
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddImageHintToRailspressContentElements < ActiveRecord::Migration[8.1]
4
+ def change
5
+ add_column :railspress_content_elements, :image_hint, :string
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class AddRequiredToRailspressContentElements < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :railspress_content_elements, :required, :boolean, default: false, null: false
4
+ end
5
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module Railspress
7
+ module Generators
8
+ class EntityGenerator < Rails::Generators::NamedBase
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ source_root File.expand_path("templates", __dir__)
12
+
13
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
14
+
15
+ desc "Generate a RailsPress-managed entity with model, migration, and registration"
16
+
17
+ def create_model_file
18
+ template "model.rb.tt", File.join("app/models", "#{file_name}.rb")
19
+ end
20
+
21
+ def create_migration
22
+ migration_template "migration.rb.tt", File.join(db_migrate_path, "create_#{table_name}.rb")
23
+ end
24
+
25
+ def add_registration
26
+ initializer_file = "config/initializers/railspress.rb"
27
+
28
+ unless File.exist?(initializer_file)
29
+ create_file initializer_file, <<~RUBY
30
+ # frozen_string_literal: true
31
+
32
+ Railspress.configure do |config|
33
+ # Register your CMS-managed entities here
34
+ # config.register_entity Project
35
+ end
36
+ RUBY
37
+ say_status :created, "RailsPress initializer", :green
38
+ end
39
+
40
+ inject_into_file initializer_file, after: "Railspress.configure do |config|\n" do
41
+ " config.register_entity #{class_name}\n"
42
+ end
43
+
44
+ say_status :registered, "#{class_name} entity", :green
45
+ end
46
+
47
+ def show_next_steps
48
+ say ""
49
+ say "=" * 60, :green
50
+ say " Entity #{class_name} created!", :green
51
+ say "=" * 60, :green
52
+ say ""
53
+ say "Next steps:", :yellow
54
+ say ""
55
+ say " 1. Run the migration:"
56
+ say " $ rails db:migrate", :cyan
57
+ say ""
58
+ say " 2. Restart your Rails server"
59
+ say ""
60
+ say " 3. Access #{class_name} in the admin:"
61
+ say " /railspress/admin/entities/#{table_name}", :cyan
62
+ say ""
63
+ say "=" * 60, :green
64
+ end
65
+
66
+ private
67
+
68
+ def db_migrate_path
69
+ "db/migrate"
70
+ end
71
+
72
+ def rich_text_fields
73
+ attributes.select { |a| a.type == :rich_text }
74
+ end
75
+
76
+ def regular_attributes
77
+ attributes.reject { |a| a.type == :rich_text }
78
+ end
79
+
80
+ def railspress_field_names
81
+ attributes.map { |a| ":#{a.name}" }.join(", ")
82
+ end
83
+
84
+ def migration_version
85
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Create<%= class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= table_name %> do |t|
6
+ <% regular_attributes.each do |attribute| -%>
7
+ t.<%= attribute.type %> :<%= attribute.name %>
8
+ <% end -%>
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %> < ApplicationRecord
4
+ include Railspress::Entity
5
+ <% rich_text_fields.each do |attribute| -%>
6
+
7
+ has_rich_text :<%= attribute.name %>
8
+ <% end -%>
9
+
10
+ # Declare which fields appear in the CMS admin
11
+ railspress_fields <%= railspress_field_names %>
12
+ <% rich_text_fields.each do |attribute| -%>
13
+ railspress_fields :<%= attribute.name %>, as: :rich_text
14
+ <% end -%>
15
+
16
+ # Optional: customize sidebar label (defaults to pluralized class name)
17
+ # railspress_label "Custom Label"
18
+
19
+ # Add your validations
20
+ # validates :title, presence: true
21
+ end
@@ -72,23 +72,14 @@ module Railspress
72
72
  end
73
73
  end
74
74
 
75
- def configure_javascript
76
- return unless importmap_available?
77
-
78
- application_js = Rails.root.join("app", "javascript", "application.js")
79
- return unless File.exist?(application_js)
80
-
81
- js_content = File.read(application_js)
75
+ def generate_initializer
76
+ initializer_path = Rails.root.join("config", "initializers", "railspress.rb")
82
77
 
83
- if js_content.include?('import "lexxy"')
84
- say_status :skip, "Lexxy already imported in application.js", :yellow
78
+ if File.exist?(initializer_path)
79
+ say_status :skip, "config/initializers/railspress.rb already exists", :yellow
85
80
  else
86
- append_to_file application_js, <<~JS
87
-
88
- // RailsPress rich text editor
89
- import "lexxy"
90
- JS
91
- say_status :added, "Lexxy import to application.js", :green
81
+ template "initializer.rb", initializer_path
82
+ say_status :created, "config/initializers/railspress.rb", :green
92
83
  end
93
84
  end
94
85
 
@@ -109,11 +100,11 @@ module Railspress
109
100
  say " 3. (Optional) Change the mount path in config/routes.rb:"
110
101
  say " mount Railspress::Engine => \"/blog\"", :cyan
111
102
  say ""
112
- say "JavaScript setup:", :yellow
113
- say " The installer added Lexxy (rich text editor) to your importmap"
114
- say " and application.js. If you use a custom JS entry point, add:"
115
- say ""
116
- say " import \"lexxy\"", :cyan
103
+ say "Optional features:", :yellow
104
+ say " Edit config/initializers/railspress.rb to enable:"
105
+ say " - CMS content elements (config.enable_cms)"
106
+ say " - Inline CMS editing (config.inline_editing_check)"
107
+ say " See docs/CONFIGURING.md and docs/INLINE_EDITING.md for details."
117
108
  say ""
118
109
  say "=" * 60, :green
119
110
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ Railspress.configure do |config|
4
+ # === Blog Features (always available) ===
5
+
6
+ # Author tracking for posts and content elements.
7
+ # Uncomment to enable:
8
+ # config.enable_authors
9
+ # config.author_class_name = "User"
10
+ # config.current_author_method = :current_user
11
+
12
+ # === CMS Content Elements (opt-in) ===
13
+ # Adds content groups, content elements, and the cms_element/cms_value
14
+ # view helpers for managing structured content on your site.
15
+ # Image elements support dropzone upload and focal points when
16
+ # enable_focal_points is also active.
17
+ # See docs/CONFIGURING.md for details.
18
+ # Uncomment to enable:
19
+ # config.enable_cms
20
+
21
+ # === Inline CMS Editing (requires enable_cms) ===
22
+ # Right-click editing of CMS content on public pages.
23
+ # Also requires: import "railspress" in your application.js
24
+ # and yield :head in your layout. See docs/INLINE_EDITING.md.
25
+ # Uncomment to enable:
26
+ # config.inline_editing_check = ->(context) {
27
+ # context.controller.current_user&.admin?
28
+ # }
29
+ end
@@ -6,8 +6,46 @@ module Railspress
6
6
  initializer "railspress.assets" do |app|
7
7
  if app.config.respond_to?(:assets)
8
8
  app.config.assets.paths << root.join("app", "assets", "stylesheets").to_s
9
+ app.config.assets.paths << root.join("app", "assets", "stylesheets", "railspress").to_s
9
10
  app.config.assets.paths << root.join("app", "assets", "javascripts").to_s
10
11
  app.config.assets.paths << root.join("app", "assets", "images").to_s
12
+ app.config.assets.paths << root.join("app", "javascript").to_s
13
+ end
14
+ end
15
+
16
+ # Make CMS helper available to host application views (or stub when disabled)
17
+ initializer "railspress.cms_helper" do
18
+ ActiveSupport.on_load(:action_view) do
19
+ if Railspress.cms_enabled?
20
+ include Railspress::CmsHelper
21
+ else
22
+ include Railspress::CmsHelper::DisabledStub
23
+ end
24
+ end
25
+ end
26
+
27
+ # Clear CMS cache on each request in development
28
+ initializer "railspress.cms_cache" do |app|
29
+ if Railspress.cms_enabled?
30
+ app.config.to_prepare do
31
+ Railspress::CmsHelper.clear_cache
32
+ end
33
+ end
34
+ end
35
+
36
+ # Validate configuration after all initializers have run
37
+ initializer "railspress.validate_config" do
38
+ if Railspress.inline_editing_check && !Railspress.cms_enabled?
39
+ raise Railspress::ConfigurationError,
40
+ "Inline editing requires CMS. Add `config.enable_cms` to your initializer."
41
+ end
42
+ end
43
+
44
+ # Configure importmap for Stimulus controllers
45
+ initializer "railspress.importmap", before: "importmap" do |app|
46
+ if app.respond_to?(:importmap)
47
+ app.config.importmap.paths << root.join("config", "importmap.rb")
48
+ app.config.importmap.cache_sweepers << root.join("app", "javascript")
11
49
  end
12
50
  end
13
51
  end