iron-cms 0.1.2 → 0.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -0
  3. data/app/assets/builds/iron.css +9 -0
  4. data/app/controllers/iron/contents_controller.rb +33 -0
  5. data/app/models/concerns/iron/instance_scoped.rb +25 -0
  6. data/app/models/iron/account.rb +2 -0
  7. data/app/models/iron/archive.rb +69 -0
  8. data/app/models/iron/block_definition.rb +8 -0
  9. data/app/models/iron/content_export.rb +73 -0
  10. data/app/models/iron/content_import/entry_builder.rb +77 -0
  11. data/app/models/iron/content_import/entry_snapshot.rb +23 -0
  12. data/app/models/iron/content_import/field_reconstructor.rb +276 -0
  13. data/app/models/iron/content_import/field_snapshot.rb +33 -0
  14. data/app/models/iron/content_import/registry.rb +32 -0
  15. data/app/models/iron/content_import/session.rb +89 -0
  16. data/app/models/iron/content_import.rb +15 -0
  17. data/app/models/iron/content_type.rb +8 -0
  18. data/app/models/iron/current.rb +2 -1
  19. data/app/models/iron/entry/portable.rb +35 -0
  20. data/app/models/iron/entry.rb +1 -1
  21. data/app/models/iron/field/portable.rb +33 -0
  22. data/app/models/iron/field.rb +1 -1
  23. data/app/models/iron/field_definition/portable.rb +12 -6
  24. data/app/models/iron/fields/block.rb +22 -8
  25. data/app/models/iron/fields/block_list.rb +10 -0
  26. data/app/models/iron/fields/boolean.rb +10 -0
  27. data/app/models/iron/fields/date.rb +10 -0
  28. data/app/models/iron/fields/file.rb +8 -0
  29. data/app/models/iron/fields/number.rb +10 -0
  30. data/app/models/iron/fields/reference.rb +10 -0
  31. data/app/models/iron/fields/reference_list.rb +14 -0
  32. data/app/models/iron/fields/rich_text_area.rb +26 -0
  33. data/app/models/iron/fields/text_area.rb +10 -0
  34. data/app/models/iron/fields/text_field.rb +8 -0
  35. data/app/models/iron/locale.rb +1 -1
  36. data/app/models/iron/schema_importer/import_strategy.rb +59 -0
  37. data/app/models/iron/schema_importer/merge_strategy.rb +52 -0
  38. data/app/models/iron/schema_importer/replace_strategy.rb +51 -0
  39. data/app/models/iron/schema_importer/safe_strategy.rb +55 -0
  40. data/app/models/iron/schema_importer.rb +29 -199
  41. data/app/views/iron/contents/new.html.erb +34 -0
  42. data/app/views/iron/settings/show.html.erb +19 -0
  43. data/config/routes.rb +5 -0
  44. data/db/migrate/20250908203158_add_instance_token_to_iron_accounts.rb +6 -0
  45. data/lib/iron/engine.rb +9 -0
  46. data/lib/iron/global_id/instance_scoped_locator.rb +29 -0
  47. data/lib/iron/version.rb +1 -1
  48. data/lib/iron-cms.rb +1 -1
  49. data/lib/iron.rb +1 -0
  50. metadata +21 -1
@@ -0,0 +1,51 @@
1
+ module Iron
2
+ class SchemaImporter::ReplaceStrategy < SchemaImporter::ImportStrategy
3
+ def import_block_definition(row)
4
+ BlockDefinition.create!(
5
+ handle: row["handle"],
6
+ name: row["name"],
7
+ description: row["description"]
8
+ )
9
+ end
10
+
11
+ def import_content_type(row)
12
+ content_type_class(row["type"]).create!(
13
+ handle: row["handle"],
14
+ name: row["name"],
15
+ description: row["description"],
16
+ icon: row["icon"],
17
+ web_publishing_enabled: row["web_publishing_enabled"] == "true",
18
+ base_path: row["base_path"]
19
+ )
20
+ end
21
+
22
+ def import_field_definition(row)
23
+ raise "Field type is required" unless row["type"].present?
24
+ return unless row["parent_type"].present? && row["parent_handle"].present? && row["handle"].present?
25
+
26
+ parent = find_parent(row["parent_type"], row["parent_handle"])
27
+ return unless parent
28
+
29
+ parent.field_definitions.find_by(handle: row["handle"])&.destroy!
30
+
31
+ klass = field_definition_class(row["type"])
32
+ attributes = {
33
+ schemable: parent,
34
+ handle: row["handle"],
35
+ name: row["name"],
36
+ rank: row["rank"],
37
+ metadata: parse_metadata(row["metadata"])
38
+ }
39
+
40
+ if klass <= FieldDefinitions::Block || klass <= FieldDefinitions::BlockList
41
+ attributes[:supported_block_definition_ids] = parse_supported_block_definitions(row["supported_block_definitions"]).pluck(:id)
42
+ end
43
+
44
+ if klass <= FieldDefinitions::Reference || klass <= FieldDefinitions::ReferenceList
45
+ attributes[:supported_content_type_ids] = parse_supported_content_types(row["supported_content_types"]).pluck(:id)
46
+ end
47
+
48
+ klass.create!(attributes)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,55 @@
1
+ module Iron
2
+ class SchemaImporter::SafeStrategy < SchemaImporter::ImportStrategy
3
+ def import_block_definition(row)
4
+ return if BlockDefinition.exists?(handle: row["handle"])
5
+
6
+ BlockDefinition.create!(
7
+ handle: row["handle"],
8
+ name: row["name"],
9
+ description: row["description"]
10
+ )
11
+ end
12
+
13
+ def import_content_type(row)
14
+ return if ContentType.exists?(handle: row["handle"])
15
+
16
+ content_type_class(row["type"]).create!(
17
+ handle: row["handle"],
18
+ name: row["name"],
19
+ description: row["description"],
20
+ icon: row["icon"],
21
+ web_publishing_enabled: row["web_publishing_enabled"] == "true",
22
+ base_path: row["base_path"]
23
+ )
24
+ end
25
+
26
+ def import_field_definition(row)
27
+ raise "Field type is required" unless row["type"].present?
28
+ return unless row["parent_type"].present? && row["parent_handle"].present? && row["handle"].present?
29
+
30
+ parent = find_parent(row["parent_type"], row["parent_handle"])
31
+ return unless parent
32
+
33
+ return if parent.field_definitions.exists?(handle: row["handle"])
34
+
35
+ klass = field_definition_class(row["type"])
36
+ attributes = {
37
+ schemable: parent,
38
+ handle: row["handle"],
39
+ name: row["name"],
40
+ rank: row["rank"],
41
+ metadata: parse_metadata(row["metadata"])
42
+ }
43
+
44
+ if klass <= FieldDefinitions::Block || klass <= FieldDefinitions::BlockList
45
+ attributes[:supported_block_definition_ids] = parse_supported_block_definitions(row["supported_block_definitions"]).pluck(:id)
46
+ end
47
+
48
+ if klass <= FieldDefinitions::Reference || klass <= FieldDefinitions::ReferenceList
49
+ attributes[:supported_content_type_ids] = parse_supported_content_types(row["supported_content_types"]).pluck(:id)
50
+ end
51
+
52
+ klass.create!(attributes)
53
+ end
54
+ end
55
+ end
@@ -11,16 +11,16 @@ module Iron
11
11
  end
12
12
 
13
13
  def initialize(file, mode: "merge")
14
+ raise ArgumentError, "Missing import file" unless file.present?
15
+ raise ArgumentError, "Invalid import mode: #{mode}. Must be one of: #{IMPORT_MODES.join(', ')}" unless IMPORT_MODES.include?(mode)
16
+
14
17
  @file = file
15
18
  @mode = mode.to_s
16
19
  @errors = []
17
-
18
- validate_mode!
20
+ @strategy = build_strategy(mode)
19
21
  end
20
22
 
21
23
  def import
22
- return ImportResult.new(false, errors) unless valid?
23
-
24
24
  ActiveRecord::Base.transaction do
25
25
  Account.clear_schema! if mode == "replace"
26
26
 
@@ -31,205 +31,45 @@ module Iron
31
31
  return ImportResult.new(false, errors)
32
32
  end
33
33
 
34
- import_block_definitions(archive["block_definitions.csv"])
35
- import_content_types(archive["content_types.csv"])
36
- import_field_definitions(archive["field_definitions.csv"])
37
-
38
- update_content_type_references(archive["content_types.csv"])
39
-
40
- ImportResult.new(true, [])
41
- end
42
- rescue StandardError => e
43
- errors << "Import failed: #{e.message}"
44
- ImportResult.new(false, errors)
45
- end
46
-
47
- private
48
-
49
- def valid?
50
- validate_file_presence
51
- errors.empty?
52
- end
53
-
54
- def validate_mode!
55
- unless IMPORT_MODES.include?(mode)
56
- raise ArgumentError, "Invalid import mode: #{mode}. Must be one of: #{IMPORT_MODES.join(', ')}"
57
- end
58
- end
59
-
60
- def validate_file_presence
61
- errors << "No file provided" unless file
62
- end
63
-
64
- def import_block_definitions(csv_content)
65
- return unless csv_content
66
-
67
- CSV.parse(csv_content, headers: true) do |row|
68
- handle = row["handle"]
69
- next unless handle.present?
70
-
71
- case mode
72
- when "merge"
73
- block_def = BlockDefinition.find_or_initialize_by(handle: handle)
74
- block_def.assign_attributes(
75
- name: row["name"],
76
- description: row["description"]
77
- )
78
- block_def.save!
79
- when "replace"
80
- BlockDefinition.create!(
81
- handle: handle,
82
- name: row["name"],
83
- description: row["description"]
84
- )
85
- when "safe"
86
- next if BlockDefinition.exists?(handle: handle)
87
-
88
- BlockDefinition.create!(
89
- handle: handle,
90
- name: row["name"],
91
- description: row["description"]
92
- )
34
+ if archive["block_definitions.csv"]
35
+ CSV.parse(archive["block_definitions.csv"], headers: true) do |row|
36
+ next unless row["handle"].present?
37
+ @strategy.import_block_definition(row)
93
38
  end
94
39
  end
95
- end
96
40
 
97
- def import_content_types(csv_content)
98
- return unless csv_content
99
-
100
- CSV.parse(csv_content, headers: true) do |row|
101
- handle = row["handle"]
102
- next unless handle.present?
103
-
104
- case mode
105
- when "merge"
106
- content_type = ContentType.find_or_initialize_by(handle: handle)
107
- update_content_type(content_type, row)
108
- when "replace"
109
- content_type = build_content_type(row)
110
- content_type.save!
111
- when "safe"
112
- next if ContentType.exists?(handle: handle)
113
-
114
- content_type = build_content_type(row)
115
- content_type.save!
41
+ if archive["content_types.csv"]
42
+ CSV.parse(archive["content_types.csv"], headers: true) do |row|
43
+ next unless row["handle"].present?
44
+ @strategy.import_content_type(row)
116
45
  end
117
46
  end
118
- end
119
-
120
- def import_field_definitions(csv_content)
121
- return unless csv_content
122
-
123
- CSV.parse(csv_content, headers: true) do |row|
124
- parent_type = row["parent_type"]
125
- parent_handle = row["parent_handle"]
126
- handle = row["handle"]
127
47
 
128
- next unless parent_type.present? && parent_handle.present? && handle.present?
129
-
130
- parent = find_parent(parent_type, parent_handle)
131
- next unless parent
132
-
133
- case mode
134
- when "merge"
135
- field_def = parent.field_definitions.find_or_initialize_by(handle: handle)
136
- update_field_definition(field_def, row)
137
- when "replace"
138
- field_def = build_field_definition(parent, row)
139
- field_def.save!
140
- when "safe"
141
- next if parent.field_definitions.exists?(handle: handle)
142
-
143
- field_def = build_field_definition(parent, row)
144
- field_def.save!
48
+ if archive["field_definitions.csv"]
49
+ CSV.parse(archive["field_definitions.csv"], headers: true) do |row|
50
+ @strategy.import_field_definition(row)
145
51
  end
146
52
  end
147
- end
148
-
149
- def find_parent(parent_type, parent_handle)
150
- case parent_type
151
- when "content_type"
152
- ContentType.find_by(handle: parent_handle)
153
- when "block"
154
- BlockDefinition.find_by(handle: parent_handle)
155
- end
156
- end
157
-
158
- def build_content_type(row)
159
- klass = case row["type"]
160
- when "single" then ContentTypes::Single
161
- when "collection" then ContentTypes::Collection
162
- else ContentType
163
- end
164
-
165
- klass.new(
166
- handle: row["handle"],
167
- name: row["name"],
168
- description: row["description"],
169
- icon: row["icon"],
170
- web_publishing_enabled: row["web_publishing_enabled"] == "true",
171
- base_path: row["base_path"]
172
- )
173
- end
174
-
175
- def update_content_type(content_type, row)
176
- content_type.assign_attributes(
177
- name: row["name"],
178
- description: row["description"],
179
- icon: row["icon"],
180
- web_publishing_enabled: row["web_publishing_enabled"] == "true",
181
- base_path: row["base_path"]
182
- )
183
-
184
- # Update type if changed
185
- if row["type"] && content_type.type != "Iron::ContentTypes::#{row['type'].camelize}"
186
- content_type.type = "Iron::ContentTypes::#{row['type'].camelize}"
187
- end
188
-
189
- content_type.save!
190
- end
191
-
192
- def build_field_definition(parent, row)
193
- klass = "Iron::FieldDefinitions::#{row['type'].camelize}".constantize
194
53
 
195
- field_def = klass.new(
196
- schemable: parent,
197
- handle: row["handle"],
198
- name: row["name"],
199
- rank: row["rank"],
200
- metadata: parse_metadata(row["metadata"])
201
- )
202
-
203
- # Set supported block definitions before saving
204
- set_supported_block_definitions(field_def, row["supported_block_definitions"])
54
+ update_content_type_references(archive["content_types.csv"])
205
55
 
206
- field_def
56
+ ImportResult.new(true, [])
207
57
  end
58
+ rescue StandardError => e
59
+ errors << "Import failed: #{e.message}"
60
+ ImportResult.new(false, errors)
61
+ end
208
62
 
209
- def update_field_definition(field_def, row)
210
- field_def.assign_attributes(
211
- name: row["name"],
212
- rank: row["rank"],
213
- metadata: parse_metadata(row["metadata"])
214
- )
63
+ private
215
64
 
216
- # Update type if changed
217
- if row["type"] && field_def.type != "Iron::FieldDefinitions::#{row['type'].camelize}"
218
- field_def.type = "Iron::FieldDefinitions::#{row['type'].camelize}"
65
+ def build_strategy(mode)
66
+ case mode.to_s
67
+ when "merge" then SchemaImporter::MergeStrategy.new
68
+ when "replace" then SchemaImporter::ReplaceStrategy.new
69
+ when "safe" then SchemaImporter::SafeStrategy.new
70
+ else
71
+ raise ArgumentError, "Invalid import mode: #{mode}"
219
72
  end
220
-
221
- # Set supported block definitions before saving
222
- set_supported_block_definitions(field_def, row["supported_block_definitions"])
223
-
224
- field_def.save!
225
- end
226
-
227
- def parse_metadata(metadata_string)
228
- return {} if metadata_string.blank?
229
-
230
- JSON.parse(metadata_string)
231
- rescue JSON::ParserError
232
- {}
233
73
  end
234
74
 
235
75
  def update_content_type_references(content_types_csv)
@@ -252,16 +92,6 @@ module Iron
252
92
  end
253
93
  end
254
94
 
255
- def set_supported_block_definitions(field_def, supported_handles_string)
256
- return unless supported_handles_string.present?
257
- return unless field_def.respond_to?(:supported_block_definitions)
258
-
259
- handles = supported_handles_string.split("|")
260
- block_definitions = BlockDefinition.where(handle: handles)
261
-
262
- field_def.supported_block_definitions = block_definitions
263
- end
264
-
265
95
  class ImportResult
266
96
  attr_reader :errors
267
97
 
@@ -0,0 +1,34 @@
1
+ <% content_for :title, "Import Content" %>
2
+
3
+ <div class="">
4
+ <%= back_button_to "Settings", settings_path %>
5
+ <h1 class="page-title">Import Content</h1>
6
+ <p class="mt-2 text-sm text-stone-500">Upload a content archive exported from another Iron instance.</p>
7
+
8
+ <%= form_with url: import_content_path, multipart: true, class: "mt-8 max-w-2xl", data: { turbo: false } do |form| %>
9
+ <div class="field-group">
10
+ <div class="field">
11
+ <%= form.label :content_file, "Content Archive" %>
12
+ <div>
13
+ <%= form.file_field :content_file,
14
+ accept: ".tar",
15
+ required: true,
16
+ class: "input" %>
17
+ <p class="mt-2 text-sm text-stone-500">Select a .tar file exported using Iron's content export.</p>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="field">
22
+ <label class="text-sm font-medium text-stone-700">Import behaviour</label>
23
+ <p class="text-sm text-stone-500 mt-1">
24
+ Entries with matching IDs and instance tokens are updated. Other records are created.
25
+ </p>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="mt-6 flex items-center gap-x-3">
30
+ <%= form.submit "Import Content", class: "btn" %>
31
+ <%= link_to "Cancel", settings_path, class: "btn", data: { variant: "plain" } %>
32
+ </div>
33
+ <% end %>
34
+ </div>
@@ -35,4 +35,23 @@
35
35
  </div>
36
36
  <% end %>
37
37
  </div>
38
+
39
+ <div class="bg-white dark:bg-stone-800/50 rounded-lg shadow-sm p-6 max-w-96">
40
+ <h2 class="text-lg font-semibold mb-4">Content Management</h2>
41
+ <p class="text-sm text-stone-600 dark:text-stone-400 mb-4">
42
+ Export content entries and their files or import an archive from another instance.
43
+ </p>
44
+ <div class="flex flex-col gap-2">
45
+ <%= link_to "Export Content",
46
+ export_content_path,
47
+ class: "btn",
48
+ data: {
49
+ turbo: false,
50
+ } %>
51
+ <%= link_to "Import Content",
52
+ new_content_path,
53
+ class: "btn",
54
+ data: { variant: "secondary" } %>
55
+ </div>
56
+ </div>
38
57
  </div>
data/config/routes.rb CHANGED
@@ -22,6 +22,11 @@ Iron::Engine.routes.draw do
22
22
  post :import, on: :member
23
23
  end
24
24
 
25
+ resource :content, only: [ :new ] do
26
+ get :export, on: :collection
27
+ post :import, on: :collection
28
+ end
29
+
25
30
  resources :block_definitions do
26
31
  resources :field_definitions, shallow: true, module: :block_definitions
27
32
  end
@@ -0,0 +1,6 @@
1
+ class AddInstanceTokenToIronAccounts < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :iron_accounts, :instance_token, :string
4
+ add_index :iron_accounts, :instance_token, unique: true
5
+ end
6
+ end
data/lib/iron/engine.rb CHANGED
@@ -31,6 +31,15 @@ module Iron
31
31
  app.config.active_storage.analyzers.prepend Iron::ImageAnalyzer
32
32
  end
33
33
 
34
+ initializer "iron.global_id_locator" do |app|
35
+ app.config.after_initialize do
36
+ global_id_app = ::GlobalID.app || ::Rails.application.config.try(:global_id)&.app
37
+ next unless global_id_app
38
+
39
+ ::GlobalID::Locator.use(global_id_app, Iron::GlobalID::InstanceScopedLocator.new)
40
+ end
41
+ end
42
+
34
43
  config.to_prepare do
35
44
  ActionView::Base.include(Module.new do
36
45
  def attachment_url(attachment, **options)
@@ -0,0 +1,29 @@
1
+ require "globalid"
2
+
3
+ module Iron
4
+ module GlobalID
5
+ class InstanceScopedLocator < ::GlobalID::Locator::UnscopedLocator
6
+ def locate(gid, options = {})
7
+ return unless instance_matches?(gid)
8
+
9
+ super
10
+ end
11
+
12
+ def locate_many(gids, options = {})
13
+ filtered_gids = gids.select { |candidate| instance_matches?(candidate) }
14
+ return [] if filtered_gids.empty?
15
+
16
+ super(filtered_gids, options)
17
+ end
18
+
19
+ private
20
+
21
+ def instance_matches?(gid)
22
+ instance_token = gid.params[:instance]
23
+ return true if instance_token.blank?
24
+
25
+ Current.account&.instance_token == instance_token
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/iron/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Iron
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/iron-cms.rb CHANGED
@@ -1 +1 @@
1
- require "iron"
1
+ require "iron"
data/lib/iron.rb CHANGED
@@ -2,6 +2,7 @@ require "iron/version"
2
2
  require "iron/engine"
3
3
  require "iron/cva"
4
4
  require "iron/sdk"
5
+ require "iron/global_id/instance_scoped_locator"
5
6
 
6
7
  module Iron
7
8
  # Your code goes here...
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iron-cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Massimo De Marchi
@@ -198,6 +198,7 @@ files:
198
198
  - app/controllers/iron/blocks_controller.rb
199
199
  - app/controllers/iron/content_types/field_definitions_controller.rb
200
200
  - app/controllers/iron/content_types_controller.rb
201
+ - app/controllers/iron/contents_controller.rb
201
202
  - app/controllers/iron/entries_controller.rb
202
203
  - app/controllers/iron/field_definitions_controller.rb
203
204
  - app/controllers/iron/first_runs_controller.rb
@@ -239,11 +240,21 @@ files:
239
240
  - app/mailers/iron/application_mailer.rb
240
241
  - app/mailers/passwords_mailer.rb
241
242
  - app/models/concerns/iron/csv_serializable.rb
243
+ - app/models/concerns/iron/instance_scoped.rb
242
244
  - app/models/iron/account.rb
243
245
  - app/models/iron/account/joinable.rb
244
246
  - app/models/iron/application_record.rb
247
+ - app/models/iron/archive.rb
245
248
  - app/models/iron/block_definition.rb
246
249
  - app/models/iron/block_definition/portable.rb
250
+ - app/models/iron/content_export.rb
251
+ - app/models/iron/content_import.rb
252
+ - app/models/iron/content_import/entry_builder.rb
253
+ - app/models/iron/content_import/entry_snapshot.rb
254
+ - app/models/iron/content_import/field_reconstructor.rb
255
+ - app/models/iron/content_import/field_snapshot.rb
256
+ - app/models/iron/content_import/registry.rb
257
+ - app/models/iron/content_import/session.rb
247
258
  - app/models/iron/content_type.rb
248
259
  - app/models/iron/content_type/field_queryable.rb
249
260
  - app/models/iron/content_type/portable.rb
@@ -254,12 +265,14 @@ files:
254
265
  - app/models/iron/current.rb
255
266
  - app/models/iron/entry.rb
256
267
  - app/models/iron/entry/deep_validation.rb
268
+ - app/models/iron/entry/portable.rb
257
269
  - app/models/iron/entry/presentable.rb
258
270
  - app/models/iron/entry/schemable.rb
259
271
  - app/models/iron/entry/titlable.rb
260
272
  - app/models/iron/entry/web_publishable.rb
261
273
  - app/models/iron/field.rb
262
274
  - app/models/iron/field/belongs_to_entry.rb
275
+ - app/models/iron/field/portable.rb
263
276
  - app/models/iron/field_definition.rb
264
277
  - app/models/iron/field_definition/portable.rb
265
278
  - app/models/iron/field_definitions/block.rb
@@ -291,6 +304,10 @@ files:
291
304
  - app/models/iron/schema_archive.rb
292
305
  - app/models/iron/schema_exporter.rb
293
306
  - app/models/iron/schema_importer.rb
307
+ - app/models/iron/schema_importer/import_strategy.rb
308
+ - app/models/iron/schema_importer/merge_strategy.rb
309
+ - app/models/iron/schema_importer/replace_strategy.rb
310
+ - app/models/iron/schema_importer/safe_strategy.rb
294
311
  - app/models/iron/session.rb
295
312
  - app/models/iron/user.rb
296
313
  - app/models/iron/user/role.rb
@@ -308,6 +325,7 @@ files:
308
325
  - app/views/iron/content_types/index.html.erb
309
326
  - app/views/iron/content_types/new.html.erb
310
327
  - app/views/iron/content_types/show.html.erb
328
+ - app/views/iron/contents/new.html.erb
311
329
  - app/views/iron/entries/_entry.html.erb
312
330
  - app/views/iron/entries/_entry_option.html.erb
313
331
  - app/views/iron/entries/_form.html.erb
@@ -390,6 +408,7 @@ files:
390
408
  - db/migrate/20250601080146_add_icon_to_iron_content_types.rb
391
409
  - db/migrate/20250609091605_add_web_publishing_to_iron_content_types.rb
392
410
  - db/migrate/20250609091813_add_route_to_iron_entries.rb
411
+ - db/migrate/20250908203158_add_instance_token_to_iron_accounts.rb
393
412
  - lib/generators/iron/pages/pages_generator.rb
394
413
  - lib/generators/iron/pages/templates/pages_controller.rb
395
414
  - lib/generators/iron/pages/templates/show.html.erb
@@ -399,6 +418,7 @@ files:
399
418
  - lib/iron.rb
400
419
  - lib/iron/cva.rb
401
420
  - lib/iron/engine.rb
421
+ - lib/iron/global_id/instance_scoped_locator.rb
402
422
  - lib/iron/image_analyzer.rb
403
423
  - lib/iron/lexorank.rb
404
424
  - lib/iron/lexorank/exceptions.rb