Class: LatoCms::Page
- Inherits:
-
ApplicationRecord
- Object
- ApplicationRecord
- LatoCms::Page
- Includes:
- LatoSpaces::Associable, LatoSpaces::AssociableRequired, LatoSpaces::AssociableUnique
- Defined in:
- app/models/lato_cms/page.rb
Instance Attribute Summary collapse
-
#actions ⇒ Object
Returns the value of attribute actions.
Class Method Summary collapse
- .parse_translated_field_values(content, expected_count) ⇒ Object
- .translate_fields_prompt(fields, target_locale) ⇒ Object
Instance Method Summary collapse
- #as_json(options = {}) ⇒ Object
- #component_effectively_enabled?(template_component_id, default: true) ⇒ Boolean
- #component_enabled?(template_component_id, default: true) ⇒ Boolean
- #component_required?(template_component_id) ⇒ Boolean
-
#link_translation(other_page) ⇒ Object
Links another page as a translation of this one, merging any existing groups so all members share a single translation_group_id.
- #set_component_enabled(template_component_id, enabled) ⇒ Object
- #template ⇒ Object
- #template_available? ⇒ Boolean
- #template_components ⇒ Object
- #template_name ⇒ Object
-
#translate_component_fields!(template_component_id) ⇒ Object
Translates a component's free-text fields (see LatoCms::PageField::TRANSLATABLE_FIELD_TYPES) into this page's own locale via the configured LLM, in one batched call, overwriting their values in place.
-
#translation_candidates(locale) ⇒ Object
Pages of a given locale that can be linked to this page: same spaces group, not this page, and not already part of any translation group.
-
#translation_for(locale) ⇒ Object
The linked translation for a given locale, if any.
-
#translations ⇒ Object
Sibling pages linked as translations of this page.
-
#unlink_translation(other_page) ⇒ Object
Removes a page from this page's translation group.
Instance Attribute Details
#actions ⇒ Object
Returns the value of attribute actions.
7 8 9 |
# File 'app/models/lato_cms/page.rb', line 7 def actions @actions end |
Class Method Details
.parse_translated_field_values(content, expected_count) ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'app/models/lato_cms/page.rb', line 109 def self.parse_translated_field_values(content, expected_count) return [] if content.blank? parsed = JSON.parse(content[/\[.*\]/m] || content) return [] unless parsed.is_a?(Array) && parsed.size == expected_count parsed.map(&:to_s) rescue JSON::ParserError [] end |
.translate_fields_prompt(fields, target_locale) ⇒ Object
103 104 105 106 107 |
# File 'app/models/lato_cms/page.rb', line 103 def self.translate_fields_prompt(fields, target_locale) "Translate each string in this JSON array into #{target_locale} (language code). Preserve any HTML " \ "tags exactly as-is, translate only the visible text. Respond with a JSON array of the same length " \ "and in the same order, translated strings only, no markdown, no explanation.\n\n#{fields.map(&:value).to_json}" end |
Instance Method Details
#as_json(options = {}) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'app/models/lato_cms/page.rb', line 160 def as_json( = {}) data = { id: id, title: title, permalink: permalink, locale: locale, template_id: template_id, template_name: template_name, frontend_url: frontend_url, translations: translations_json, created_at: created_at, updated_at: updated_at } if [:include_fields] fields.load unless fields.loaded? data[:fields] = build_fields_json(template_components.select { |tc| component_effectively_enabled?(tc[:template_component_id]) }) end data end |
#component_effectively_enabled?(template_component_id, default: true) ⇒ Boolean
149 150 151 152 153 |
# File 'app/models/lato_cms/page.rb', line 149 def component_effectively_enabled?(template_component_id, default: true) return true if component_required?(template_component_id) component_enabled?(template_component_id, default: default) end |
#component_enabled?(template_component_id, default: true) ⇒ Boolean
137 138 139 140 141 142 |
# File 'app/models/lato_cms/page.rb', line 137 def component_enabled?(template_component_id, default: true) value = (component_states || {})[template_component_id.to_s] return default if value.nil? value == true end |
#component_required?(template_component_id) ⇒ Boolean
144 145 146 147 |
# File 'app/models/lato_cms/page.rb', line 144 def component_required?(template_component_id) tc = template_components.find { |component| component[:template_component_id] == template_component_id.to_s } tc && tc[:required] == true end |
#link_translation(other_page) ⇒ Object
Links another page as a translation of this one, merging any existing groups so all members share a single translation_group_id.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'app/models/lato_cms/page.rb', line 49 def link_translation(other_page) return false unless other_page && other_page.id != id return false unless other_page.lato_spaces_group_id == lato_spaces_group_id group_id = translation_group_id.presence || other_page.translation_group_id.presence || SecureRandom.uuid old_group_id = other_page.translation_group_id.presence transaction do update!(translation_group_id: group_id) # Re-point every member of the other page's former group to keep groups merged. if old_group_id && old_group_id != group_id LatoCms::Page .for_lato_spaces_group(lato_spaces_group_id) .where(translation_group_id: old_group_id) .update_all(translation_group_id: group_id) else other_page.update!(translation_group_id: group_id) end end true end |
#set_component_enabled(template_component_id, enabled) ⇒ Object
155 156 157 158 |
# File 'app/models/lato_cms/page.rb', line 155 def set_component_enabled(template_component_id, enabled) key = template_component_id.to_s self.component_states = (component_states || {}).merge(key => ActiveModel::Type::Boolean.new.cast(enabled)) end |
#template ⇒ Object
120 121 122 |
# File 'app/models/lato_cms/page.rb', line 120 def template LatoCms::TemplateManager.find_template(template_id) end |
#template_available? ⇒ Boolean
128 129 130 |
# File 'app/models/lato_cms/page.rb', line 128 def template_available? template_id.blank? || template.present? end |
#template_components ⇒ Object
132 133 134 135 |
# File 'app/models/lato_cms/page.rb', line 132 def template_components return [] unless template LatoCms::TemplateManager.resolve_template_components(template) end |
#template_name ⇒ Object
124 125 126 |
# File 'app/models/lato_cms/page.rb', line 124 def template_name template&.dig('name') || template_id end |
#translate_component_fields!(template_component_id) ⇒ Object
Translates a component's free-text fields (see LatoCms::PageField::TRANSLATABLE_FIELD_TYPES) into this page's own locale via the configured LLM, in one batched call, overwriting their values in place. Used by PagesController#clone_component_action's "Clone & translate" option, as the async half run via LatoCms::TranslateComponentFieldsJob (a Lato::Operation): the fields themselves are already cloned by the time this runs, synchronously, so a failure here never loses the clone, only the translation. Raises on failure (LLM unreachable, malformed/short response) rather than swallowing it, since the admin is watching the operation's progress and a silent no-op would misreport success.
91 92 93 94 95 96 97 98 99 100 101 |
# File 'app/models/lato_cms/page.rb', line 91 def translate_component_fields!(template_component_id) targets = fields.where(template_component_id: template_component_id).reject(&:repeater_order?) .select { |f| LatoCms::PageField::TRANSLATABLE_FIELD_TYPES.include?(f.field_type) && f.value.present? } return if targets.empty? content = LatoCms::LlmClient.chat(messages: [{ role: 'user', content: self.class.translate_fields_prompt(targets, locale) }]) translated = self.class.parse_translated_field_values(content, targets.size) raise "The LLM response couldn't be parsed into #{targets.size} translated value(s)" if translated.blank? targets.each_with_index { |field, index| field.update_column(:value, translated[index]) if translated[index].present? } end |
#translation_candidates(locale) ⇒ Object
Pages of a given locale that can be linked to this page: same spaces group, not this page, and not already part of any translation group.
40 41 42 43 44 45 |
# File 'app/models/lato_cms/page.rb', line 40 def translation_candidates(locale) LatoCms::Page .for_lato_spaces_group(lato_spaces_group_id) .where(locale: locale.to_s, translation_group_id: nil) .where.not(id: id) end |
#translation_for(locale) ⇒ Object
The linked translation for a given locale, if any.
34 35 36 |
# File 'app/models/lato_cms/page.rb', line 34 def translation_for(locale) translations.find_by(locale: locale.to_s) end |
#translations ⇒ Object
Sibling pages linked as translations of this page.
24 25 26 27 28 29 30 31 |
# File 'app/models/lato_cms/page.rb', line 24 def translations return LatoCms::Page.none if translation_group_id.blank? LatoCms::Page .for_lato_spaces_group(lato_spaces_group_id) .where(translation_group_id: translation_group_id) .where.not(id: id) end |
#unlink_translation(other_page) ⇒ Object
Removes a page from this page's translation group.
73 74 75 76 77 78 |
# File 'app/models/lato_cms/page.rb', line 73 def unlink_translation(other_page) return false unless other_page && other_page.translation_group_id == translation_group_id other_page.update!(translation_group_id: nil) true end |