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
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
require "csv"
|
|
2
|
-
|
|
3
|
-
module Iron
|
|
4
|
-
class ContentImport::FieldReconstructor
|
|
5
|
-
def initialize(content_type, registry, archive)
|
|
6
|
-
@content_type = content_type
|
|
7
|
-
@registry = registry
|
|
8
|
-
@archive = archive
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def reconstruct_all(csv_data)
|
|
12
|
-
entry_snapshots = build_entry_snapshots(csv_data)
|
|
13
|
-
|
|
14
|
-
entry_snapshots.each do |entry_gid, snapshot|
|
|
15
|
-
reconstruct_entry(entry_gid, snapshot)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def build_entry_snapshots(csv_data)
|
|
22
|
-
snapshots = {}
|
|
23
|
-
return snapshots unless csv_data.present?
|
|
24
|
-
|
|
25
|
-
CSV.parse(csv_data, headers: true) do |row|
|
|
26
|
-
entry_key = row["entry_key"]
|
|
27
|
-
next if entry_key.blank?
|
|
28
|
-
|
|
29
|
-
snapshot = (snapshots[entry_key] ||= ContentImport::EntrySnapshot.new(entry_key))
|
|
30
|
-
populate_snapshot(snapshot, row)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
snapshots
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def populate_snapshot(snapshot, row)
|
|
37
|
-
path = row["path"]
|
|
38
|
-
return if path.blank?
|
|
39
|
-
|
|
40
|
-
if path == "/route"
|
|
41
|
-
snapshot.add_route(row["value"])
|
|
42
|
-
elsif path.start_with?("/fields/")
|
|
43
|
-
add_field_snapshot(snapshot, row)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def add_field_snapshot(snapshot, row)
|
|
48
|
-
locale_code = row["locale"]
|
|
49
|
-
return unless locale_code.present?
|
|
50
|
-
|
|
51
|
-
field_snapshot = ContentImport::FieldSnapshot.new(
|
|
52
|
-
locale_code: locale_code,
|
|
53
|
-
path: row["path"],
|
|
54
|
-
value: row["value"]
|
|
55
|
-
)
|
|
56
|
-
snapshot.add_field_snapshot(field_snapshot)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def reconstruct_entry(entry_gid, snapshot)
|
|
60
|
-
entry = @registry.find_entry(entry_gid)
|
|
61
|
-
return unless entry
|
|
62
|
-
|
|
63
|
-
update_entry_route(entry, snapshot.route) unless snapshot.route.nil?
|
|
64
|
-
reconstruct_entry_fields(entry, snapshot)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def update_entry_route(entry, route)
|
|
68
|
-
entry.update_columns(route: route)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def reconstruct_entry_fields(entry, snapshot)
|
|
72
|
-
snapshot.group_field_snapshots_by_locale.each do |locale_code, field_snapshots|
|
|
73
|
-
locale = Iron::Locale.find_by(code: locale_code)
|
|
74
|
-
next unless locale
|
|
75
|
-
|
|
76
|
-
reconstruct_fields_for_locale(entry, field_snapshots, locale)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def reconstruct_fields_for_locale(entry, field_snapshots, locale)
|
|
81
|
-
field_groups = field_snapshots
|
|
82
|
-
.select(&:field_path?)
|
|
83
|
-
.group_by(&:field_handle)
|
|
84
|
-
|
|
85
|
-
field_groups.each do |handle, snapshots|
|
|
86
|
-
definition = @content_type.field_definitions.find_by(handle: handle)
|
|
87
|
-
next unless definition
|
|
88
|
-
|
|
89
|
-
field = entry.find_or_build_field(definition, locale)
|
|
90
|
-
reconstruct_field(field, snapshots)
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def reconstruct_field(field, snapshots)
|
|
95
|
-
case field
|
|
96
|
-
when Iron::Fields::TextField
|
|
97
|
-
field.value_string = snapshots.first.value
|
|
98
|
-
when Iron::Fields::TextArea
|
|
99
|
-
field.value_text = snapshots.first.value
|
|
100
|
-
when Iron::Fields::Number
|
|
101
|
-
field.value_decimal = snapshots.first.value.to_f if snapshots.first.value.present?
|
|
102
|
-
when Iron::Fields::Boolean
|
|
103
|
-
field.value_boolean = ActiveModel::Type::Boolean.new.cast(snapshots.first.value)
|
|
104
|
-
when Iron::Fields::Date
|
|
105
|
-
field.value_datetime = Time.zone.parse(snapshots.first.value) if snapshots.first.value.present?
|
|
106
|
-
when Iron::Fields::File
|
|
107
|
-
attach_file(field, snapshots.first.value)
|
|
108
|
-
when Iron::Fields::Reference
|
|
109
|
-
field.referenced_entry = @registry.find_entry(snapshots.first.value)
|
|
110
|
-
when Iron::Fields::RichTextArea
|
|
111
|
-
reconstruct_rich_text(field, snapshots)
|
|
112
|
-
when Iron::Fields::ReferenceList
|
|
113
|
-
reconstruct_reference_list(field, snapshots)
|
|
114
|
-
when Iron::Fields::Block
|
|
115
|
-
reconstruct_block(field, snapshots)
|
|
116
|
-
when Iron::Fields::BlockList
|
|
117
|
-
reconstruct_block_list(field, snapshots)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
field.save!(validate: false)
|
|
121
|
-
rescue ArgumentError
|
|
122
|
-
# Handle date parsing errors
|
|
123
|
-
field.save!(validate: false)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def attach_file(field, blob_id)
|
|
127
|
-
return unless blob_id.present?
|
|
128
|
-
|
|
129
|
-
data = @archive["uploads/#{blob_id}"]
|
|
130
|
-
return unless data
|
|
131
|
-
|
|
132
|
-
field.file.attach(
|
|
133
|
-
io: StringIO.new(data),
|
|
134
|
-
filename: "imported-#{blob_id}",
|
|
135
|
-
content_type: "application/octet-stream"
|
|
136
|
-
)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def reconstruct_rich_text(field, snapshots)
|
|
140
|
-
html_snapshot = snapshots.find { |s| s.path.end_with?("/html") }
|
|
141
|
-
|
|
142
|
-
if html_snapshot&.value.present?
|
|
143
|
-
field.update!(rich_text: html_snapshot.value)
|
|
144
|
-
else
|
|
145
|
-
field.rich_text&.destroy
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def reconstruct_reference_list(field, snapshots)
|
|
150
|
-
# Group snapshots by rank (the first segment after the field handle)
|
|
151
|
-
field.save!(validate: false) if field.new_record?
|
|
152
|
-
|
|
153
|
-
items = snapshots.filter_map do |snapshot|
|
|
154
|
-
segments = snapshot.field_segments
|
|
155
|
-
next if segments.length < 2
|
|
156
|
-
|
|
157
|
-
rank = segments.last
|
|
158
|
-
entry = @registry.find_entry(snapshot.value)
|
|
159
|
-
next unless entry
|
|
160
|
-
|
|
161
|
-
{ rank:, entry: }
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
existing = field.references.index_by(&:rank)
|
|
165
|
-
kept_ids = []
|
|
166
|
-
|
|
167
|
-
items.each do |item|
|
|
168
|
-
reference = existing[item[:rank]] || field.references.build(rank: item[:rank])
|
|
169
|
-
reference.entry = item[:entry]
|
|
170
|
-
reference.save!
|
|
171
|
-
kept_ids << reference.id
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
field.references.where.not(id: kept_ids).destroy_all
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def reconstruct_block(field, snapshots)
|
|
178
|
-
# Fields::Block IS the block - it doesn't have a separate block relation
|
|
179
|
-
# The field already has the block definition through field.definition
|
|
180
|
-
|
|
181
|
-
# Group snapshots by field handle
|
|
182
|
-
field_snapshots = snapshots.reject { |s| s.path.include?("/_block") }
|
|
183
|
-
reconstruct_block_fields(field, field_snapshots, field.locale)
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def reconstruct_block_list(field, snapshots)
|
|
187
|
-
# Group snapshots by block rank
|
|
188
|
-
blocks_by_rank = {}
|
|
189
|
-
|
|
190
|
-
snapshots.each do |snapshot|
|
|
191
|
-
segments = snapshot.field_segments
|
|
192
|
-
next if segments.size < 2
|
|
193
|
-
|
|
194
|
-
rank = segments[1]
|
|
195
|
-
blocks_by_rank[rank] ||= []
|
|
196
|
-
blocks_by_rank[rank] << snapshot
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Clear all existing blocks first to avoid rank conflicts
|
|
200
|
-
field.blocks.destroy_all
|
|
201
|
-
|
|
202
|
-
blocks_by_rank.each do |rank, block_snapshots|
|
|
203
|
-
# Find block handle
|
|
204
|
-
block_snapshot = block_snapshots.find { |s| s.path.include?("/_block") }
|
|
205
|
-
block_handle = block_snapshot&.value
|
|
206
|
-
next unless block_handle
|
|
207
|
-
|
|
208
|
-
block_definition = Iron::BlockDefinition.find_by(handle: block_handle)
|
|
209
|
-
next unless block_definition
|
|
210
|
-
|
|
211
|
-
block = field.blocks.build(
|
|
212
|
-
rank: rank,
|
|
213
|
-
entry: field.entry,
|
|
214
|
-
locale: field.locale,
|
|
215
|
-
definition: block_definition
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
# Save the block first to establish it in the database
|
|
219
|
-
block.save!(validate: false)
|
|
220
|
-
|
|
221
|
-
# Then reconstruct its fields
|
|
222
|
-
field_snapshots = block_snapshots.reject { |s| s.path.include?("/_block") }
|
|
223
|
-
reconstruct_block_fields(block, field_snapshots, field.locale)
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def reconstruct_block_fields(block, snapshots, locale)
|
|
228
|
-
# Group snapshots by field handle
|
|
229
|
-
field_groups = {}
|
|
230
|
-
|
|
231
|
-
snapshots.each do |snapshot|
|
|
232
|
-
field_handle = block_child_handle(block, snapshot)
|
|
233
|
-
next unless field_handle
|
|
234
|
-
|
|
235
|
-
field_groups[field_handle] ||= []
|
|
236
|
-
field_groups[field_handle] << snapshot
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
# Get the actual block definition
|
|
240
|
-
block_def = if block.respond_to?(:block_definition)
|
|
241
|
-
block.block_definition
|
|
242
|
-
else
|
|
243
|
-
block.definition
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Clear existing fields to avoid rank conflicts
|
|
247
|
-
block.fields.where(locale: locale).destroy_all
|
|
248
|
-
|
|
249
|
-
field_groups.each do |handle, field_snapshots|
|
|
250
|
-
definition = block_def.field_definitions.find_by(handle: handle)
|
|
251
|
-
next unless definition
|
|
252
|
-
|
|
253
|
-
field = block.fields.build(
|
|
254
|
-
type: definition.field_type,
|
|
255
|
-
entry: block.entry,
|
|
256
|
-
definition: definition,
|
|
257
|
-
locale: locale
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
reconstruct_field(field, field_snapshots)
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
def block_child_handle(block, snapshot)
|
|
265
|
-
relative_segments = snapshot.field_segments
|
|
266
|
-
block_path = block.send(:field_path)
|
|
267
|
-
|
|
268
|
-
if block_path.present?
|
|
269
|
-
block_segments = block_path.sub(%r{^/fields/}, "").split("/")
|
|
270
|
-
relative_segments = relative_segments.drop(block_segments.length)
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
relative_segments.first
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
class ContentImport::FieldSnapshot
|
|
3
|
-
attr_reader :locale_code, :path, :value
|
|
4
|
-
|
|
5
|
-
def initialize(locale_code:, path:, value:)
|
|
6
|
-
@locale_code = locale_code
|
|
7
|
-
@path = path
|
|
8
|
-
@value = value
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def field_handle
|
|
12
|
-
return nil unless field_path?
|
|
13
|
-
|
|
14
|
-
segments[0]
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def field_segments
|
|
18
|
-
return [] unless field_path?
|
|
19
|
-
|
|
20
|
-
segments
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def field_path?
|
|
24
|
-
@path&.start_with?("/fields/")
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def segments
|
|
30
|
-
@segments ||= @path.sub(%r{^/fields/}, "").split("/")
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
class ContentImport::Registry
|
|
3
|
-
def initialize
|
|
4
|
-
@entries_by_original_gid = {}
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def register_entry(original_gid, entry)
|
|
8
|
-
@entries_by_original_gid[original_gid] = entry
|
|
9
|
-
@entries_by_original_gid[entry.to_gid.to_s] = entry
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def find_entry(gid_string)
|
|
13
|
-
return nil if gid_string.blank?
|
|
14
|
-
|
|
15
|
-
cached_entry = @entries_by_original_gid[gid_string]
|
|
16
|
-
return cached_entry if cached_entry
|
|
17
|
-
|
|
18
|
-
locate_existing_entry(gid_string)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def locate_existing_entry(gid_string)
|
|
24
|
-
entry = ::GlobalID::Locator.locate(gid_string)
|
|
25
|
-
return nil unless entry.is_a?(Iron::Entry)
|
|
26
|
-
|
|
27
|
-
@entries_by_original_gid[gid_string] = entry
|
|
28
|
-
rescue ActiveRecord::RecordNotFound
|
|
29
|
-
nil
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
require "csv"
|
|
2
|
-
|
|
3
|
-
module Iron
|
|
4
|
-
class ContentImport::Session
|
|
5
|
-
attr_reader :errors
|
|
6
|
-
|
|
7
|
-
def initialize(archive)
|
|
8
|
-
raise ArgumentError, "Missing import archive" unless archive.present?
|
|
9
|
-
|
|
10
|
-
@archive = archive
|
|
11
|
-
@errors = []
|
|
12
|
-
@registry = ContentImport::Registry.new
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def run
|
|
16
|
-
ActiveRecord::Base.transaction do
|
|
17
|
-
prepare_entries
|
|
18
|
-
reconstruct_entries
|
|
19
|
-
|
|
20
|
-
Result.new(success: true)
|
|
21
|
-
end
|
|
22
|
-
rescue StandardError => e
|
|
23
|
-
warn e.full_message
|
|
24
|
-
@errors << "Import failed: #{e.message}"
|
|
25
|
-
Result.new(success: false, errors: @errors)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def prepare_entries
|
|
31
|
-
content_manifests.each do |content_type, csv_data|
|
|
32
|
-
entry_builder = ContentImport::EntryBuilder.new(content_type, @registry)
|
|
33
|
-
entry_builder.ensure_entries_exist(csv_data)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def reconstruct_entries
|
|
38
|
-
content_manifests.each do |content_type, csv_data|
|
|
39
|
-
reconstructor = ContentImport::FieldReconstructor.new(content_type, @registry, @archive)
|
|
40
|
-
reconstructor.reconstruct_all(csv_data)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def content_manifests
|
|
45
|
-
@content_manifests ||= build_content_manifests
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def build_content_manifests
|
|
49
|
-
@archive.file_paths
|
|
50
|
-
.select { |path| content_manifest?(path) }
|
|
51
|
-
.sort
|
|
52
|
-
.filter_map do |path|
|
|
53
|
-
content_type = find_content_type(path)
|
|
54
|
-
|
|
55
|
-
unless content_type
|
|
56
|
-
@errors << "Unknown content type '#{extract_handle(path)}' in archive"
|
|
57
|
-
next
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
[ content_type, @archive[path] ]
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def content_manifest?(path)
|
|
65
|
-
path.start_with?("content/") && path.end_with?(".csv")
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def extract_handle(path)
|
|
69
|
-
::File.basename(path, ".csv")
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def find_content_type(path)
|
|
73
|
-
Iron::ContentType.find_by(handle: extract_handle(path))
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
class Result
|
|
77
|
-
attr_reader :errors
|
|
78
|
-
|
|
79
|
-
def initialize(success:, errors: [])
|
|
80
|
-
@success = success
|
|
81
|
-
@errors = errors
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def success?
|
|
85
|
-
@success
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
module ContentType::Portable
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
include ::Iron::CsvSerializable
|
|
5
|
-
|
|
6
|
-
class_methods do
|
|
7
|
-
def csv_headers
|
|
8
|
-
%w[handle name type description icon title_field_handle web_publishing_enabled web_page_title_field_handle base_path]
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def csv_scope
|
|
12
|
-
includes(:title_field_definition, :web_page_title_field_definition)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def to_csv_row
|
|
17
|
-
[
|
|
18
|
-
handle,
|
|
19
|
-
name,
|
|
20
|
-
type.demodulize.underscore,
|
|
21
|
-
description,
|
|
22
|
-
icon,
|
|
23
|
-
title_field_definition&.handle,
|
|
24
|
-
web_publishing_enabled,
|
|
25
|
-
web_page_title_field_definition&.handle,
|
|
26
|
-
base_path
|
|
27
|
-
]
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
class Entry
|
|
3
|
-
module Portable
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
def to_csv_rows
|
|
7
|
-
rows = []
|
|
8
|
-
|
|
9
|
-
rows.concat(entry_attribute_rows)
|
|
10
|
-
|
|
11
|
-
fields.where(parent_type: nil).includes(:locale, :definition).each do |field|
|
|
12
|
-
rows.concat(field.to_csv_rows)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
rows
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def entry_attribute_rows
|
|
21
|
-
rows = []
|
|
22
|
-
|
|
23
|
-
rows << csv_row("/created_at", "date", created_at.iso8601)
|
|
24
|
-
rows << csv_row("/updated_at", "date", updated_at.iso8601)
|
|
25
|
-
rows << csv_row("/route", "text", route) unless route.nil?
|
|
26
|
-
|
|
27
|
-
rows
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def csv_row(path, type, value)
|
|
31
|
-
[ to_gid, nil, path, type, value ]
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
class Field
|
|
3
|
-
module Portable
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
def to_csv_rows
|
|
7
|
-
[ csv_row(field_path, csv_type, csv_value) ]
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def field_path
|
|
11
|
-
if parent.is_a?(Fields::Block)
|
|
12
|
-
"#{parent.field_path}/#{definition.handle}"
|
|
13
|
-
elsif parent.is_a?(Fields::BlockList)
|
|
14
|
-
"#{parent.field_path}/#{rank}"
|
|
15
|
-
else
|
|
16
|
-
"/fields/#{definition.handle}"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def csv_row(path, type, value = nil)
|
|
21
|
-
[ entry.to_gid, locale&.code, path, type, value ]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def csv_type
|
|
25
|
-
raise NotImplementedError, "#{self.class} must implement csv_type"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def csv_value
|
|
29
|
-
raise NotImplementedError, "#{self.class} must implement csv_value"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
module FieldDefinition::Portable
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
include ::Iron::CsvSerializable
|
|
5
|
-
|
|
6
|
-
class_methods do
|
|
7
|
-
def csv_headers
|
|
8
|
-
%w[parent_type parent_handle handle name type rank metadata supported_block_definitions supported_content_types]
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def csv_scope
|
|
12
|
-
includes(:schemable)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def to_csv_row
|
|
17
|
-
[
|
|
18
|
-
schemable_type == "Iron::ContentType" ? "content_type" : "block",
|
|
19
|
-
schemable.handle,
|
|
20
|
-
handle,
|
|
21
|
-
name,
|
|
22
|
-
type.demodulize.underscore,
|
|
23
|
-
rank,
|
|
24
|
-
metadata.to_json,
|
|
25
|
-
export_supported_block_definitions,
|
|
26
|
-
export_supported_content_types
|
|
27
|
-
]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def export_supported_block_definitions
|
|
33
|
-
return nil unless respond_to?(:supported_block_definitions)
|
|
34
|
-
supported_block_definitions.pluck(:handle).join("|")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def export_supported_content_types
|
|
38
|
-
return nil unless respond_to?(:supported_content_types)
|
|
39
|
-
supported_content_types.pluck(:handle).join("|")
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
require "csv"
|
|
2
|
-
require "rubygems/package"
|
|
3
|
-
require "stringio"
|
|
4
|
-
|
|
5
|
-
module Iron
|
|
6
|
-
class SchemaArchive
|
|
7
|
-
REQUIRED_FILES = %w[content_types.csv block_definitions.csv field_definitions.csv].freeze
|
|
8
|
-
|
|
9
|
-
attr_reader :errors
|
|
10
|
-
|
|
11
|
-
def self.from_file(file)
|
|
12
|
-
new(file.read)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def initialize(tar_content = nil)
|
|
16
|
-
@tar_content = tar_content
|
|
17
|
-
@csv_files = {}
|
|
18
|
-
@errors = []
|
|
19
|
-
extract_files if tar_content
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def valid?
|
|
23
|
-
return false if @tar_content.nil?
|
|
24
|
-
|
|
25
|
-
missing_files = REQUIRED_FILES - @csv_files.keys
|
|
26
|
-
if missing_files.any?
|
|
27
|
-
@errors << "Missing required files: #{missing_files.join(', ')}"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
@errors.empty?
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def [](filename)
|
|
34
|
-
@csv_files[filename]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def add_file(filename, csv_content)
|
|
38
|
-
@csv_files[filename] = csv_content
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def to_tar
|
|
42
|
-
tarfile = StringIO.new
|
|
43
|
-
|
|
44
|
-
Gem::Package::TarWriter.new(tarfile) do |tar|
|
|
45
|
-
@csv_files.each do |filename, content|
|
|
46
|
-
tar.add_file_simple(filename, 0644, content.bytesize) do |io|
|
|
47
|
-
io.write(content)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
tarfile.string
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
|
-
def extract_files
|
|
58
|
-
tar_io = StringIO.new(@tar_content)
|
|
59
|
-
|
|
60
|
-
Gem::Package::TarReader.new(tar_io) do |tar|
|
|
61
|
-
tar.each do |entry|
|
|
62
|
-
if entry.file? && entry.full_name.end_with?(".csv")
|
|
63
|
-
@csv_files[entry.full_name] = entry.read.force_encoding("UTF-8")
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
rescue StandardError => e
|
|
68
|
-
@errors << "Failed to extract tar archive: #{e.message}"
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module Iron
|
|
2
|
-
class SchemaExporter
|
|
3
|
-
def self.export
|
|
4
|
-
new.export
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def export
|
|
8
|
-
archive = SchemaArchive.new
|
|
9
|
-
archive.add_file("content_types.csv", ContentType.to_csv)
|
|
10
|
-
archive.add_file("block_definitions.csv", BlockDefinition.to_csv)
|
|
11
|
-
archive.add_file("field_definitions.csv", FieldDefinition.to_csv)
|
|
12
|
-
archive.to_tar
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|