railspress-engine 0.1.2 → 1.2.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/LICENSE +20 -0
- data/README.md +195 -25
- data/app/assets/javascripts/railspress/admin.js +39 -0
- data/app/assets/javascripts/railspress/markdown_mode.js +343 -0
- data/app/assets/stylesheets/application.css +0 -0
- data/app/assets/stylesheets/railspress/admin/badges.css +70 -0
- data/app/assets/stylesheets/railspress/admin/base.css +25 -0
- data/app/assets/stylesheets/railspress/admin/buttons.css +140 -0
- data/app/assets/stylesheets/railspress/admin/cards.css +52 -0
- data/app/assets/stylesheets/railspress/admin/components/exports.css +55 -0
- data/app/assets/stylesheets/railspress/admin/components/focal_point.css +801 -0
- data/app/assets/stylesheets/railspress/admin/components/imports.css +144 -0
- data/app/assets/stylesheets/railspress/admin/components/lexxy.css +156 -0
- data/app/assets/stylesheets/railspress/admin/filters.css +73 -0
- data/app/assets/stylesheets/railspress/admin/flash.css +26 -0
- data/app/assets/stylesheets/railspress/admin/forms.css +459 -0
- data/app/assets/stylesheets/railspress/admin/layout.css +256 -0
- data/app/assets/stylesheets/railspress/admin/lists.css +24 -0
- data/app/assets/stylesheets/railspress/admin/page.css +111 -0
- data/app/assets/stylesheets/railspress/admin/responsive.css +174 -0
- data/app/assets/stylesheets/railspress/admin/stats.css +43 -0
- data/app/assets/stylesheets/railspress/admin/tables.css +163 -0
- data/app/assets/stylesheets/railspress/admin/utilities.css +202 -0
- data/app/assets/stylesheets/railspress/admin/variables.css +58 -0
- data/app/assets/stylesheets/railspress/application.css +44 -13
- data/app/controllers/railspress/admin/base_controller.rb +6 -3
- data/app/controllers/railspress/admin/categories_controller.rb +1 -1
- data/app/controllers/railspress/admin/cms_transfers_controller.rb +49 -0
- data/app/controllers/railspress/admin/content_element_versions_controller.rb +12 -0
- data/app/controllers/railspress/admin/content_elements_controller.rb +143 -0
- data/app/controllers/railspress/admin/content_groups_controller.rb +69 -0
- data/app/controllers/railspress/admin/dashboard_controller.rb +6 -0
- data/app/controllers/railspress/admin/entities_controller.rb +157 -0
- data/app/controllers/railspress/admin/exports_controller.rb +55 -0
- data/app/controllers/railspress/admin/focal_points_controller.rb +100 -0
- data/app/controllers/railspress/admin/imports_controller.rb +63 -0
- data/app/controllers/railspress/admin/posts_controller.rb +58 -4
- data/app/controllers/railspress/admin/prototypes_controller.rb +30 -0
- data/app/controllers/railspress/admin/tags_controller.rb +1 -1
- data/app/controllers/railspress/application_controller.rb +1 -0
- data/app/helpers/railspress/admin_helper.rb +733 -0
- data/app/helpers/railspress/application_helper.rb +23 -0
- data/app/helpers/railspress/cms_helper.rb +319 -0
- data/app/javascript/railspress/controllers/cms_inline_editor_controller.js +147 -0
- data/app/javascript/railspress/controllers/content_element_form_controller.js +15 -0
- data/app/javascript/railspress/controllers/crop_controller.js +224 -0
- data/app/javascript/railspress/controllers/dropzone_controller.js +261 -0
- data/app/javascript/railspress/controllers/focal_point_controller.js +124 -0
- data/app/javascript/railspress/controllers/image_section_controller.js +94 -0
- data/app/javascript/railspress/controllers/index.js +37 -0
- data/app/javascript/railspress/index.js +62 -0
- data/app/jobs/railspress/export_posts_job.rb +16 -0
- data/app/jobs/railspress/import_posts_job.rb +44 -0
- data/app/models/concerns/railspress/has_focal_point.rb +242 -0
- data/app/models/concerns/railspress/soft_deletable.rb +23 -0
- data/app/models/concerns/railspress/taggable.rb +23 -0
- data/app/models/railspress/content_element.rb +103 -0
- data/app/models/railspress/content_element_version.rb +32 -0
- data/app/models/railspress/content_group.rb +39 -0
- data/app/models/railspress/export.rb +67 -0
- data/app/models/railspress/focal_point.rb +70 -0
- data/app/models/railspress/import.rb +65 -0
- data/app/models/railspress/post.rb +102 -15
- data/app/models/railspress/post_export_processor.rb +162 -0
- data/app/models/railspress/post_import_processor.rb +382 -0
- data/app/models/railspress/tag.rb +10 -3
- data/app/models/railspress/tagging.rb +11 -0
- data/app/services/railspress/content_export_service.rb +122 -0
- data/app/services/railspress/content_import_service.rb +228 -0
- data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
- data/app/views/active_storage/blobs/_blob.html.erb +1 -1
- data/app/views/layouts/railspress/admin.html.erb +3 -1
- data/app/views/railspress/admin/categories/index.html.erb +11 -15
- data/app/views/railspress/admin/cms_transfers/show.html.erb +167 -0
- data/app/views/railspress/admin/content_element_versions/show.html.erb +42 -0
- data/app/views/railspress/admin/content_elements/_form.html.erb +71 -0
- data/app/views/railspress/admin/content_elements/_inline_form.html.erb +32 -0
- data/app/views/railspress/admin/content_elements/_inline_form_frame.html.erb +6 -0
- data/app/views/railspress/admin/content_elements/edit.html.erb +6 -0
- data/app/views/railspress/admin/content_elements/index.html.erb +74 -0
- data/app/views/railspress/admin/content_elements/new.html.erb +6 -0
- data/app/views/railspress/admin/content_elements/show.html.erb +124 -0
- data/app/views/railspress/admin/content_groups/_form.html.erb +9 -0
- data/app/views/railspress/admin/content_groups/edit.html.erb +6 -0
- data/app/views/railspress/admin/content_groups/index.html.erb +42 -0
- data/app/views/railspress/admin/content_groups/new.html.erb +6 -0
- data/app/views/railspress/admin/content_groups/show.html.erb +92 -0
- data/app/views/railspress/admin/dashboard/index.html.erb +36 -1
- data/app/views/railspress/admin/entities/_form.html.erb +53 -0
- data/app/views/railspress/admin/entities/edit.html.erb +4 -0
- data/app/views/railspress/admin/entities/index.html.erb +74 -0
- data/app/views/railspress/admin/entities/new.html.erb +4 -0
- data/app/views/railspress/admin/entities/show.html.erb +117 -0
- data/app/views/railspress/admin/exports/show.html.erb +62 -0
- data/app/views/railspress/admin/imports/_instructions.html.erb +56 -0
- data/app/views/railspress/admin/imports/show.html.erb +137 -0
- data/app/views/railspress/admin/posts/_form.html.erb +102 -28
- data/app/views/railspress/admin/posts/_post_row.html.erb +40 -0
- data/app/views/railspress/admin/posts/index.html.erb +47 -36
- data/app/views/railspress/admin/posts/show.html.erb +55 -19
- data/app/views/railspress/admin/prototypes/image_section.html.erb +42 -0
- data/app/views/railspress/admin/shared/_dropzone.html.erb +84 -0
- data/app/views/railspress/admin/shared/_focal_point_editor.html.erb +102 -0
- data/app/views/railspress/admin/shared/_image_section.html.erb +159 -0
- data/app/views/railspress/admin/shared/_image_section_compact.html.erb +90 -0
- data/app/views/railspress/admin/shared/_image_section_editor.html.erb +171 -0
- data/app/views/railspress/admin/shared/_image_section_v2.html.erb +205 -0
- data/app/views/railspress/admin/shared/_sidebar.html.erb +73 -5
- data/app/views/railspress/admin/tags/index.html.erb +12 -16
- data/config/brakeman.ignore +18 -0
- data/config/importmap.rb +23 -0
- data/config/routes.rb +62 -1
- data/db/migrate/20241218000004_create_railspress_post_tags.rb +1 -1
- data/db/migrate/20241218000005_create_railspress_imports.rb +21 -0
- data/db/migrate/20241218000006_create_railspress_exports.rb +20 -0
- data/db/migrate/20241218000007_create_railspress_taggings.rb +20 -0
- data/db/migrate/20241218000008_drop_railspress_post_tags.rb +14 -0
- data/db/migrate/20241218000010_add_reading_time_to_railspress_posts.rb +5 -0
- data/db/migrate/20250105000002_create_railspress_focal_points.rb +20 -0
- data/db/migrate/20260206000001_create_railspress_content_groups.rb +18 -0
- data/db/migrate/20260206000002_create_railspress_content_elements.rb +21 -0
- data/db/migrate/20260206000003_create_railspress_content_element_versions.rb +20 -0
- data/db/migrate/20260207000001_add_unique_index_to_content_elements.rb +11 -0
- data/db/migrate/20260211112812_add_image_hint_to_railspress_content_elements.rb +7 -0
- data/db/migrate/20260211154040_add_required_to_railspress_content_elements.rb +5 -0
- data/lib/generators/railspress/entity/entity_generator.rb +89 -0
- data/lib/generators/railspress/entity/templates/migration.rb.tt +13 -0
- data/lib/generators/railspress/entity/templates/model.rb.tt +21 -0
- data/lib/generators/railspress/install/install_generator.rb +51 -40
- data/lib/generators/railspress/install/templates/initializer.rb +29 -0
- data/lib/railspress/engine.rb +38 -0
- data/lib/railspress/entity.rb +239 -0
- data/lib/railspress/version.rb +1 -1
- data/lib/railspress.rb +198 -8
- data/lib/tasks/railspress_tasks.rake +49 -4
- metadata +215 -21
- data/MIT-LICENSE +0 -20
- data/app/assets/stylesheets/railspress/admin.css +0 -1207
- 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,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,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
|
|
@@ -10,6 +10,10 @@ module Railspress
|
|
|
10
10
|
|
|
11
11
|
desc "Install RailsPress: copy migrations, mount engine, and configure JavaScript"
|
|
12
12
|
|
|
13
|
+
def detect_install_mode
|
|
14
|
+
@upgrading_existing_install = existing_railspress_installation?
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
def copy_railspress_migrations
|
|
14
18
|
run "bundle exec rake railspress:install:migrations"
|
|
15
19
|
end
|
|
@@ -59,61 +63,58 @@ module Railspress
|
|
|
59
63
|
say_status :pinned, "@rails/activestorage in importmap", :green
|
|
60
64
|
end
|
|
61
65
|
|
|
62
|
-
#
|
|
63
|
-
if importmap_content.include?('"lexxy"')
|
|
64
|
-
say_status :skip, "Lexxy already pinned in importmap", :yellow
|
|
65
|
-
else
|
|
66
|
-
append_to_file importmap_file, <<~RUBY
|
|
67
|
-
|
|
68
|
-
# RailsPress rich text editor
|
|
69
|
-
pin "lexxy", to: "lexxy.js"
|
|
70
|
-
RUBY
|
|
71
|
-
say_status :pinned, "Lexxy in importmap", :green
|
|
72
|
-
end
|
|
66
|
+
# Lexxy is auto-pinned by the engine's importmap — no host app pin needed
|
|
73
67
|
end
|
|
74
68
|
|
|
75
|
-
def
|
|
76
|
-
|
|
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)
|
|
69
|
+
def generate_initializer
|
|
70
|
+
initializer_path = Rails.root.join("config", "initializers", "railspress.rb")
|
|
82
71
|
|
|
83
|
-
if
|
|
84
|
-
say_status :skip, "
|
|
72
|
+
if File.exist?(initializer_path)
|
|
73
|
+
say_status :skip, "config/initializers/railspress.rb already exists", :yellow
|
|
85
74
|
else
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// RailsPress rich text editor
|
|
89
|
-
import "lexxy"
|
|
90
|
-
JS
|
|
91
|
-
say_status :added, "Lexxy import to application.js", :green
|
|
75
|
+
template "initializer.rb", initializer_path
|
|
76
|
+
say_status :created, "config/initializers/railspress.rb", :green
|
|
92
77
|
end
|
|
93
78
|
end
|
|
94
79
|
|
|
95
80
|
def show_post_install_message
|
|
81
|
+
upgrading = !!@upgrading_existing_install
|
|
82
|
+
|
|
96
83
|
say ""
|
|
97
84
|
say "=" * 60, :green
|
|
98
|
-
say
|
|
85
|
+
say(" RailsPress #{upgrading ? "upgrade setup completed!" : "installed successfully!"}", :green)
|
|
99
86
|
say "=" * 60, :green
|
|
100
87
|
say ""
|
|
101
|
-
say "Next steps:", :yellow
|
|
102
|
-
say ""
|
|
103
|
-
say " 1. Run migrations:"
|
|
104
|
-
say " $ rails db:migrate", :cyan
|
|
88
|
+
say(upgrading ? "Upgrade next steps:" : "Next steps:", :yellow)
|
|
105
89
|
say ""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
90
|
+
if upgrading
|
|
91
|
+
say " 1. Review upgrade notes:"
|
|
92
|
+
say " docs/UPGRADING.md", :cyan
|
|
93
|
+
say ""
|
|
94
|
+
say " 2. Run migrations:"
|
|
95
|
+
say " $ rails db:migrate", :cyan
|
|
96
|
+
say ""
|
|
97
|
+
say " 3. Verify your JS integration if using host-page features:"
|
|
98
|
+
say ' app/javascript/application.js includes: import "railspress"', :cyan
|
|
99
|
+
else
|
|
100
|
+
say " 1. Run migrations:"
|
|
101
|
+
say " $ rails db:migrate", :cyan
|
|
102
|
+
say ""
|
|
103
|
+
say " 2. Access the admin dashboard:"
|
|
104
|
+
say " http://localhost:3000/railspress/admin", :cyan
|
|
105
|
+
say ""
|
|
106
|
+
say " 3. (Optional) Change the mount path in config/routes.rb:"
|
|
107
|
+
say ' mount Railspress::Engine => "/blog"', :cyan
|
|
108
|
+
end
|
|
111
109
|
say ""
|
|
112
|
-
say "
|
|
113
|
-
say "
|
|
114
|
-
say "
|
|
110
|
+
say "Optional features:", :yellow
|
|
111
|
+
say " Edit config/initializers/railspress.rb to enable:"
|
|
112
|
+
say " - CMS content elements (config.enable_cms)"
|
|
113
|
+
say " - Inline CMS editing (config.inline_editing_check)"
|
|
114
|
+
say " See docs/CONFIGURING.md and docs/INLINE_EDITING.md for details."
|
|
115
115
|
say ""
|
|
116
|
-
say "
|
|
116
|
+
say "Full guides & documentation:", :yellow
|
|
117
|
+
say " https://railspress.org", :cyan
|
|
117
118
|
say ""
|
|
118
119
|
say "=" * 60, :green
|
|
119
120
|
end
|
|
@@ -139,6 +140,16 @@ module Railspress
|
|
|
139
140
|
def importmap_available?
|
|
140
141
|
defined?(Importmap) && Rails.root.join("config", "importmap.rb").exist?
|
|
141
142
|
end
|
|
143
|
+
|
|
144
|
+
def existing_railspress_installation?
|
|
145
|
+
routes_mounted = File.read(rails_route_file).include?("Railspress::Engine")
|
|
146
|
+
initializer_exists = Rails.root.join("config", "initializers", "railspress.rb").exist?
|
|
147
|
+
migrations_copied = Dir.glob(migrations_dir.join("*railspress*.rb")).any?
|
|
148
|
+
|
|
149
|
+
routes_mounted || initializer_exists || migrations_copied
|
|
150
|
+
rescue Errno::ENOENT
|
|
151
|
+
false
|
|
152
|
+
end
|
|
142
153
|
end
|
|
143
154
|
end
|
|
144
155
|
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
|
data/lib/railspress/engine.rb
CHANGED
|
@@ -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
|