Module: CamaleonCms::CustomFieldsRead
- Extended by:
- ActiveSupport::Concern
- Included in:
- PostDefault, TermTaxonomy
- Defined in:
- app/models/concerns/camaleon_cms/custom_fields_read.rb
Instance Method Summary collapse
-
#add_custom_field_group(values, kind = 'Post') ⇒ Object
(also: #add_field_group)
add a custom field group for current model values: name: name for the group slug: key for group (if slug = _default => this will never show title and description) description: description for the group (optional) is_repeat: (boolean, optional -> default false) indicate if group support multiple format (repeated values) Model supported: PostType, Category, Post, Posttag, Widget, Plugin, Theme, User and Custom models pre configured Note 1: If you need add fields for all post's or all categories, then you need to add the fields into the post_type.add_custom_field_group(values, kind = "Post") post_type.add_custom_field_group(values, kind = "Category") Note 2: If you need add fields for only the Post_type, you have to use options or metas return: CustomFieldGroup object kind: argument only for PostType model: (Post | Category | PostTag), default => Post.
-
#add_custom_field_to_default_group(item, options, kind = 'Post') ⇒ Object
(also: #add_field)
Add custom fields for a default group: This will create a new group with slug=_default if it doesn't exist yet more details in add_manual_field(item, options) from custom field groups kind: argument only for PostType model: (Post | Category | PostTag), default => Post.
-
#get_field_groups(args = {}) ⇒ Object
get custom field groups for current object only: Post_type, Post, Category, PostTag, Widget, Site and a Custom model pre configured return collections CustomFieldGroup args: (Hash, used only for PostType Objects) kind: (Post (Default) | Category | PostTag | PostType).
-
#get_field_object(slug) ⇒ Object
return field object for current model.
-
#get_field_value(_key, _default = nil, group_number = 0) ⇒ Object
(also: #get_field, #get_field!)
get custom field value _key: custom field key if value is not present, then return default return default only if the field was not registered.
-
#get_field_values(_key, group_number = 0) ⇒ Object
(also: #get_fields)
get custom field values _key: custom field key.
-
#get_field_values_hash(include_options = false) ⇒ Object
return all values "single value", key2: [multiple, values], key3: value4 if include_options = false {values: "single value", options: {a:1, b: 4}, key2: [multiple, values], options: {a=1, b=2 }} if include_options = true.
-
#get_fields_grouped(field_keys) ⇒ Object
return the values of custom fields grouped by group_number field_keys: (array of keys) samples: my_object.get_fields_grouped(['my_slug1', 'my_slug2']) return: [ { 'my_slug1' => ["val 1"], 'my_slug2' => ['val 2']}, { 'my_slug1' => ["val2 for slug1"], 'my_slug2' => ['val 2 for slug2']} ] ==> 2 groups.
-
#get_fields_object(_f = true) ⇒ Object
return all custom fields for current element {options: {, values: [], name: '', ...} } deprecated f attribute.
-
#get_user_field_groups(site) ⇒ Object
get custom field groups for current user return collections CustomFieldGroup site: site object.
-
#save_field_value(key, value, order = 0, clear = true) ⇒ Object
Set custom field values for current model key: slug of the custom field value: array of values for multiple values support value: string value.
-
#set_field_value(key, value, args = {}) ⇒ Object
Set custom field values for current model (support for multiple group values) key: (string required) slug of the custom field value: (array | string) array: array of values for multiple values support, string: uniq value for the custom field args: field_id: (integer optional) identifier of the custom field order: order or position of the field value group_number: number of the group (only for custom field group with is_repeat enabled) clear: (boolean, default true) if true, will remove previous values and set these values, if not will append values return false if the was not saved because there is not present the field with slug: key sample: my_post.set_field_value('subtitle', 'Sub Title') sample: set values for a field (for fields that support multiple values) my_post.set_field_value('subtitle', ['Sub Title1', 'Sub Title2']) sample: my_post.set_field_value('subtitle', 'Sub Title', 1) sample: add field values for fields in group 1 my_post.set_field_value('subtitle', 'Sub Title', 1, group_number: 1).
-
#set_field_values(datas = {}) ⇒ Object
save all fields sent from browser (reservated for browser request) sample: { "0"=>{ "untitled-text-box"=>"values"=>{"0"=>"33333"}}, "1"=>{ "untitled-text-box"=>"values"=>{"0"=>"33333"}} }.
-
#update_field_value(key, value = nil, group_number = 0) ⇒ Object
Update the value for the field with slug _key Sample: my_posy.update_field_value('sub_title', 'Test Sub Title').
Instance Method Details
#add_custom_field_group(values, kind = 'Post') ⇒ Object Also known as: add_field_group
add a custom field group for current model values: name: name for the group slug: key for group (if slug = _default => this will never show title and description) description: description for the group (optional) is_repeat: (boolean, optional -> default false) indicate if group support multiple format (repeated values) Model supported: PostType, Category, Post, Posttag, Widget, Plugin, Theme, User and Custom models pre configured Note 1: If you need add fields for all post's or all categories, then you need to add the fields into the post_type.add_custom_field_group(values, kind = "Post") post_type.add_custom_field_group(values, kind = "Category") Note 2: If you need add fields for only the Post_type, you have to use options or metas return: CustomFieldGroup object kind: argument only for PostType model: (Post | Category | PostTag), default => Post. If kind = "" this will add group for all post_types
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 196 def add_custom_field_group(values, kind = 'Post') values = values.with_indifferent_access group = get_field_groups(kind).find_by_slug(values[:slug]) # rubocop:disable Rails/DynamicFindBy unless group site = _cama_get_field_site values[:parent_id] = site.id if site.present? group = if is_a?(CamaleonCms::Post) # harcoded for post to support custom field groups CamaleonCms::CustomFieldGroup.where(object_class: 'Post', objectid: id).create!(values) else get_field_groups(kind).create!(values) end end group end |
#add_custom_field_to_default_group(item, options, kind = 'Post') ⇒ Object Also known as: add_field
Add custom fields for a default group: This will create a new group with slug=_default if it doesn't exist yet more details in add_manual_field(item, options) from custom field groups kind: argument only for PostType model: (Post | Category | PostTag), default => Post
217 218 219 220 221 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 217 def add_custom_field_to_default_group(item, , kind = 'Post') g = get_field_groups(kind).find_by_slug('_default') # rubocop:disable Rails/DynamicFindBy g ||= add_custom_field_group({ name: 'Default Field Group', slug: '_default' }, kind) g.add_manual_field(item, ) end |
#get_field_groups(args = {}) ⇒ Object
get custom field groups for current object only: Post_type, Post, Category, PostTag, Widget, Site and a Custom model pre configured return collections CustomFieldGroup args: (Hash, used only for PostType Objects) kind: (Post (Default) | Category | PostTag | PostType). If kind = "Post" this will return all groups for all posts from current post type If kind = "Category" this will return all groups for all categories from current post type If kind = "PostTag" this will return all groups for all posttags from current post type If kind = "all" this will return all groups from current post type If kind = "post_type" this will return groups for all post_types Sample: mypost.get_field_groups() ==> return fields for posts from parent posttype Sample: mycat.get_field_groups() ==> return fields for categories from parent posttype Sample: myposttag.get_field_groups() ==> return fields for posttags from parent posttype Sample: mypost_type.get_field_groups('Post') => return custom fields for posts Sample: mypost_type.get_field_groups('Category') => return custom fields for posts Sample: mypost_type.get_field_groups('PostTag') => return custom fields for posts
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 24 def get_field_groups(args = {}) args = if args.is_a?(String) { kind: args, include_parent: false } else { kind: 'Post', include_parent: false }.merge!(args) end class_name = self.class.to_s.parseCamaClass case class_name when 'Category', 'PostTag' post_type.get_field_groups(class_name) when 'Post' if term_relationships.empty? && args[:cat_ids].nil? CamaleonCms::CustomFieldGroup.where( '(objectid = ? AND object_class = ?) OR (objectid = ? AND object_class = ?)', id || -1, class_name, post_type.id, "PostType_#{class_name}" ) else cat_ids = categories.map(&:id) cat_ids += args[:cat_ids] unless args[:cat_ids].nil? cat_ids += CamaleonCms::Category.find(cat_ids).map { |category| _category_parents_ids(category) }.flatten.uniq CamaleonCms::CustomFieldGroup.where( "(objectid = ? AND object_class = ?) OR (objectid = ? AND object_class = ?) OR (objectid IN (?) AND object_class = ?)", id || -1, class_name, post_type.id, "PostType_#{class_name}", cat_ids, "Category_#{class_name}" ) end when 'NavMenuItem' .custom_field_groups when 'Site' # Site overrides `custom_field_groups` to mean "every group this site owns" (foreign key # parent_id), unlike the object_class + objectid association every other model receives from # CommonRelationships. Scoping by placement here keeps groups meant for post types, themes or # menus off the site settings form, where their required fields blocked the submit and their # values were discarded on save anyway. custom_field_groups.where(object_class: 'Site', objectid: id) when 'PostType' case args[:kind] when 'all' CamaleonCms::CustomFieldGroup.where(object_class: %w[PostType_Post PostType_Post PostType_PostTag PostType], objectid: id) when 'post_type' custom_field_groups else CamaleonCms::CustomFieldGroup.where(object_class: "PostType_#{args[:kind]}", objectid: id) end else # 'Plugin' or other classes custom_field_groups end end |
#get_field_object(slug) ⇒ Object
return field object for current model
225 226 227 228 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 225 def get_field_object(slug) CamaleonCms::CustomField.where(slug: slug, parent_id: get_field_groups.pluck(:id)).first || CamaleonCms::CustomField.where(slug: slug, parent_id: get_field_groups({ include_parent: true })).first end |
#get_field_value(_key, _default = nil, group_number = 0) ⇒ Object Also known as: get_field, get_field!
get custom field value _key: custom field key if value is not present, then return default return default only if the field was not registered
89 90 91 92 93 94 95 96 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 89 def get_field_value(_key, _default = nil, group_number = 0) v = begin get_field_values(_key, group_number).first rescue StandardError _default end v.presence || _default end |
#get_field_values(_key, group_number = 0) ⇒ Object Also known as: get_fields
get custom field values _key: custom field key
102 103 104 105 106 107 108 109 110 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 102 def get_field_values(_key, group_number = 0) if custom_field_values.loaded? custom_field_values.select do |f| f.custom_field_slug == _key && f.group_number == group_number end.map(&:value) else custom_field_values.where(custom_field_slug: _key, group_number: group_number).pluck(:value) end end |
#get_field_values_hash(include_options = false) ⇒ Object
return all values "single value", key2: [multiple, values], key3: value4 if include_options = false {values: "single value", options: {a:1, b: 4}, key2: [multiple, values], options: {a=1, b=2 }} if include_options = true
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 146 def get_field_values_hash( = false) fields = {} custom_field_values.to_a.uniq.each do |field_value| custom_field = field_value.custom_field values = custom_field.values.where(objectid: id).pluck(:value) unless fields[field_value.custom_field_slug] = custom_field.[:multiple].to_s.to_bool ? values : values.first end next unless fields[field_value.custom_field_slug] = { values: custom_field.[:multiple].to_s.to_bool ? values : values.first, options: custom_field., id: custom_field.id } end fields.to_sym end |
#get_fields_grouped(field_keys) ⇒ Object
return the values of custom fields grouped by group_number field_keys: (array of keys) samples: my_object.get_fields_grouped(['my_slug1', 'my_slug2']) return: [ { 'my_slug1' => ["val 1"], 'my_slug2' => ['val 2']}, { 'my_slug1' => ["val2 for slug1"], 'my_slug2' => ['val 2 for slug2']} ] ==> 2 groups
return: [
{ 'my_slug1' => ["val 1", 'val 2 for fields multiple support'], 'my_slug2' => ['val 2']},
{ 'my_slug1' => ["val2 for slug1", 'val 2'], 'my_slug2' => ['val 2 for slug2']}
{ 'my_slug1' => ["val3 for slug1", 'val 3'], 'my_slug2' => ['val 3 for slug2']}
] ==> 3 groups
puts res[0]['my_slug1'].first ==> "val 1"
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 128 def get_fields_grouped(field_keys) res = [] custom_field_values.where(custom_field_slug: field_keys) .order(group_number: :asc).group_by(&:group_number).each_value do |group_fields| group = field_keys.each_with_object({}) do |field_key, hsh| _tmp = group_fields .each_with_object([]) { |field, ary| ary << field.value if field_key == field.custom_field_slug } hsh[field_key] = _tmp if _tmp.present? end res << group end res end |
#get_fields_object(_f = true) ⇒ Object
return all custom fields for current element {options: {, values: [], name: '', ...} } deprecated f attribute
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 167 def get_fields_object(_f = true) fields = {} custom_field_values.eager_load(:custom_field).to_a.uniq.each do |field_value| custom_field = field_value.custom_field # if custom_field.options[:show_frontend].to_s.to_bool values = custom_field.values.where(objectid: id).pluck(:value) fields[field_value.custom_field_slug] = custom_field.attributes.merge( options: custom_field., values: custom_field.[:multiple].to_s.to_bool ? values : values.first ) # end end fields.to_sym end |
#get_user_field_groups(site) ⇒ Object
get custom field groups for current user return collections CustomFieldGroup site: site object
79 80 81 82 83 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 79 def get_user_field_groups(site) # Demodulized to match the association scope and the placement the settings form stores # ('User' for a host Admin::User); parseCamaClass still strips a Draper Decorator suffix. site.custom_field_groups.where(object_class: self.class.to_s.parseCamaClass.demodulize) end |
#save_field_value(key, value, order = 0, clear = true) ⇒ Object
Set custom field values for current model key: slug of the custom field value: array of values for multiple values support value: string value
296 297 298 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 296 def save_field_value(key, value, order = 0, clear = true) set_field_value(key, value, { clear: clear, order: order }) end |
#set_field_value(key, value, args = {}) ⇒ Object
Set custom field values for current model (support for multiple group values) key: (string required) slug of the custom field value: (array | string) array: array of values for multiple values support, string: uniq value for the custom field args:
field_id: (integer optional) identifier of the custom field
order: order or position of the field value
group_number: number of the group (only for custom field group with is_repeat enabled)
clear: (boolean, default true) if true, will remove previous values and set these values,
if not will append values
return false if the was not saved because there is not present the field with slug: key sample: my_post.set_field_value('subtitle', 'Sub Title') sample: set values for a field (for fields that support multiple values) my_post.set_field_value('subtitle', ['Sub Title1', 'Sub Title2']) sample: my_post.set_field_value('subtitle', 'Sub Title', 1) sample: add field values for fields in group 1 my_post.set_field_value('subtitle', 'Sub Title', 1, group_number: 1)
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 317 def set_field_value(key, value, args = {}) args = { order: 0, group_number: 0, field_id: nil, clear: true }.merge!(args) if args[:field_id].blank? args[:field_id] = begin get_field_object(key).id rescue StandardError nil end end args[:field_id] ||= fallback_field_id_for(key) raise ArgumentError, "There is no custom field configured for #{key}" if args[:field_id].blank? v = { custom_field_id: args[:field_id], custom_field_slug: key, value: (value), term_order: args[:order], group_number: args[:group_number] } # Atomic (audit M7): clear the previous value and write the new one in one transaction, so a # value the scan-and-reject gate refuses (create! -> RecordInvalid) rolls the delete back and # the previously stored value survives instead of being destroyed. ActiveRecord::Base.transaction do if args[:clear] custom_field_values.where({ custom_field_slug: key, group_number: args[:group_number] }).delete_all end if value.is_a?(Array) value.each { |val| custom_field_values.create!(v.merge({ value: (val) })) } else custom_field_values.create!(v) end end end |
#set_field_values(datas = {}) ⇒ Object
save all fields sent from browser (reservated for browser request) sample: { "0"=>{ "untitled-text-box"=>"values"=>{"0"=>"33333"}}, "1"=>{ "untitled-text-box"=>"values"=>{"0"=>"33333"}} }
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 236 def set_field_values(datas = {}) return if datas.blank? ActiveRecord::Base.transaction do # A value identical to one already stored is not newly authored, so it must not be re-gated on # an unrelated edit (audit M8): the admin form round-trips every value and this method # delete/recreates them all, so without this skip a single pre-gate dangerous value would fail # the whole save and brick every later edit -- the trap Post#reject_untrusted_dangerous_content # avoids via content_changed?. Snapshot the stored pairs before clearing; skip the gate for any # recreated value that matches one. previously_stored = custom_field_values.pluck(:custom_field_slug, :value).to_set custom_field_values.delete_all datas.each_value do |fields_data| fields_data.each do |field_key, values| next if values[:values].blank? # Resolve the field id from the trusted slug, not the client-supplied values[:id]: a # forged custom_field_id points the row at a different field definition, and the # scan-and-reject gate keys off custom_field.options[:field_key] -- so a forged non-gated # id would slip markup past the gate for a gated (editor/uri/field_attrs) slug. Fall back # to values[:id] only when the slug names no field here (trusted/internal callers that # pass slugs outside this object's registered groups; permitted browser payloads never do). field_id = get_field_object(field_key)&.id || fallback_field_id_for(field_key) || values[:id] group_number = [values[:group_number].to_i, 0].max order_value = -1 ( if values[:values].is_a?(Hash) || values[:values].is_a?(ActionController::Parameters) values[:values].values else values[:values] end ).each do |value| row = custom_field_values.new( custom_field_id: field_id, custom_field_slug: field_key, value: (value), term_order: order_value += 1, group_number: group_number ) row.unfiltered_value! if previously_stored.include?([field_key.to_s, row.value]) row.save! end end end end end |
#update_field_value(key, value = nil, group_number = 0) ⇒ Object
Update the value for the field with slug _key Sample: my_posy.update_field_value('sub_title', 'Test Sub Title')
283 284 285 286 287 288 289 290 |
# File 'app/models/concerns/camaleon_cms/custom_fields_read.rb', line 283 def update_field_value(key, value = nil, group_number = 0) # Route through validations so the scan-and-reject gate applies (audit M5): update_column # skipped it, letting a dangerous value an untrusted caller supplies reach the frontend verbatim. custom_field_values.find_by(custom_field_slug: key, group_number: group_number) &.update(value: value) rescue StandardError nil end |