iron-cms 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +174 -0
- data/Rakefile +8 -0
- data/app/assets/builds/iron.css +2 -0
- data/app/assets/config/iron_manifest.js +1 -0
- data/app/assets/images/iron/icons/sprite.svg +9425 -0
- data/app/assets/tailwind/iron/actiontext.css +550 -0
- data/app/assets/tailwind/iron/application.css +66 -0
- data/app/assets/tailwind/iron/components/button.css +74 -0
- data/app/assets/tailwind/iron/components/dropdown.css +42 -0
- data/app/assets/tailwind/iron/components/fieldset.css +18 -0
- data/app/assets/tailwind/iron/components/form.css +14 -0
- data/app/assets/tailwind/iron/components/icon-picker.css +9 -0
- data/app/assets/tailwind/iron/components/input.css +44 -0
- data/app/assets/tailwind/iron/components/page.css +7 -0
- data/app/assets/tailwind/iron/components/select.css +36 -0
- data/app/assets/tailwind/iron/components/sidebar.css +21 -0
- data/app/assets/tailwind/iron/components/textarea.css +27 -0
- data/app/controllers/concerns/iron/authentication.rb +66 -0
- data/app/controllers/concerns/iron/authorization.rb +11 -0
- data/app/controllers/concerns/iron/locale_aware.rb +20 -0
- data/app/controllers/concerns/iron/web_page.rb +18 -0
- data/app/controllers/iron/application_controller.rb +7 -0
- data/app/controllers/iron/block_definitions/field_definitions_controller.rb +8 -0
- data/app/controllers/iron/block_definitions_controller.rb +52 -0
- data/app/controllers/iron/blocks_controller.rb +12 -0
- data/app/controllers/iron/content_types/field_definitions_controller.rb +8 -0
- data/app/controllers/iron/content_types_controller.rb +52 -0
- data/app/controllers/iron/entries_controller.rb +103 -0
- data/app/controllers/iron/field_definitions_controller.rb +55 -0
- data/app/controllers/iron/first_runs_controller.rb +29 -0
- data/app/controllers/iron/home_controller.rb +6 -0
- data/app/controllers/iron/icons_controller.rb +10 -0
- data/app/controllers/iron/locales_controller.rb +52 -0
- data/app/controllers/iron/passwords_controller.rb +36 -0
- data/app/controllers/iron/references_controller.rb +11 -0
- data/app/controllers/iron/schemas_controller.rb +32 -0
- data/app/controllers/iron/sessions_controller.rb +32 -0
- data/app/controllers/iron/settings_controller.rb +23 -0
- data/app/controllers/iron/users_controller.rb +66 -0
- data/app/helpers/iron/application_helper.rb +25 -0
- data/app/helpers/iron/avatar_helper.rb +35 -0
- data/app/helpers/iron/block_helper.rb +4 -0
- data/app/helpers/iron/buttons_helper.rb +14 -0
- data/app/helpers/iron/components/badge_helper.rb +70 -0
- data/app/helpers/iron/components/dropdown_helper.rb +161 -0
- data/app/helpers/iron/content_types_helper.rb +11 -0
- data/app/helpers/iron/entries_helper.rb +13 -0
- data/app/helpers/iron/field_definitions_helper.rb +5 -0
- data/app/helpers/iron/form_builder.rb +43 -0
- data/app/helpers/iron/icons_helper.rb +30 -0
- data/app/helpers/iron/image_helper.rb +50 -0
- data/app/javascript/iron/application.js +6 -0
- data/app/javascript/iron/controllers/application.js +9 -0
- data/app/javascript/iron/controllers/element_controller.js +11 -0
- data/app/javascript/iron/controllers/file_upload_controller.js +18 -0
- data/app/javascript/iron/controllers/form_controller.js +7 -0
- data/app/javascript/iron/controllers/handle_controller.js +27 -0
- data/app/javascript/iron/controllers/icon_picker_controller.js +48 -0
- data/app/javascript/iron/controllers/index.js +4 -0
- data/app/javascript/iron/controllers/sortable_list_controller.js +125 -0
- data/app/javascript/iron/controllers/toggle_controller.js +14 -0
- data/app/javascript/iron/controllers/trix_controller.js +19 -0
- data/app/javascript/iron/lib/lexorank.js +51 -0
- data/app/jobs/iron/application_job.rb +4 -0
- data/app/jobs/iron/generate_entry_routes_job.rb +15 -0
- data/app/mailers/iron/application_mailer.rb +6 -0
- data/app/mailers/passwords_mailer.rb +6 -0
- data/app/models/concerns/iron/csv_serializable.rb +28 -0
- data/app/models/iron/account/joinable.rb +16 -0
- data/app/models/iron/account.rb +13 -0
- data/app/models/iron/application_record.rb +5 -0
- data/app/models/iron/block_definition/portable.rb +20 -0
- data/app/models/iron/block_definition.rb +10 -0
- data/app/models/iron/content_type/field_queryable.rb +101 -0
- data/app/models/iron/content_type/portable.rb +30 -0
- data/app/models/iron/content_type/titlable.rb +13 -0
- data/app/models/iron/content_type/web_publishable.rb +47 -0
- data/app/models/iron/content_type.rb +39 -0
- data/app/models/iron/content_types/collection.rb +6 -0
- data/app/models/iron/content_types/single.rb +9 -0
- data/app/models/iron/current.rb +12 -0
- data/app/models/iron/entry/deep_validation.rb +32 -0
- data/app/models/iron/entry/presentable.rb +40 -0
- data/app/models/iron/entry/schemable.rb +16 -0
- data/app/models/iron/entry/titlable.rb +35 -0
- data/app/models/iron/entry/web_publishable.rb +85 -0
- data/app/models/iron/entry.rb +16 -0
- data/app/models/iron/field/belongs_to_entry.rb +29 -0
- data/app/models/iron/field.rb +20 -0
- data/app/models/iron/field_definition/portable.rb +36 -0
- data/app/models/iron/field_definition.rb +42 -0
- data/app/models/iron/field_definitions/block.rb +23 -0
- data/app/models/iron/field_definitions/block_list.rb +8 -0
- data/app/models/iron/field_definitions/boolean.rb +7 -0
- data/app/models/iron/field_definitions/date.rb +7 -0
- data/app/models/iron/field_definitions/file.rb +67 -0
- data/app/models/iron/field_definitions/number.rb +7 -0
- data/app/models/iron/field_definitions/reference.rb +8 -0
- data/app/models/iron/field_definitions/reference_list.rb +8 -0
- data/app/models/iron/field_definitions/rich_text_area.rb +4 -0
- data/app/models/iron/field_definitions/text_area.rb +7 -0
- data/app/models/iron/field_definitions/text_field.rb +25 -0
- data/app/models/iron/fields/block.rb +40 -0
- data/app/models/iron/fields/block_list.rb +16 -0
- data/app/models/iron/fields/boolean.rb +7 -0
- data/app/models/iron/fields/date.rb +7 -0
- data/app/models/iron/fields/file.rb +29 -0
- data/app/models/iron/fields/number.rb +7 -0
- data/app/models/iron/fields/reference.rb +14 -0
- data/app/models/iron/fields/reference_list.rb +17 -0
- data/app/models/iron/fields/rich_text_area.rb +9 -0
- data/app/models/iron/fields/text_area.rb +7 -0
- data/app/models/iron/fields/text_field.rb +19 -0
- data/app/models/iron/first_run.rb +13 -0
- data/app/models/iron/icon_catalog.rb +45 -0
- data/app/models/iron/locale.rb +30 -0
- data/app/models/iron/reference.rb +8 -0
- data/app/models/iron/schema_archive.rb +71 -0
- data/app/models/iron/schema_exporter.rb +15 -0
- data/app/models/iron/schema_importer.rb +278 -0
- data/app/models/iron/session.rb +5 -0
- data/app/models/iron/user/role.rb +21 -0
- data/app/models/iron/user.rb +37 -0
- data/app/views/active_storage/blobs/_blob.html.erb +14 -0
- data/app/views/iron/block_definitions/_block_definition.html.erb +21 -0
- data/app/views/iron/block_definitions/_form.html.erb +46 -0
- data/app/views/iron/block_definitions/edit.html.erb +8 -0
- data/app/views/iron/block_definitions/index.html.erb +18 -0
- data/app/views/iron/block_definitions/new.html.erb +8 -0
- data/app/views/iron/block_definitions/show.html.erb +34 -0
- data/app/views/iron/blocks/new.turbo_stream.erb +6 -0
- data/app/views/iron/content_types/_content_type.html.erb +21 -0
- data/app/views/iron/content_types/_form.html.erb +137 -0
- data/app/views/iron/content_types/edit.html.erb +8 -0
- data/app/views/iron/content_types/index.html.erb +28 -0
- data/app/views/iron/content_types/new.html.erb +8 -0
- data/app/views/iron/content_types/show.html.erb +49 -0
- data/app/views/iron/entries/_entry.html.erb +17 -0
- data/app/views/iron/entries/_entry_option.html.erb +19 -0
- data/app/views/iron/entries/_form.html.erb +63 -0
- data/app/views/iron/entries/edit.html.erb +52 -0
- data/app/views/iron/entries/entry.html.erb +21 -0
- data/app/views/iron/entries/fields/_block.html.erb +92 -0
- data/app/views/iron/entries/fields/_block_list.html.erb +45 -0
- data/app/views/iron/entries/fields/_boolean.html.erb +13 -0
- data/app/views/iron/entries/fields/_date.html.erb +11 -0
- data/app/views/iron/entries/fields/_file.html.erb +60 -0
- data/app/views/iron/entries/fields/_number.html.erb +11 -0
- data/app/views/iron/entries/fields/_reference.html.erb +12 -0
- data/app/views/iron/entries/fields/_reference_item.html.erb +28 -0
- data/app/views/iron/entries/fields/_reference_list.html.erb +44 -0
- data/app/views/iron/entries/fields/_rich_text_area.html.erb +13 -0
- data/app/views/iron/entries/fields/_text_area.html.erb +11 -0
- data/app/views/iron/entries/fields/_text_field.html.erb +28 -0
- data/app/views/iron/entries/index.html.erb +21 -0
- data/app/views/iron/entries/new.html.erb +8 -0
- data/app/views/iron/entries/search.html.erb +33 -0
- data/app/views/iron/field_definitions/_field_definition.html.erb +37 -0
- data/app/views/iron/field_definitions/block/_form.html.erb +16 -0
- data/app/views/iron/field_definitions/block_list/_form.html.erb +13 -0
- data/app/views/iron/field_definitions/boolean/_form.html.erb +2 -0
- data/app/views/iron/field_definitions/date/_form.html.erb +2 -0
- data/app/views/iron/field_definitions/edit.html.erb +16 -0
- data/app/views/iron/field_definitions/file/_form.html.erb +40 -0
- data/app/views/iron/field_definitions/index.html.erb +14 -0
- data/app/views/iron/field_definitions/layouts/_form.html.erb +47 -0
- data/app/views/iron/field_definitions/new.html.erb +22 -0
- data/app/views/iron/field_definitions/number/_form.html.erb +2 -0
- data/app/views/iron/field_definitions/reference/_form.html.erb +13 -0
- data/app/views/iron/field_definitions/reference_list/_form.html.erb +13 -0
- data/app/views/iron/field_definitions/rich_text_area/_form.html.erb +2 -0
- data/app/views/iron/field_definitions/text_area/_form.html.erb +2 -0
- data/app/views/iron/field_definitions/text_field/_form.html.erb +17 -0
- data/app/views/iron/first_runs/show.html.erb +44 -0
- data/app/views/iron/home/show.html.erb +2 -0
- data/app/views/iron/icons/index.html.erb +21 -0
- data/app/views/iron/locales/_form.html.erb +24 -0
- data/app/views/iron/locales/_locale.html.erb +15 -0
- data/app/views/iron/locales/edit.html.erb +8 -0
- data/app/views/iron/locales/index.html.erb +12 -0
- data/app/views/iron/locales/new.html.erb +8 -0
- data/app/views/iron/passwords/edit.html.erb +7 -0
- data/app/views/iron/passwords/new.html.erb +26 -0
- data/app/views/iron/passwords_mailer/reset.html.erb +4 -0
- data/app/views/iron/passwords_mailer/reset.text.erb +2 -0
- data/app/views/iron/published_pages/show.html.erb +10 -0
- data/app/views/iron/references/new.turbo_stream.erb +6 -0
- data/app/views/iron/schemas/new.html.erb +57 -0
- data/app/views/iron/sessions/new.html.erb +36 -0
- data/app/views/iron/settings/show.html.erb +38 -0
- data/app/views/iron/shared/_icon_picker.html.erb +60 -0
- data/app/views/iron/users/_form.html.erb +21 -0
- data/app/views/iron/users/_user.html.erb +27 -0
- data/app/views/iron/users/edit.html.erb +12 -0
- data/app/views/iron/users/index.html.erb +21 -0
- data/app/views/iron/users/new.html.erb +29 -0
- data/app/views/iron/users/show.html.erb +21 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/iron/_sidebar.html.erb +83 -0
- data/app/views/layouts/iron/_sidebar_item.html.erb +14 -0
- data/app/views/layouts/iron/_toast.html.erb +29 -0
- data/app/views/layouts/iron/application.html.erb +46 -0
- data/app/views/layouts/iron/authentication.html.erb +41 -0
- data/config/importmap.rb +9 -0
- data/config/routes.rb +40 -0
- data/db/migrate/20250422131656_initial_iron_schema.rb +105 -0
- data/db/migrate/20250427100754_create_iron_accounts.rb +10 -0
- data/db/migrate/20250427104020_add_role_to_iron_users.rb +5 -0
- data/db/migrate/20250428094923_add_default_iron_locale_to_iron_account.rb +5 -0
- data/db/migrate/20250504144731_create_join_table_content_types_field_definitions.rb +25 -0
- data/db/migrate/20250505090716_add_title_field_definition_to_iron_content_types.rb +5 -0
- data/db/migrate/20250512210352_create_iron_references.rb +11 -0
- data/db/migrate/20250524220355_add_referenced_entry_id_to_iron_fields.rb +5 -0
- data/db/migrate/20250529211703_add_metadata_to_iron_field_definitions.rb +5 -0
- data/db/migrate/20250529211704_add_value_boolean_to_iron_fields.rb +5 -0
- data/db/migrate/20250531172837_add_type_to_iron_content_types.rb +5 -0
- data/db/migrate/20250601080146_add_icon_to_iron_content_types.rb +5 -0
- data/db/migrate/20250609091605_add_web_publishing_to_iron_content_types.rb +7 -0
- data/db/migrate/20250609091813_add_route_to_iron_entries.rb +6 -0
- data/lib/generators/iron/pages/pages_generator.rb +34 -0
- data/lib/generators/iron/pages/templates/pages_controller.rb +14 -0
- data/lib/generators/iron/pages/templates/show.html.erb +9 -0
- data/lib/generators/iron/template/template_generator.rb +35 -0
- data/lib/generators/iron/template/templates/content_type.html.erb +5 -0
- data/lib/iron/cva.rb +69 -0
- data/lib/iron/engine.rb +42 -0
- data/lib/iron/image_analyzer.rb +26 -0
- data/lib/iron/lexorank/exceptions.rb +4 -0
- data/lib/iron/lexorank/rankable.rb +185 -0
- data/lib/iron/lexorank/rebalance_rank_job.rb +10 -0
- data/lib/iron/lexorank/utils.rb +77 -0
- data/lib/iron/lexorank.rb +7 -0
- data/lib/iron/routing.rb +66 -0
- data/lib/iron/sdk.rb +19 -0
- data/lib/iron/version.rb +3 -0
- data/lib/iron.rb +8 -0
- data/lib/puma/plugin/iron_tailwindcss.rb +78 -0
- metadata +458 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module Iron
|
2
|
+
class FieldDefinitions::File < FieldDefinition
|
3
|
+
FILE_TYPE_PRESETS = {
|
4
|
+
"images" => {
|
5
|
+
extensions: %w[jpg jpeg png gif webp svg heic],
|
6
|
+
mime_types: %w[image/jpeg image/png image/gif image/webp image/svg+xml image/heic]
|
7
|
+
},
|
8
|
+
"videos" => {
|
9
|
+
extensions: %w[mp4 mov avi webm],
|
10
|
+
mime_types: %w[video/mp4 video/quicktime video/x-msvideo video/webm]
|
11
|
+
},
|
12
|
+
"documents" => {
|
13
|
+
extensions: %w[pdf doc docx xls xlsx ppt pptx txt],
|
14
|
+
mime_types: %w[
|
15
|
+
application/pdf
|
16
|
+
application/msword
|
17
|
+
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
18
|
+
application/vnd.ms-excel
|
19
|
+
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
20
|
+
application/vnd.ms-powerpoint
|
21
|
+
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
22
|
+
text/plain
|
23
|
+
]
|
24
|
+
},
|
25
|
+
"audio" => {
|
26
|
+
extensions: %w[mp3 wav ogg m4a],
|
27
|
+
mime_types: %w[audio/mpeg audio/wav audio/ogg audio/mp4]
|
28
|
+
}
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
store_accessor :metadata, :file_scope, :selected_presets
|
32
|
+
|
33
|
+
def file_scope
|
34
|
+
super || "all"
|
35
|
+
end
|
36
|
+
|
37
|
+
def selected_presets
|
38
|
+
super || []
|
39
|
+
end
|
40
|
+
|
41
|
+
def accepted_extensions
|
42
|
+
return nil if file_scope == "all"
|
43
|
+
|
44
|
+
extensions = []
|
45
|
+
selected_presets.each do |preset|
|
46
|
+
extensions.concat(FILE_TYPE_PRESETS[preset][:extensions]) if FILE_TYPE_PRESETS[preset]
|
47
|
+
end
|
48
|
+
extensions.uniq
|
49
|
+
end
|
50
|
+
|
51
|
+
def accepted_mime_types
|
52
|
+
return nil if file_scope == "all"
|
53
|
+
|
54
|
+
mime_types = []
|
55
|
+
selected_presets.each do |preset|
|
56
|
+
mime_types.concat(FILE_TYPE_PRESETS[preset][:mime_types]) if FILE_TYPE_PRESETS[preset]
|
57
|
+
end
|
58
|
+
mime_types.uniq
|
59
|
+
end
|
60
|
+
|
61
|
+
def validation_description
|
62
|
+
return "All file types accepted" if file_scope == "all"
|
63
|
+
|
64
|
+
"Accepted formats: #{accepted_extensions.join(', ')}" if accepted_extensions.present?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Iron
|
2
|
+
class FieldDefinitions::TextField < FieldDefinition
|
3
|
+
store_accessor :metadata, :allowed_values, :required
|
4
|
+
|
5
|
+
def allowed_values_text
|
6
|
+
Array(allowed_values).join("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
def allowed_values_text=(text)
|
10
|
+
self.allowed_values = text.to_s.lines.map(&:strip).reject(&:blank?)
|
11
|
+
end
|
12
|
+
|
13
|
+
def required
|
14
|
+
ActiveModel::Type::Boolean.new.cast(super)
|
15
|
+
end
|
16
|
+
|
17
|
+
def required=(value)
|
18
|
+
super(ActiveModel::Type::Boolean.new.cast(value))
|
19
|
+
end
|
20
|
+
|
21
|
+
def value_column
|
22
|
+
:value_string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::Block < Field
|
3
|
+
has_many :fields, as: :parent, inverse_of: :parent, dependent: :destroy
|
4
|
+
accepts_nested_attributes_for :fields, allow_destroy: true
|
5
|
+
|
6
|
+
has_rank scoped_by: :parent
|
7
|
+
|
8
|
+
def block_list
|
9
|
+
parent.is_a?(Iron::Fields::BlockList) ? parent : nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_or_build_field(definition)
|
13
|
+
fields.find { |f| f.definition == definition } || fields.build(type: definition.field_type, entry:, definition: definition, locale:)
|
14
|
+
end
|
15
|
+
|
16
|
+
def value
|
17
|
+
locale = Iron::Current.locale || Iron::Locale.default
|
18
|
+
|
19
|
+
block_definition = if definition.is_a?(Iron::FieldDefinitions::Block)
|
20
|
+
definition.supported_block_definition
|
21
|
+
else
|
22
|
+
definition
|
23
|
+
end
|
24
|
+
|
25
|
+
result = OpenStruct.new(id: id, _type: block_definition.handle)
|
26
|
+
|
27
|
+
block_definition.field_definitions.each do |field_definition|
|
28
|
+
field = fields.find do |f|
|
29
|
+
f.definition_id == field_definition.id &&
|
30
|
+
f.definition_type == field_definition.class.base_class.to_s &&
|
31
|
+
f.locale_id == locale.id
|
32
|
+
end
|
33
|
+
|
34
|
+
result[field_definition.handle] = field&.value
|
35
|
+
end
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::BlockList < Field
|
3
|
+
has_many :blocks, -> { ranked }, as: :parent, inverse_of: :parent, dependent: :destroy do
|
4
|
+
def build_for(block_definition)
|
5
|
+
block_list = proxy_association.owner
|
6
|
+
build(entry: block_list.entry, definition: block_definition, locale: block_list.locale)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
accepts_nested_attributes_for :blocks, allow_destroy: true
|
11
|
+
|
12
|
+
def value
|
13
|
+
blocks.map(&:value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::File < Field
|
3
|
+
has_one_attached :file
|
4
|
+
|
5
|
+
validate :file_type_matches_restrictions, if: :definition_restricts_file_type?
|
6
|
+
|
7
|
+
def value
|
8
|
+
file.attached? ? file : nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def image?
|
12
|
+
file.attached? && file.blob.content_type&.start_with?("image/")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def definition_restricts_file_type?
|
18
|
+
file.attached? &&
|
19
|
+
definition.file_scope == "specific" &&
|
20
|
+
definition.accepted_mime_types.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
def file_type_matches_restrictions
|
24
|
+
unless file.content_type.in? definition.accepted_mime_types
|
25
|
+
errors.add(:base, "must be one of the accepted formats: #{definition.accepted_extensions.join(', ')}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::Reference < Field
|
3
|
+
belongs_to :referenced_entry, class_name: "Iron::Entry", optional: true
|
4
|
+
|
5
|
+
def value
|
6
|
+
if referenced_entry.present?
|
7
|
+
OpenStruct.new(
|
8
|
+
id: referenced_entry_id,
|
9
|
+
_type: referenced_entry.content_type.handle
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::ReferenceList < Field
|
3
|
+
has_many :references, -> { ranked }, foreign_key: :list_id, dependent: :destroy, class_name: "Iron::Reference", inverse_of: "list"
|
4
|
+
has_many :entries, through: :references
|
5
|
+
|
6
|
+
accepts_nested_attributes_for :references, allow_destroy: true
|
7
|
+
|
8
|
+
def value
|
9
|
+
references.map do |reference|
|
10
|
+
OpenStruct.new(
|
11
|
+
id: reference.entry_id,
|
12
|
+
_type: reference.entry.content_type.handle
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Iron
|
2
|
+
class Fields::TextField < Field
|
3
|
+
validate :validate_required_field
|
4
|
+
|
5
|
+
def value
|
6
|
+
value_string
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def validate_required_field
|
12
|
+
return unless definition.required
|
13
|
+
|
14
|
+
if value_string.blank?
|
15
|
+
errors.add(:base, :blank)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Iron
|
2
|
+
class FirstRun
|
3
|
+
ACCOUNT_NAME = "Iron"
|
4
|
+
|
5
|
+
def self.create!(user_params)
|
6
|
+
english_locale = Locale.create!(code: "en", name: "English")
|
7
|
+
|
8
|
+
account = Account.create!(name: ACCOUNT_NAME, default_locale: english_locale)
|
9
|
+
|
10
|
+
User.create!(user_params.merge(role: :administrator))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Iron
|
2
|
+
class IconCatalog
|
3
|
+
class << self
|
4
|
+
def all
|
5
|
+
@cached_icons ||= parse_sprite_icons
|
6
|
+
end
|
7
|
+
|
8
|
+
def clear_cache!
|
9
|
+
@cached_icons = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def refresh!
|
13
|
+
clear_cache!
|
14
|
+
all
|
15
|
+
end
|
16
|
+
|
17
|
+
def include?(icon_name)
|
18
|
+
all.include?(icon_name.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache_key
|
22
|
+
raise "Icon sprite file not found at #{sprite_file_path}" unless File.exist?(sprite_file_path)
|
23
|
+
|
24
|
+
mtime = File.mtime(sprite_file_path).to_i
|
25
|
+
"iron/icon_catalog/#{mtime}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse_sprite_icons
|
31
|
+
return [] unless File.exist?(sprite_file_path)
|
32
|
+
|
33
|
+
sprite_content = File.read(sprite_file_path)
|
34
|
+
sprite_content.scan(/id="([^"]+)"/).flatten.sort
|
35
|
+
rescue => e
|
36
|
+
Rails.logger.warn "Failed to parse icon sprite: #{e.message}"
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
|
40
|
+
def sprite_file_path
|
41
|
+
Iron::Engine.root.join("app", "assets", "images", "iron", "icons", "sprite.svg")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Iron
|
2
|
+
class Locale < ApplicationRecord
|
3
|
+
validates :code, presence: true
|
4
|
+
validates :name, presence: true
|
5
|
+
|
6
|
+
before_destroy :ensure_not_default_locale
|
7
|
+
|
8
|
+
def self.detect(request)
|
9
|
+
locale_param = request.params[:locale]
|
10
|
+
Locale.find_by(code: locale_param) if locale_param.present?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.default
|
14
|
+
Account.first&.default_locale
|
15
|
+
end
|
16
|
+
|
17
|
+
def default?
|
18
|
+
self == Locale.default
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def ensure_not_default_locale
|
24
|
+
if default?
|
25
|
+
errors.add(:base, "Cannot delete locale while it is set as default for an account")
|
26
|
+
throw :abort
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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
|
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
|
@@ -0,0 +1,15 @@
|
|
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
|