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,171 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Image Section Editor
|
|
3
|
+
====================
|
|
4
|
+
Expanded focal point editor with its own form.
|
|
5
|
+
Loads via Turbo Frame, saves independently of parent form.
|
|
6
|
+
|
|
7
|
+
All actions (Change, Remove, Save) submit to the focal_points controller
|
|
8
|
+
to avoid nested form issues.
|
|
9
|
+
|
|
10
|
+
Required locals:
|
|
11
|
+
record - The record with HasFocalPoint concern
|
|
12
|
+
attachment_name - Symbol for the attachment (e.g., :header_image)
|
|
13
|
+
|
|
14
|
+
Optional locals:
|
|
15
|
+
label - Section label (default: "Main Image")
|
|
16
|
+
contexts - Hash of context configs for previews
|
|
17
|
+
error_message - Error message to display
|
|
18
|
+
%>
|
|
19
|
+
|
|
20
|
+
<% label ||= "Main Image" %>
|
|
21
|
+
<% contexts ||= Railspress.image_contexts %>
|
|
22
|
+
<% error_message ||= nil %>
|
|
23
|
+
<% attachment = record.send(attachment_name) %>
|
|
24
|
+
<% focal_point = record.focal_point(attachment_name) %>
|
|
25
|
+
<% focal_point_record = record.send("#{attachment_name}_focal_point") %>
|
|
26
|
+
<% frame_id = dom_id(record, "image_section_#{attachment_name}") %>
|
|
27
|
+
|
|
28
|
+
<%# Build cancel URL based on record type %>
|
|
29
|
+
<%
|
|
30
|
+
if record.is_a?(Railspress::Post)
|
|
31
|
+
cancel_url = image_editor_admin_post_path(record, attachment: attachment_name, compact: true)
|
|
32
|
+
elsif record.is_a?(Railspress::ContentElement)
|
|
33
|
+
cancel_url = image_editor_admin_content_element_path(record, compact: true)
|
|
34
|
+
else
|
|
35
|
+
entity_type = record.class.railspress_config.route_key
|
|
36
|
+
cancel_url = admin_entity_image_editor_path(entity_type: entity_type, id: record.id, attachment: attachment_name, compact: true)
|
|
37
|
+
end
|
|
38
|
+
%>
|
|
39
|
+
|
|
40
|
+
<%= content_tag "turbo-frame", id: frame_id do %>
|
|
41
|
+
<div class="rp-image-section">
|
|
42
|
+
<div class="rp-image-section__label">
|
|
43
|
+
<label class="rp-label"><%= label %></label>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<% if error_message %>
|
|
47
|
+
<div class="rp-flash rp-flash--error" style="margin-bottom: var(--rp-space-sm);">
|
|
48
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
|
|
49
|
+
<circle cx="12" cy="12" r="10"/>
|
|
50
|
+
<line x1="15" y1="9" x2="9" y2="15"/>
|
|
51
|
+
<line x1="9" y1="9" x2="15" y2="15"/>
|
|
52
|
+
</svg>
|
|
53
|
+
<%= error_message %>
|
|
54
|
+
</div>
|
|
55
|
+
<% end %>
|
|
56
|
+
|
|
57
|
+
<div class="rp-image-section__details">
|
|
58
|
+
<%# Single form for all actions - submits to focal_points controller %>
|
|
59
|
+
<%= form_with model: focal_point_record,
|
|
60
|
+
url: admin_focal_point_path(focal_point_record),
|
|
61
|
+
method: :patch,
|
|
62
|
+
multipart: true,
|
|
63
|
+
data: {
|
|
64
|
+
controller: "railspress--focal-point",
|
|
65
|
+
railspress__focal_point_x_value: focal_point[:x],
|
|
66
|
+
railspress__focal_point_y_value: focal_point[:y]
|
|
67
|
+
},
|
|
68
|
+
class: "rp-image-section__editor" do |form| %>
|
|
69
|
+
|
|
70
|
+
<div class="rp-image-section__editor-header">
|
|
71
|
+
<span class="rp-image-section__editor-title"><%= label %> Settings</span>
|
|
72
|
+
<div class="rp-image-section__editor-actions">
|
|
73
|
+
<%# Change image - file input that auto-submits %>
|
|
74
|
+
<label class="rp-btn rp-btn--outline rp-btn--sm">
|
|
75
|
+
Change
|
|
76
|
+
<%= file_field_tag :image, accept: "image/*", class: "rp-sr-only", onchange: "this.form.submit()" %>
|
|
77
|
+
</label>
|
|
78
|
+
|
|
79
|
+
<%# Remove image - button that sets hidden field and submits %>
|
|
80
|
+
<button type="button"
|
|
81
|
+
class="rp-btn rp-btn--outline rp-btn--sm rp-btn--danger"
|
|
82
|
+
onclick="if(confirm('Remove this image?')) { this.form.querySelector('[name=remove_image]').value = '1'; this.form.submit(); }">
|
|
83
|
+
Remove
|
|
84
|
+
</button>
|
|
85
|
+
<%= hidden_field_tag :remove_image, "0" %>
|
|
86
|
+
|
|
87
|
+
<%= link_to "Cancel", cancel_url,
|
|
88
|
+
class: "rp-btn rp-btn--outline rp-btn--sm",
|
|
89
|
+
data: { turbo_frame: frame_id } %>
|
|
90
|
+
<%= form.submit "Save",
|
|
91
|
+
class: "rp-btn rp-btn--primary rp-btn--sm" %>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="rp-image-section__editor-body">
|
|
96
|
+
<div class="rp-focal-editor__layout">
|
|
97
|
+
<%# Source image with crosshair %>
|
|
98
|
+
<div class="rp-focal-editor__source">
|
|
99
|
+
<div class="rp-focal-editor__image-wrap"
|
|
100
|
+
data-action="click->railspress--focal-point#pick
|
|
101
|
+
touchstart->railspress--focal-point#touch
|
|
102
|
+
keydown->railspress--focal-point#keydown"
|
|
103
|
+
tabindex="0">
|
|
104
|
+
<% if attachment.attached? %>
|
|
105
|
+
<%= image_tag main_app.url_for(attachment.variant(resize_to_limit: [1200, 800])),
|
|
106
|
+
data: { railspress__focal_point_target: "image" },
|
|
107
|
+
class: "rp-focal-editor__image",
|
|
108
|
+
alt: "Source image for focal point selection" %>
|
|
109
|
+
<% end %>
|
|
110
|
+
<div data-railspress--focal-point-target="crosshair"
|
|
111
|
+
class="rp-focal-editor__crosshair"
|
|
112
|
+
style="left: <%= focal_point[:x] * 100 %>%; top: <%= focal_point[:y] * 100 %>%;"></div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="rp-focal-editor__hint">
|
|
116
|
+
<span>Click to set focal point</span>
|
|
117
|
+
<span class="rp-focal-editor__coords" data-railspress--focal-point-target="coordsDisplay">
|
|
118
|
+
<%= "#{(focal_point[:x] * 100).round}%, #{(focal_point[:y] * 100).round}%" %>
|
|
119
|
+
</span>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="rp-focal-editor__actions">
|
|
123
|
+
<button type="button"
|
|
124
|
+
class="rp-btn rp-btn--outline rp-btn--sm"
|
|
125
|
+
data-action="railspress--focal-point#reset">
|
|
126
|
+
Reset to Center
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<%# Live previews %>
|
|
132
|
+
<% if contexts.any? %>
|
|
133
|
+
<div class="rp-focal-editor__previews">
|
|
134
|
+
<span class="rp-focal-editor__previews-title">Live Preview</span>
|
|
135
|
+
|
|
136
|
+
<% contexts.each do |name, config| %>
|
|
137
|
+
<% aspect = config[:aspect] %>
|
|
138
|
+
<% aspect_ratio = aspect.is_a?(Array) ? "#{aspect[0]} / #{aspect[1]}" : aspect %>
|
|
139
|
+
<% ratio_label = aspect.is_a?(Array) ? "#{aspect[0]}:#{aspect[1]}" : aspect.to_s %>
|
|
140
|
+
|
|
141
|
+
<div class="rp-focal-editor__preview">
|
|
142
|
+
<div class="rp-focal-editor__preview-image" style="aspect-ratio: <%= aspect_ratio %>;">
|
|
143
|
+
<% if attachment.attached? %>
|
|
144
|
+
<%= image_tag main_app.url_for(attachment.variant(resize_to_limit: [400, 300])),
|
|
145
|
+
data: { railspress__focal_point_target: "preview" },
|
|
146
|
+
style: record.image_css_for(name, attachment_name),
|
|
147
|
+
alt: "#{name.to_s.titleize} preview" %>
|
|
148
|
+
<% end %>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="rp-focal-editor__preview-label">
|
|
151
|
+
<span class="rp-focal-editor__preview-name"><%= name.to_s.titleize %></span>
|
|
152
|
+
<span class="rp-focal-editor__preview-ratio"><%= ratio_label %></span>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<% end %>
|
|
156
|
+
</div>
|
|
157
|
+
<% end %>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<%# Hidden inputs for focal point values %>
|
|
161
|
+
<%= form.hidden_field :focal_x,
|
|
162
|
+
value: focal_point[:x],
|
|
163
|
+
data: { railspress__focal_point_target: "xInput" } %>
|
|
164
|
+
<%= form.hidden_field :focal_y,
|
|
165
|
+
value: focal_point[:y],
|
|
166
|
+
data: { railspress__focal_point_target: "yInput" } %>
|
|
167
|
+
</div>
|
|
168
|
+
<% end %>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<% end %>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Image Section V2 Partial
|
|
3
|
+
========================
|
|
4
|
+
Improved image section with Stimulus-powered expand/collapse.
|
|
5
|
+
Located in main column (not sidebar) with better UX.
|
|
6
|
+
|
|
7
|
+
Required locals:
|
|
8
|
+
has_image - Boolean, whether an image is attached
|
|
9
|
+
|
|
10
|
+
Optional locals (when has_image is true):
|
|
11
|
+
image_url - URL of the image to display
|
|
12
|
+
filename - Original filename
|
|
13
|
+
filesize - Human-readable file size
|
|
14
|
+
focal_point - Hash with :x and :y (0.0-1.0)
|
|
15
|
+
contexts - Hash of context configs for previews
|
|
16
|
+
|
|
17
|
+
Optional locals (for form integration):
|
|
18
|
+
form - Rails form builder (enables form fields)
|
|
19
|
+
record - The record with HasFocalPoint concern
|
|
20
|
+
attachment_name - Symbol for the attachment (e.g., :header_image)
|
|
21
|
+
|
|
22
|
+
Optional locals (always):
|
|
23
|
+
label - Section label (default: "Main Image")
|
|
24
|
+
%>
|
|
25
|
+
|
|
26
|
+
<% label ||= "Main Image" %>
|
|
27
|
+
<% contexts ||= {} %>
|
|
28
|
+
<% focal_point ||= { x: 0.5, y: 0.5 } %>
|
|
29
|
+
<% has_form = local_assigns[:form].present? %>
|
|
30
|
+
<% attachment_name ||= :header_image %>
|
|
31
|
+
<% focal_point_record = has_form && local_assigns[:record] ? record.send("#{attachment_name}_focal_point") : nil %>
|
|
32
|
+
|
|
33
|
+
<div class="rp-image-section-v2"
|
|
34
|
+
data-controller="railspress--image-section"
|
|
35
|
+
data-railspress--image-section-expanded-value="false">
|
|
36
|
+
|
|
37
|
+
<%# Header row with label and action %>
|
|
38
|
+
<div class="rp-image-section-v2__header">
|
|
39
|
+
<label class="rp-label"><%= label %></label>
|
|
40
|
+
<% if local_assigns[:has_image] %>
|
|
41
|
+
<button type="button"
|
|
42
|
+
class="rp-btn rp-btn--outline rp-btn--sm"
|
|
43
|
+
data-railspress--image-section-target="editButton"
|
|
44
|
+
data-action="railspress--image-section#toggle">
|
|
45
|
+
Edit
|
|
46
|
+
</button>
|
|
47
|
+
<% end %>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<% if local_assigns[:has_image] %>
|
|
51
|
+
<%# Compact view (shown when collapsed) %>
|
|
52
|
+
<div class="rp-image-section-v2__compact"
|
|
53
|
+
data-railspress--image-section-target="compact"
|
|
54
|
+
data-action="click->railspress--image-section#expand">
|
|
55
|
+
<div class="rp-image-section-v2__thumb">
|
|
56
|
+
<img src="<%= image_url %>"
|
|
57
|
+
style="object-position: <%= focal_point[:x] * 100 %>% <%= focal_point[:y] * 100 %>%;"
|
|
58
|
+
alt="">
|
|
59
|
+
</div>
|
|
60
|
+
<div class="rp-image-section-v2__info">
|
|
61
|
+
<span class="rp-image-section-v2__filename"><%= filename %></span>
|
|
62
|
+
<span class="rp-image-section-v2__meta"><%= filesize %></span>
|
|
63
|
+
<% if focal_point[:x] != 0.5 || focal_point[:y] != 0.5 %>
|
|
64
|
+
<span class="rp-image-section-v2__focal-indicator">
|
|
65
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
66
|
+
<circle cx="12" cy="12" r="3"/>
|
|
67
|
+
<path d="M12 2v4m0 12v4m10-10h-4M6 12H2"/>
|
|
68
|
+
</svg>
|
|
69
|
+
Focal point set
|
|
70
|
+
</span>
|
|
71
|
+
<% end %>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<%# Expanded editor (hidden by default) %>
|
|
76
|
+
<div class="rp-image-section-v2__editor"
|
|
77
|
+
data-railspress--image-section-target="editor"
|
|
78
|
+
hidden>
|
|
79
|
+
<div class="rp-image-section-v2__editor-header">
|
|
80
|
+
<span class="rp-image-section-v2__editor-title"><%= label %> Settings</span>
|
|
81
|
+
<div class="rp-image-section-v2__editor-actions">
|
|
82
|
+
<% if has_form %>
|
|
83
|
+
<label class="rp-btn rp-btn--outline rp-btn--sm">
|
|
84
|
+
Change
|
|
85
|
+
<%= form.file_field attachment_name,
|
|
86
|
+
accept: "image/*",
|
|
87
|
+
class: "rp-sr-only",
|
|
88
|
+
direct_upload: true %>
|
|
89
|
+
</label>
|
|
90
|
+
<label class="rp-btn rp-btn--outline rp-btn--sm">
|
|
91
|
+
<%= form.check_box :"remove_#{attachment_name}", { class: "rp-sr-only" }, "1", "0" %>
|
|
92
|
+
Remove
|
|
93
|
+
</label>
|
|
94
|
+
<% else %>
|
|
95
|
+
<button type="button" class="rp-btn rp-btn--outline rp-btn--sm">Change</button>
|
|
96
|
+
<button type="button" class="rp-btn rp-btn--outline rp-btn--sm">Remove</button>
|
|
97
|
+
<% end %>
|
|
98
|
+
<button type="button"
|
|
99
|
+
class="rp-btn rp-btn--primary rp-btn--sm"
|
|
100
|
+
data-action="railspress--image-section#collapse">
|
|
101
|
+
Done
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="rp-image-section-v2__editor-body">
|
|
107
|
+
<%# Focal point editor %>
|
|
108
|
+
<div class="rp-focal-editor"
|
|
109
|
+
data-controller="railspress--focal-point"
|
|
110
|
+
data-railspress--focal-point-x-value="<%= focal_point[:x] %>"
|
|
111
|
+
data-railspress--focal-point-y-value="<%= focal_point[:y] %>">
|
|
112
|
+
|
|
113
|
+
<div class="rp-focal-editor__layout">
|
|
114
|
+
<%# Source image with crosshair %>
|
|
115
|
+
<div class="rp-focal-editor__source">
|
|
116
|
+
<div class="rp-focal-editor__image-wrap"
|
|
117
|
+
data-action="click->railspress--focal-point#pick
|
|
118
|
+
touchstart->railspress--focal-point#touch"
|
|
119
|
+
tabindex="0"
|
|
120
|
+
data-action="keydown->railspress--focal-point#keydown">
|
|
121
|
+
<img src="<%= image_url %>"
|
|
122
|
+
data-railspress--focal-point-target="image"
|
|
123
|
+
class="rp-focal-editor__image"
|
|
124
|
+
alt="Source image for focal point selection">
|
|
125
|
+
<div data-railspress--focal-point-target="crosshair"
|
|
126
|
+
class="rp-focal-editor__crosshair"
|
|
127
|
+
style="left: <%= focal_point[:x] * 100 %>%; top: <%= focal_point[:y] * 100 %>%;"></div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="rp-focal-editor__hint">
|
|
131
|
+
<span>Click to set focal point</span>
|
|
132
|
+
<span class="rp-focal-editor__coords" data-railspress--focal-point-target="coordsDisplay">
|
|
133
|
+
<%= "#{(focal_point[:x] * 100).round}%, #{(focal_point[:y] * 100).round}%" %>
|
|
134
|
+
</span>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="rp-focal-editor__actions">
|
|
138
|
+
<button type="button"
|
|
139
|
+
class="rp-btn rp-btn--outline rp-btn--sm"
|
|
140
|
+
data-action="railspress--focal-point#reset">
|
|
141
|
+
Reset to Center
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<%# Live previews %>
|
|
147
|
+
<% if contexts.any? %>
|
|
148
|
+
<div class="rp-focal-editor__previews">
|
|
149
|
+
<span class="rp-focal-editor__previews-title">Live Preview</span>
|
|
150
|
+
|
|
151
|
+
<% contexts.each do |name, config| %>
|
|
152
|
+
<% aspect = config[:aspect] %>
|
|
153
|
+
<% aspect_ratio = aspect.is_a?(Array) ? "#{aspect[0]} / #{aspect[1]}" : aspect %>
|
|
154
|
+
<% ratio_label = aspect.is_a?(Array) ? "#{aspect[0]}:#{aspect[1]}" : aspect.to_s %>
|
|
155
|
+
|
|
156
|
+
<div class="rp-focal-editor__preview">
|
|
157
|
+
<div class="rp-focal-editor__preview-image" style="aspect-ratio: <%= aspect_ratio %>;">
|
|
158
|
+
<img src="<%= image_url %>"
|
|
159
|
+
data-railspress--focal-point-target="preview"
|
|
160
|
+
style="object-position: <%= focal_point[:x] * 100 %>% <%= focal_point[:y] * 100 %>%;"
|
|
161
|
+
alt="<%= name.to_s.titleize %> preview">
|
|
162
|
+
</div>
|
|
163
|
+
<div class="rp-focal-editor__preview-label">
|
|
164
|
+
<span class="rp-focal-editor__preview-name"><%= name.to_s.titleize %></span>
|
|
165
|
+
<span class="rp-focal-editor__preview-ratio"><%= ratio_label %></span>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<% end %>
|
|
169
|
+
</div>
|
|
170
|
+
<% end %>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<%# Hidden inputs for form submission %>
|
|
174
|
+
<% if has_form && focal_point_record %>
|
|
175
|
+
<%= form.fields_for "#{attachment_name}_focal_point_attributes", focal_point_record do |fp_form| %>
|
|
176
|
+
<%= fp_form.hidden_field :focal_x, data: { railspress__focal_point_target: "inputX" } %>
|
|
177
|
+
<%= fp_form.hidden_field :focal_y, data: { railspress__focal_point_target: "inputY" } %>
|
|
178
|
+
<% end %>
|
|
179
|
+
<% end %>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<% else %>
|
|
185
|
+
<%# No image - show dropzone %>
|
|
186
|
+
<% if has_form %>
|
|
187
|
+
<%= render "railspress/admin/shared/dropzone",
|
|
188
|
+
form: form,
|
|
189
|
+
field_name: attachment_name,
|
|
190
|
+
prompt: "Click to upload #{label.downcase}" %>
|
|
191
|
+
<% else %>
|
|
192
|
+
<div class="rp-image-section-v2__dropzone">
|
|
193
|
+
<svg class="rp-image-section-v2__dropzone-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
194
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
195
|
+
<circle cx="8.5" cy="8.5" r="1.5"/>
|
|
196
|
+
<polyline points="21 15 16 10 5 21"/>
|
|
197
|
+
</svg>
|
|
198
|
+
<p class="rp-image-section-v2__dropzone-text">
|
|
199
|
+
<strong>Click to upload</strong> or drag and drop<br>
|
|
200
|
+
<span class="rp-hint">PNG, JPG, WebP up to 10MB</span>
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
<% end %>
|
|
204
|
+
<% end %>
|
|
205
|
+
</div>
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
<div class="rp-sidebar-content">
|
|
2
|
-
<
|
|
2
|
+
<div class="rp-sidebar-header">
|
|
3
|
+
<h1 class="rp-logo">RailsPress</h1>
|
|
4
|
+
<button class="rp-sidebar-toggle" aria-label="Toggle sidebar">
|
|
5
|
+
<svg class="rp-sidebar-toggle-icon rp-sidebar-toggle-icon--collapse" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
6
|
+
<polyline points="11 17 6 12 11 7"/>
|
|
7
|
+
<polyline points="18 17 13 12 18 7"/>
|
|
8
|
+
</svg>
|
|
9
|
+
<svg class="rp-sidebar-toggle-icon rp-sidebar-toggle-icon--expand" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
10
|
+
<polyline points="13 17 18 12 13 7"/>
|
|
11
|
+
<polyline points="6 17 11 12 6 7"/>
|
|
12
|
+
</svg>
|
|
13
|
+
</button>
|
|
14
|
+
</div>
|
|
3
15
|
|
|
4
16
|
<nav class="rp-nav">
|
|
5
17
|
<%= link_to railspress.admin_root_path, class: "rp-nav-link #{current_page?(railspress.admin_root_path) ? 'rp-nav-link--active' : ''}" do %>
|
|
@@ -7,7 +19,7 @@
|
|
|
7
19
|
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
|
8
20
|
<polyline points="9 22 9 12 15 12 15 22"/>
|
|
9
21
|
</svg>
|
|
10
|
-
Dashboard
|
|
22
|
+
<span class="rp-nav-text">Dashboard</span>
|
|
11
23
|
<% end %>
|
|
12
24
|
|
|
13
25
|
<%= link_to railspress.admin_posts_path, class: "rp-nav-link #{controller_name == 'posts' ? 'rp-nav-link--active' : ''}" do %>
|
|
@@ -17,14 +29,14 @@
|
|
|
17
29
|
<line x1="16" y1="13" x2="8" y2="13"/>
|
|
18
30
|
<line x1="16" y1="17" x2="8" y2="17"/>
|
|
19
31
|
</svg>
|
|
20
|
-
Posts
|
|
32
|
+
<span class="rp-nav-text">Posts</span>
|
|
21
33
|
<% end %>
|
|
22
34
|
|
|
23
35
|
<%= link_to railspress.admin_categories_path, class: "rp-nav-link #{controller_name == 'categories' ? 'rp-nav-link--active' : ''}" do %>
|
|
24
36
|
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
25
37
|
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
|
26
38
|
</svg>
|
|
27
|
-
Categories
|
|
39
|
+
<span class="rp-nav-text">Categories</span>
|
|
28
40
|
<% end %>
|
|
29
41
|
|
|
30
42
|
<%= link_to railspress.admin_tags_path, class: "rp-nav-link #{controller_name == 'tags' ? 'rp-nav-link--active' : ''}" do %>
|
|
@@ -32,7 +44,63 @@
|
|
|
32
44
|
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/>
|
|
33
45
|
<line x1="7" y1="7" x2="7.01" y2="7"/>
|
|
34
46
|
</svg>
|
|
35
|
-
Tags
|
|
47
|
+
<span class="rp-nav-text">Tags</span>
|
|
48
|
+
<% end %>
|
|
49
|
+
|
|
50
|
+
<% if Railspress.cms_enabled? %>
|
|
51
|
+
<div class="rp-nav-divider"></div>
|
|
52
|
+
|
|
53
|
+
<%= link_to railspress.admin_content_groups_path, class: "rp-nav-link #{controller_name == 'content_groups' ? 'rp-nav-link--active' : ''}" do %>
|
|
54
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
55
|
+
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/>
|
|
56
|
+
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"/>
|
|
57
|
+
</svg>
|
|
58
|
+
<span class="rp-nav-text">Content Groups</span>
|
|
59
|
+
<% end %>
|
|
60
|
+
|
|
61
|
+
<%= link_to railspress.admin_content_elements_path, class: "rp-nav-link #{controller_name == 'content_elements' ? 'rp-nav-link--active' : ''}" do %>
|
|
62
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
63
|
+
<polyline points="4 7 4 4 20 4 20 7"/>
|
|
64
|
+
<line x1="9" y1="20" x2="15" y2="20"/>
|
|
65
|
+
<line x1="12" y1="4" x2="12" y2="20"/>
|
|
66
|
+
</svg>
|
|
67
|
+
<span class="rp-nav-text">Content Elements</span>
|
|
68
|
+
<% end %>
|
|
69
|
+
|
|
70
|
+
<%= link_to railspress.admin_cms_transfer_path, class: "rp-nav-link #{controller_name == 'cms_transfers' ? 'rp-nav-link--active' : ''}" do %>
|
|
71
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
72
|
+
<path d="M12 3v6m0 0l3-3m-3 3L9 6"/>
|
|
73
|
+
<path d="M12 21v-6m0 0l3 3m-3-3l-3 3"/>
|
|
74
|
+
<rect x="3" y="9" width="18" height="6" rx="2"/>
|
|
75
|
+
</svg>
|
|
76
|
+
<span class="rp-nav-text">CMS Transfer</span>
|
|
77
|
+
<% end %>
|
|
78
|
+
<% end %>
|
|
79
|
+
|
|
80
|
+
<% if Railspress.registered_entities.any? %>
|
|
81
|
+
<div class="rp-nav-divider"></div>
|
|
82
|
+
|
|
83
|
+
<% Railspress.registered_entities.each do |route_key, config| %>
|
|
84
|
+
<%= link_to railspress.admin_entity_index_path(entity_type: route_key),
|
|
85
|
+
class: "rp-nav-link #{controller_name == 'entities' && params[:entity_type] == route_key ? 'rp-nav-link--active' : ''}" do %>
|
|
86
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
87
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
88
|
+
<line x1="3" y1="9" x2="21" y2="9"/>
|
|
89
|
+
<line x1="9" y1="21" x2="9" y2="9"/>
|
|
90
|
+
</svg>
|
|
91
|
+
<span class="rp-nav-text"><%= config.label %></span>
|
|
92
|
+
<% end %>
|
|
93
|
+
<% end %>
|
|
94
|
+
<% end %>
|
|
95
|
+
|
|
96
|
+
<div class="rp-nav-divider"></div>
|
|
97
|
+
<%= link_to "/", class: "rp-nav-link" do %>
|
|
98
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
99
|
+
<circle cx="12" cy="12" r="10"/>
|
|
100
|
+
<line x1="2" y1="12" x2="22" y2="12"/>
|
|
101
|
+
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
|
|
102
|
+
</svg>
|
|
103
|
+
<span class="rp-nav-text">Main Site</span>
|
|
36
104
|
<% end %>
|
|
37
105
|
</nav>
|
|
38
106
|
</div>
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
<% content_for :title, "Tags" %>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<h1 class="rp-page-title">Tags</h1>
|
|
5
|
-
<div class="rp-page-actions">
|
|
6
|
-
<%= link_to "New Tag", new_admin_tag_path, class: "rp-btn rp-btn--primary" %>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
3
|
+
<%= rp_page_header "Tags", "New Tag" => new_admin_tag_path %>
|
|
9
4
|
|
|
10
5
|
<div class="rp-card">
|
|
11
6
|
<% if @tags.any? %>
|
|
@@ -13,10 +8,10 @@
|
|
|
13
8
|
<table class="rp-table">
|
|
14
9
|
<thead>
|
|
15
10
|
<tr>
|
|
16
|
-
<th
|
|
17
|
-
<th
|
|
18
|
-
<th
|
|
19
|
-
<th class="rp-table-actions"
|
|
11
|
+
<th><%= rp_table_header("Name") %></th>
|
|
12
|
+
<th><%= rp_table_header("Slug") %></th>
|
|
13
|
+
<th><%= rp_table_header("Usage") %></th>
|
|
14
|
+
<th class="rp-table-actions"><%= rp_table_header("Actions") %></th>
|
|
20
15
|
</tr>
|
|
21
16
|
</thead>
|
|
22
17
|
<tbody>
|
|
@@ -24,12 +19,13 @@
|
|
|
24
19
|
<tr>
|
|
25
20
|
<td class="rp-table-primary"><%= tag.name %></td>
|
|
26
21
|
<td class="rp-table-secondary"><%= tag.slug %></td>
|
|
27
|
-
<td><%= tag.
|
|
22
|
+
<td><%= tag.taggings.count %></td>
|
|
28
23
|
<td class="rp-table-actions">
|
|
29
|
-
<%=
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
<%= rp_table_action_icons(
|
|
25
|
+
edit_path: edit_admin_tag_path(tag),
|
|
26
|
+
delete_path: admin_tag_path(tag),
|
|
27
|
+
confirm: "Delete this tag?"
|
|
28
|
+
) %>
|
|
33
29
|
</td>
|
|
34
30
|
</tr>
|
|
35
31
|
<% end %>
|
|
@@ -37,6 +33,6 @@
|
|
|
37
33
|
</table>
|
|
38
34
|
</div>
|
|
39
35
|
<% else %>
|
|
40
|
-
|
|
36
|
+
<%= rp_empty_state "No tags yet.", link_text: "Create your first tag", link_path: new_admin_tag_path %>
|
|
41
37
|
<% end %>
|
|
42
38
|
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ignored_warnings": [
|
|
3
|
+
{
|
|
4
|
+
"fingerprint": "63b49bb226efb52aff46bf077f8feded65c3d627b29de6ee403b37d2b08cfe23",
|
|
5
|
+
"note": "Attachment name is validated against entity_config.fields before use"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"fingerprint": "bb7ad80d40c0237746aced9b06bdfc144d6e3052b53647f09b1738005326de38",
|
|
9
|
+
"note": "Attachment name is validated in entities_controller#image_editor before rendering this partial"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"fingerprint": "1d94979921177aefb3064de87b6032a5f2760ef30946a52e3015b54af6150e35",
|
|
13
|
+
"note": "Attachment name is validated in entities_controller#image_editor before rendering this partial"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"updated": "2026-02-14",
|
|
17
|
+
"brakeman_version": "8.0.2"
|
|
18
|
+
}
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# RailsPress Engine Importmap Configuration
|
|
2
|
+
#
|
|
3
|
+
# This file is automatically loaded by host applications using importmap-rails.
|
|
4
|
+
#
|
|
5
|
+
# Usage in host app (add to app/javascript/application.js):
|
|
6
|
+
#
|
|
7
|
+
# import "railspress"
|
|
8
|
+
#
|
|
9
|
+
# This auto-registers all RailsPress Stimulus controllers.
|
|
10
|
+
|
|
11
|
+
# Lexxy rich text editor (hard dependency — auto-pinned so host apps don't need to)
|
|
12
|
+
pin "lexxy", to: "lexxy.js"
|
|
13
|
+
|
|
14
|
+
# Turbo for Turbo Frames support
|
|
15
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
16
|
+
|
|
17
|
+
# Main entry point - auto-registers all controllers
|
|
18
|
+
pin "railspress", to: "railspress/index.js"
|
|
19
|
+
|
|
20
|
+
# Individual controllers (for advanced use cases)
|
|
21
|
+
pin_all_from Railspress::Engine.root.join("app/javascript/railspress/controllers"),
|
|
22
|
+
under: "railspress/controllers",
|
|
23
|
+
to: "railspress/controllers"
|
data/config/routes.rb
CHANGED
|
@@ -4,6 +4,67 @@ Railspress::Engine.routes.draw do
|
|
|
4
4
|
root "dashboard#index"
|
|
5
5
|
resources :categories, except: [ :show ]
|
|
6
6
|
resources :tags, except: [ :show ]
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
# Content Element CMS (opt-in via config.enable_cms)
|
|
9
|
+
if Railspress.cms_enabled?
|
|
10
|
+
resources :content_groups
|
|
11
|
+
resources :content_elements do
|
|
12
|
+
member do
|
|
13
|
+
get :inline
|
|
14
|
+
get :image_editor
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
resources :content_element_versions, only: [ :show ]
|
|
18
|
+
|
|
19
|
+
# CMS Content Transfer (export/import)
|
|
20
|
+
resource :cms_transfer, only: [ :show ] do
|
|
21
|
+
post :export, on: :member
|
|
22
|
+
post :import, on: :member
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
resources :posts do
|
|
27
|
+
member do
|
|
28
|
+
get "image_editor/:attachment", action: :image_editor, as: :image_editor
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Standalone focal point updates (works outside parent form)
|
|
33
|
+
resources :focal_points, only: [ :update ]
|
|
34
|
+
resources :imports, only: [ :create ] do
|
|
35
|
+
collection do
|
|
36
|
+
get ":type", action: :show, as: :typed
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
resources :exports, only: [ :create ] do
|
|
40
|
+
collection do
|
|
41
|
+
get ":type", action: :show, as: :typed
|
|
42
|
+
end
|
|
43
|
+
member do
|
|
44
|
+
get :download
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# UI/UX Prototypes (development only)
|
|
49
|
+
resources :prototypes, only: [] do
|
|
50
|
+
collection do
|
|
51
|
+
get :image_section
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Dynamic entity routes for host-defined CMS models
|
|
56
|
+
scope "entities" do
|
|
57
|
+
scope ":entity_type", constraints: ->(req) { Railspress.entity_registered?(req.params[:entity_type]) } do
|
|
58
|
+
get "/", to: "entities#index", as: :entity_index
|
|
59
|
+
get "/new", to: "entities#new", as: :new_entity
|
|
60
|
+
post "/", to: "entities#create"
|
|
61
|
+
get "/:id", to: "entities#show", as: :entity
|
|
62
|
+
get "/:id/edit", to: "entities#edit", as: :edit_entity
|
|
63
|
+
get "/:id/image_editor/:attachment", to: "entities#image_editor", as: :entity_image_editor
|
|
64
|
+
patch "/:id", to: "entities#update"
|
|
65
|
+
put "/:id", to: "entities#update"
|
|
66
|
+
delete "/:id", to: "entities#destroy"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
8
69
|
end
|
|
9
70
|
end
|