iron-cms 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/builds/iron.css +668 -388
- data/app/assets/tailwind/iron/application.css +1 -0
- data/app/assets/tailwind/iron/components/button.css +0 -7
- data/app/assets/tailwind/iron/components/checkbox.css +21 -0
- data/app/assets/tailwind/iron/components/form.css +1 -1
- data/app/assets/tailwind/iron/lexxy.css +165 -51
- data/app/controllers/iron/account/exports_controller.rb +26 -0
- data/app/controllers/iron/account/imports_controller.rb +27 -0
- data/app/helpers/iron/form_builder.rb +7 -0
- data/app/jobs/iron/export_job.rb +9 -0
- data/app/jobs/iron/import_job.rb +9 -0
- data/app/models/concerns/iron/broadcastable.rb +9 -0
- data/app/models/concerns/iron/processable.rb +34 -0
- data/app/models/iron/account/export.rb +86 -0
- data/app/models/iron/account/import.rb +208 -0
- data/app/models/iron/block_definition/exportable.rb +14 -0
- data/app/models/iron/block_definition/importable.rb +27 -0
- data/app/models/iron/block_definition.rb +1 -1
- data/app/models/iron/content_type/exportable.rb +20 -0
- data/app/models/iron/content_type/importable.rb +32 -0
- data/app/models/iron/content_type.rb +1 -1
- data/app/models/iron/current.rb +6 -3
- data/app/models/iron/entry/exportable.rb +49 -0
- data/app/models/iron/entry/importable.rb +181 -0
- data/app/models/iron/entry.rb +1 -1
- data/app/models/iron/field.rb +9 -1
- data/app/models/iron/field_definition/exportable.rb +23 -0
- data/app/models/iron/field_definition/importable.rb +39 -0
- data/app/models/iron/field_definition.rb +1 -1
- data/app/models/iron/fields/block.rb +18 -0
- data/app/models/iron/fields/block_list.rb +8 -0
- data/app/models/iron/fields/boolean.rb +4 -0
- data/app/models/iron/fields/date.rb +4 -0
- data/app/models/iron/fields/file.rb +16 -0
- data/app/models/iron/fields/number.rb +4 -0
- data/app/models/iron/fields/reference.rb +4 -0
- data/app/models/iron/fields/reference_list.rb +4 -0
- data/app/models/iron/fields/rich_text_area.rb +32 -0
- data/app/models/iron/fields/text_area.rb +4 -0
- data/app/models/iron/fields/text_field.rb +4 -0
- data/app/models/iron/user.rb +2 -0
- data/app/views/iron/account/exports/index.html.erb +43 -0
- data/app/views/iron/account/exports/new.html.erb +39 -0
- data/app/views/iron/account/exports/show.html.erb +40 -0
- data/app/views/iron/account/imports/index.html.erb +43 -0
- data/app/views/iron/account/imports/new.html.erb +52 -0
- data/app/views/iron/account/imports/show.html.erb +37 -0
- data/app/views/iron/content_types/index.html.erb +1 -8
- data/app/views/iron/entries/fields/_file.html.erb +3 -3
- data/app/views/iron/settings/show.html.erb +4 -11
- data/config/routes.rb +3 -9
- data/db/migrate/20251209103109_create_iron_account_exports.rb +13 -0
- data/db/migrate/20251209103110_create_iron_account_imports.rb +13 -0
- data/lib/iron/version.rb +1 -1
- data/lib/iron.rb +1 -1
- metadata +40 -28
- data/app/controllers/iron/contents_controller.rb +0 -33
- data/app/controllers/iron/schemas_controller.rb +0 -32
- data/app/models/concerns/iron/csv_serializable.rb +0 -28
- data/app/models/iron/archive.rb +0 -69
- data/app/models/iron/block_definition/portable.rb +0 -20
- data/app/models/iron/content_export.rb +0 -73
- data/app/models/iron/content_import/entry_builder.rb +0 -80
- data/app/models/iron/content_import/entry_snapshot.rb +0 -23
- data/app/models/iron/content_import/field_reconstructor.rb +0 -276
- data/app/models/iron/content_import/field_snapshot.rb +0 -33
- data/app/models/iron/content_import/registry.rb +0 -32
- data/app/models/iron/content_import/session.rb +0 -89
- data/app/models/iron/content_import.rb +0 -15
- data/app/models/iron/content_type/portable.rb +0 -30
- data/app/models/iron/entry/portable.rb +0 -35
- data/app/models/iron/field/portable.rb +0 -33
- data/app/models/iron/field_definition/portable.rb +0 -42
- data/app/models/iron/schema_archive.rb +0 -71
- data/app/models/iron/schema_exporter.rb +0 -15
- data/app/models/iron/schema_importer/import_strategy.rb +0 -59
- data/app/models/iron/schema_importer/merge_strategy.rb +0 -52
- data/app/models/iron/schema_importer/replace_strategy.rb +0 -51
- data/app/models/iron/schema_importer/safe_strategy.rb +0 -55
- data/app/models/iron/schema_importer.rb +0 -108
- data/app/views/iron/contents/new.html.erb +0 -34
- data/app/views/iron/schemas/new.html.erb +0 -57
- data/lib/iron/test_fixtures.rb +0 -50
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@utility checkbox {
|
|
2
|
+
@apply grid size-4 grid-cols-1;
|
|
3
|
+
|
|
4
|
+
& input {
|
|
5
|
+
@apply col-start-1 row-start-1 appearance-none rounded-sm border border-stone-300 bg-white;
|
|
6
|
+
@apply checked:border-sky-600 checked:bg-sky-600;
|
|
7
|
+
@apply focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600;
|
|
8
|
+
@apply disabled:border-stone-300 disabled:bg-stone-100 disabled:checked:bg-stone-100;
|
|
9
|
+
@apply dark:border-white/10 dark:bg-white/5;
|
|
10
|
+
@apply dark:checked:border-sky-500 dark:checked:bg-sky-500;
|
|
11
|
+
@apply dark:focus-visible:outline-sky-500;
|
|
12
|
+
@apply dark:disabled:border-white/5 dark:disabled:bg-white/10 dark:disabled:checked:bg-white/10;
|
|
13
|
+
@apply forced-colors:appearance-auto;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
& svg {
|
|
17
|
+
@apply pointer-events-none col-start-1 row-start-1 size-3.5 self-center justify-self-center;
|
|
18
|
+
@apply stroke-white opacity-0 group-has-checked/checkbox:opacity-100;
|
|
19
|
+
@apply group-has-disabled/checkbox:stroke-stone-950/25 dark:group-has-disabled/checkbox:stroke-white/25;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
* Matches the design system with proper light/dark mode support
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/* Highlight color variables for text and background highlighting */
|
|
7
|
+
:root {
|
|
8
|
+
--highlight-1: rgb(136, 118, 38);
|
|
9
|
+
--highlight-2: rgb(185, 94, 6);
|
|
10
|
+
--highlight-3: rgb(207, 0, 0);
|
|
11
|
+
--highlight-4: rgb(216, 28, 170);
|
|
12
|
+
--highlight-5: rgb(144, 19, 254);
|
|
13
|
+
--highlight-6: rgb(5, 98, 185);
|
|
14
|
+
--highlight-7: rgb(17, 138, 15);
|
|
15
|
+
--highlight-8: rgb(148, 82, 22);
|
|
16
|
+
--highlight-9: rgb(102, 102, 102);
|
|
17
|
+
|
|
18
|
+
--highlight-bg-1: rgba(229, 223, 6, 0.3);
|
|
19
|
+
--highlight-bg-2: rgba(255, 185, 87, 0.3);
|
|
20
|
+
--highlight-bg-3: rgba(255, 118, 118, 0.3);
|
|
21
|
+
--highlight-bg-4: rgba(248, 137, 216, 0.3);
|
|
22
|
+
--highlight-bg-5: rgba(190, 165, 255, 0.3);
|
|
23
|
+
--highlight-bg-6: rgba(124, 192, 252, 0.3);
|
|
24
|
+
--highlight-bg-7: rgba(140, 255, 129, 0.3);
|
|
25
|
+
--highlight-bg-8: rgba(221, 170, 123, 0.3);
|
|
26
|
+
--highlight-bg-9: rgba(200, 200, 200, 0.3);
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
@layer components {
|
|
7
30
|
/* Editor container */
|
|
8
31
|
lexxy-editor {
|
|
@@ -31,7 +54,8 @@
|
|
|
31
54
|
}
|
|
32
55
|
|
|
33
56
|
/* Toolbar buttons */
|
|
34
|
-
lexxy-toolbar button
|
|
57
|
+
lexxy-toolbar button,
|
|
58
|
+
lexxy-toolbar summary {
|
|
35
59
|
@apply inline-flex items-center justify-center;
|
|
36
60
|
@apply h-8 w-8 rounded-md;
|
|
37
61
|
@apply text-stone-700 dark:text-stone-200;
|
|
@@ -42,12 +66,14 @@
|
|
|
42
66
|
}
|
|
43
67
|
|
|
44
68
|
lexxy-toolbar button[aria-pressed="true"],
|
|
45
|
-
lexxy-toolbar button.active
|
|
69
|
+
lexxy-toolbar button.active,
|
|
70
|
+
lexxy-toolbar details[open] > summary {
|
|
46
71
|
@apply bg-stone-200 text-stone-900;
|
|
47
72
|
@apply dark:bg-white/15 dark:text-white;
|
|
48
73
|
}
|
|
49
74
|
|
|
50
|
-
lexxy-toolbar button svg
|
|
75
|
+
lexxy-toolbar button svg,
|
|
76
|
+
lexxy-toolbar summary svg {
|
|
51
77
|
@apply size-5 text-inherit;
|
|
52
78
|
fill: currentColor;
|
|
53
79
|
}
|
|
@@ -66,71 +92,159 @@
|
|
|
66
92
|
@apply pointer-events-none;
|
|
67
93
|
}
|
|
68
94
|
|
|
69
|
-
/*
|
|
70
|
-
lexxy-
|
|
71
|
-
@apply
|
|
95
|
+
/* Toolbar dropdowns (shared container styles) */
|
|
96
|
+
.lexxy-editor__toolbar-dropdown {
|
|
97
|
+
@apply relative select-none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.lexxy-editor__toolbar-dropdown > summary {
|
|
101
|
+
list-style: none;
|
|
102
|
+
@apply cursor-pointer;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.lexxy-editor__toolbar-dropdown > summary::-webkit-details-marker {
|
|
106
|
+
display: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.lexxy-editor__toolbar-dropdown > summary::marker {
|
|
110
|
+
display: none;
|
|
111
|
+
content: "";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.lexxy-editor__toolbar-dropdown-content {
|
|
115
|
+
--dropdown-padding: --spacing(3);
|
|
116
|
+
--dropdown-gap: --spacing(1.5);
|
|
117
|
+
|
|
118
|
+
@apply absolute z-10;
|
|
119
|
+
@apply flex gap-(--dropdown-gap) p-(--dropdown-padding);
|
|
72
120
|
@apply bg-white dark:bg-stone-800;
|
|
73
|
-
@apply border border-
|
|
74
|
-
@apply rounded-
|
|
75
|
-
@apply
|
|
121
|
+
@apply border-2 border-sky-200 dark:border-sky-500/30;
|
|
122
|
+
@apply rounded-md;
|
|
123
|
+
@apply text-stone-900 dark:text-white;
|
|
124
|
+
@apply top-8 start-0;
|
|
125
|
+
@apply max-w-[40ch];
|
|
126
|
+
@apply rounded-ss-none;
|
|
76
127
|
}
|
|
77
128
|
|
|
78
|
-
lexxy-
|
|
79
|
-
@apply
|
|
80
|
-
@apply
|
|
129
|
+
.lexxy-editor__toolbar-dropdown:is([open]) .lexxy-editor__toolbar-button {
|
|
130
|
+
@apply bg-sky-100 dark:bg-sky-500/20;
|
|
131
|
+
@apply rounded-ee-none rounded-es-none;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.lexxy-editor__toolbar-dropdown:is([open]) .lexxy-editor__toolbar-button:hover {
|
|
135
|
+
@apply bg-sky-100 dark:bg-sky-500/20;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Responsive dropdown in overflow menu */
|
|
139
|
+
[overflowing] .lexxy-editor__toolbar-dropdown {
|
|
140
|
+
@apply static;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
[overflowing] .lexxy-editor__toolbar-dropdown-content {
|
|
144
|
+
--dropdown-padding: --spacing(2);
|
|
145
|
+
@apply end-(--dropdown-padding) start-(--dropdown-padding);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Link dropdown */
|
|
149
|
+
lexxy-link-dropdown {
|
|
150
|
+
@apply flex-1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
lexxy-link-dropdown > * {
|
|
154
|
+
@apply flex-1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
lexxy-link-dropdown .lexxy-editor__toolbar-dropdown-actions {
|
|
158
|
+
@apply flex flex-1 gap-2 mt-2;
|
|
159
|
+
@apply text-sm;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
lexxy-link-dropdown input[type="url"] {
|
|
163
|
+
@apply w-full rounded-md px-2;
|
|
81
164
|
@apply border border-stone-300 dark:border-white/10;
|
|
165
|
+
@apply bg-white dark:bg-stone-900;
|
|
82
166
|
@apply text-stone-900 dark:text-white;
|
|
83
167
|
@apply placeholder:text-stone-400 dark:placeholder:text-stone-500;
|
|
84
|
-
@apply focus-visible:outline-2 focus-visible:outline-
|
|
168
|
+
@apply focus-visible:outline-2 focus-visible:outline-sky-600 dark:focus-visible:outline-sky-500;
|
|
169
|
+
@apply leading-6 min-w-[40ch] box-border;
|
|
85
170
|
}
|
|
86
171
|
|
|
87
|
-
|
|
88
|
-
@apply
|
|
89
|
-
@apply bg-indigo-600 text-white;
|
|
90
|
-
@apply hover:bg-indigo-500;
|
|
172
|
+
[overflowing] lexxy-link-dropdown input[type="url"] {
|
|
173
|
+
@apply min-w-0;
|
|
91
174
|
}
|
|
92
175
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@apply
|
|
96
|
-
@apply rounded-lg border border-stone-200 bg-white shadow-lg;
|
|
97
|
-
@apply dark:border-white/10 dark:bg-stone-800;
|
|
98
|
-
min-width: 18rem;
|
|
176
|
+
lexxy-link-dropdown .lexxy-editor__toolbar-dropdown-actions button {
|
|
177
|
+
@apply w-full justify-center;
|
|
178
|
+
@apply button-secondary button-sm;
|
|
99
179
|
}
|
|
100
180
|
|
|
101
|
-
|
|
102
|
-
|
|
181
|
+
/* Highlight/Color dropdown */
|
|
182
|
+
lexxy-highlight-dropdown {
|
|
183
|
+
@apply flex flex-col;
|
|
103
184
|
}
|
|
104
185
|
|
|
105
|
-
lexxy-
|
|
106
|
-
@apply
|
|
107
|
-
@apply
|
|
186
|
+
lexxy-highlight-dropdown [data-button-group] {
|
|
187
|
+
@apply flex flex-row gap-(--dropdown-gap);
|
|
188
|
+
@apply justify-start;
|
|
108
189
|
}
|
|
109
190
|
|
|
110
|
-
lexxy-
|
|
111
|
-
|
|
112
|
-
@apply
|
|
113
|
-
@apply
|
|
114
|
-
@apply focus-visible:outline-2 focus-visible:outline-indigo-600;
|
|
115
|
-
@apply dark:border-white/10 dark:bg-stone-900 dark:text-white dark:placeholder:text-stone-500 dark:focus-visible:outline-indigo-500;
|
|
191
|
+
lexxy-highlight-dropdown [data-button-group] button {
|
|
192
|
+
--button-size: --spacing(8);
|
|
193
|
+
@apply aspect-square;
|
|
194
|
+
@apply w-(--button-size) min-w-(--button-size) max-w-(--button-size);
|
|
116
195
|
}
|
|
117
196
|
|
|
118
|
-
lexxy-
|
|
119
|
-
|
|
197
|
+
lexxy-highlight-dropdown [data-button-group] button::after {
|
|
198
|
+
content: "Aa";
|
|
199
|
+
@apply absolute inset-0;
|
|
200
|
+
@apply self-center;
|
|
201
|
+
@apply inline-block;
|
|
202
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
120
203
|
}
|
|
121
204
|
|
|
122
|
-
lexxy-
|
|
123
|
-
|
|
124
|
-
@apply
|
|
125
|
-
@apply text-
|
|
126
|
-
@apply
|
|
127
|
-
|
|
205
|
+
lexxy-highlight-dropdown button {
|
|
206
|
+
--button-size: --spacing(8);
|
|
207
|
+
@apply flex-1 relative;
|
|
208
|
+
@apply text-stone-900 dark:text-white;
|
|
209
|
+
@apply min-h-(--button-size);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
lexxy-highlight-dropdown button:hover {
|
|
213
|
+
@apply opacity-80;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
lexxy-highlight-dropdown button[aria-pressed="true"] {
|
|
217
|
+
@apply ring-2 ring-inset ring-current;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
lexxy-highlight-dropdown button[aria-pressed="true"]::after {
|
|
221
|
+
content: "✓";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
lexxy-highlight-dropdown .lexxy-editor__toolbar-dropdown-reset {
|
|
225
|
+
@apply mt-2 w-full;
|
|
226
|
+
@apply button-secondary button-sm;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
lexxy-highlight-dropdown .lexxy-editor__toolbar-dropdown-reset[disabled] {
|
|
230
|
+
@apply hidden;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Responsive highlight dropdown */
|
|
234
|
+
[overflowing] lexxy-highlight-dropdown {
|
|
235
|
+
@apply w-fit;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
[overflowing] lexxy-highlight-dropdown [data-button-group] {
|
|
239
|
+
@apply flex-wrap;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[overflowing] lexxy-highlight-dropdown [data-button-group] button {
|
|
243
|
+
--button-size: --spacing(6);
|
|
128
244
|
}
|
|
129
245
|
|
|
130
|
-
lexxy-
|
|
131
|
-
@apply text-
|
|
132
|
-
@apply border border-stone-200;
|
|
133
|
-
@apply dark:text-white dark:bg-white/10 dark:border-white/20 dark:hover:bg-white/15;
|
|
246
|
+
[overflowing] lexxy-highlight-dropdown [data-button-group] button::after {
|
|
247
|
+
@apply text-sm;
|
|
134
248
|
}
|
|
135
249
|
|
|
136
250
|
/* Attachment upload progress */
|
|
@@ -145,12 +259,12 @@
|
|
|
145
259
|
}
|
|
146
260
|
|
|
147
261
|
lexxy-editor .attachment__progress::-webkit-progress-value {
|
|
148
|
-
@apply bg-
|
|
262
|
+
@apply bg-sky-600;
|
|
149
263
|
@apply transition-all duration-200;
|
|
150
264
|
}
|
|
151
265
|
|
|
152
266
|
lexxy-editor .attachment__progress::-moz-progress-bar {
|
|
153
|
-
@apply bg-
|
|
267
|
+
@apply bg-sky-600;
|
|
154
268
|
}
|
|
155
269
|
|
|
156
270
|
lexxy-editor .attachment__progress[value="100"] {
|
|
@@ -201,7 +315,7 @@
|
|
|
201
315
|
/* Selected attachment state */
|
|
202
316
|
lexxy-editor [data-trix-mutable] img,
|
|
203
317
|
lexxy-editor .attachment.selected img {
|
|
204
|
-
@apply ring-2 ring-
|
|
318
|
+
@apply ring-2 ring-sky-500;
|
|
205
319
|
}
|
|
206
320
|
}
|
|
207
321
|
|
|
@@ -233,9 +347,9 @@
|
|
|
233
347
|
}
|
|
234
348
|
|
|
235
349
|
.lexxy-content a {
|
|
236
|
-
@apply text-
|
|
350
|
+
@apply text-sky-600 dark:text-sky-400;
|
|
237
351
|
@apply underline underline-offset-2;
|
|
238
|
-
@apply hover:text-
|
|
352
|
+
@apply hover:text-sky-500 dark:hover:text-sky-300;
|
|
239
353
|
}
|
|
240
354
|
|
|
241
355
|
.lexxy-content h1 {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Iron
|
|
2
|
+
class Account::ExportsController < ApplicationController
|
|
3
|
+
def index
|
|
4
|
+
@exports = Account::Export.order(created_at: :desc)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def new
|
|
8
|
+
@export = Account::Export.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
@export = Account::Export.create!(export_params)
|
|
13
|
+
@export.process_later
|
|
14
|
+
redirect_to @export, notice: "Export started"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def show
|
|
18
|
+
@export = Account::Export.find(params[:id])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def export_params
|
|
23
|
+
params.require(:account_export).permit(:include_schema, :include_content)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Iron
|
|
2
|
+
class Account::ImportsController < ApplicationController
|
|
3
|
+
def index
|
|
4
|
+
@imports = Account::Import.order(created_at: :desc)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def new
|
|
8
|
+
@import = Account::Import.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
@import = Account::Import.create!(import_params)
|
|
13
|
+
@import.file.attach(params[:account_import][:file])
|
|
14
|
+
@import.process_later
|
|
15
|
+
redirect_to @import, notice: "Import started"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show
|
|
19
|
+
@import = Account::Import.find(params[:id])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def import_params
|
|
24
|
+
params.require(:account_import).permit(:include_schema, :include_content)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -40,6 +40,13 @@ module Iron
|
|
|
40
40
|
super(method, text, options, &block)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
44
|
+
@template.content_tag(:div, class: "group/checkbox checkbox") do
|
|
45
|
+
super(method, options, checked_value, unchecked_value) +
|
|
46
|
+
@template.icon("check")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
43
50
|
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
|
44
51
|
selected_value = object.send(method) rescue nil
|
|
45
52
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Iron
|
|
2
|
+
module Processable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
RETENTION_LIMIT = 5
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
enum :status, %w[pending processing completed failed].index_by(&:itself), default: :pending
|
|
9
|
+
|
|
10
|
+
scope :recent, -> { order(created_at: :desc).limit(RETENTION_LIMIT) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class_methods do
|
|
14
|
+
def cleanup
|
|
15
|
+
where.not(id: recent.select(:id)).destroy_all
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def process
|
|
20
|
+
processing!
|
|
21
|
+
perform
|
|
22
|
+
update!(status: :completed, completed_at: Time.current)
|
|
23
|
+
self.class.cleanup
|
|
24
|
+
rescue => e
|
|
25
|
+
update!(status: :failed, error_message: e.message)
|
|
26
|
+
raise
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def perform
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Iron
|
|
2
|
+
class Account::Export < ApplicationRecord
|
|
3
|
+
include Processable, Broadcastable
|
|
4
|
+
|
|
5
|
+
belongs_to :user, class_name: "Iron::User", default: -> { Current.user }
|
|
6
|
+
has_one_attached :file
|
|
7
|
+
|
|
8
|
+
def process_later
|
|
9
|
+
ExportJob.perform_later(self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def title
|
|
13
|
+
created_at.strftime("%b %-d, %Y at %-l:%M %p")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def perform
|
|
18
|
+
raise ArgumentError, "Nothing selected to export" unless include_schema? || include_content?
|
|
19
|
+
|
|
20
|
+
zipfile = generate_zip
|
|
21
|
+
file.attach(
|
|
22
|
+
io: File.open(zipfile.path),
|
|
23
|
+
filename: "iron-export-#{id}.zip",
|
|
24
|
+
content_type: "application/zip"
|
|
25
|
+
)
|
|
26
|
+
ensure
|
|
27
|
+
zipfile&.close
|
|
28
|
+
zipfile&.unlink
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def generate_zip
|
|
32
|
+
Tempfile.new([ "export", ".zip" ]).tap do |tempfile|
|
|
33
|
+
Zip::File.open(tempfile.path, create: true) do |zip|
|
|
34
|
+
add_schema_to_zip(zip) if include_schema?
|
|
35
|
+
add_content_to_zip(zip) if include_content?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_schema_to_zip(zip)
|
|
41
|
+
zip.get_output_stream("schema.json") { |f| f.write(export_schema_json) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_content_to_zip(zip)
|
|
45
|
+
exportable_entries.find_each do |entry|
|
|
46
|
+
path = "entries/#{entry.content_type.handle}/#{entry.id}.json"
|
|
47
|
+
zip.get_output_stream(path) { |f| f.write(entry.export_json) }
|
|
48
|
+
|
|
49
|
+
entry.export_attachments.each do |attachment|
|
|
50
|
+
zip.get_output_stream("files/#{attachment[:path]}", compression_method: Zip::Entry::STORED) do |f|
|
|
51
|
+
attachment[:blob].download { |chunk| f.write(chunk) }
|
|
52
|
+
end
|
|
53
|
+
rescue ActiveStorage::FileNotFoundError
|
|
54
|
+
# Skip missing files
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def export_schema_json
|
|
60
|
+
JSON.pretty_generate(
|
|
61
|
+
version: "1.0",
|
|
62
|
+
exported_at: Time.current.iso8601,
|
|
63
|
+
block_definitions: export_block_definitions,
|
|
64
|
+
content_types: export_content_types
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def export_block_definitions
|
|
69
|
+
BlockDefinition
|
|
70
|
+
.includes(:field_definitions)
|
|
71
|
+
.order(:handle)
|
|
72
|
+
.map(&:export_attributes)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def export_content_types
|
|
76
|
+
ContentType
|
|
77
|
+
.includes(:title_field_definition, :web_page_title_field_definition, :field_definitions)
|
|
78
|
+
.order(:handle)
|
|
79
|
+
.map(&:export_attributes)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def exportable_entries
|
|
83
|
+
Entry.includes(:content_type, :creator, fields: [ :locale, :definition ])
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|