module Avo::Concerns::HasItems
def deprecated_dsl_api(name, method)
def deprecated_dsl_api(name, method) message = "This API was deprecated. Please use the `#{name}` method inside the `#{method}` method." raise DeprecatedAPIError.new message end
def extract_fields(structure)
Extracts fields from a structure
def extract_fields(structure) structure.items.map do |item| if item.is_field? item elsif extractable_structure?(item) extract_fields(item) else nil end end.compact end
def extractable_structure?(structure)
Extractable structures are panels, rows and sidebars
def extractable_structure?(structure) structure.is_panel? || structure.is_row? || (structure.is_sidebar? && !view.index?) end
def field(name, as: :text, **args, &block)
def field(name, as: :text, **args, &block) deprecated_dsl_api __method__, "fields" end
def fields(**args)
def fields(**args) self.class.fields(**args) end
def get_field(id)
def get_field(id) get_field_definitions.find do |f| f.id == id.to_sym end end
def get_field_definitions(only_root: false)
def get_field_definitions(only_root: false) only_fields(only_root: only_root).map do |field| field.hydrate(resource: self, user: user, view: view) end end
def get_fields(panel: nil, reflection: nil, only_root: false)
def get_fields(panel: nil, reflection: nil, only_root: false) fields = get_field_definitions(only_root: only_root) .select do |field| # Get the fields for this view field.visible_in_view?(view: view) end .select do |field| field.visible? end .select do |field| is_valid = true # Strip out the reflection field in index queries with a parent association. if reflection.present? # regular non-polymorphic association # we're matching the reflection inverse_of foriegn key with the field's foreign_key if field.is_a?(Avo::Fields::BelongsToField) if field.respond_to?(:foreign_key) && reflection.inverse_of.present? && reflection.inverse_of.respond_to?(:foreign_key) && reflection.inverse_of.foreign_key == field.foreign_key is_valid = false end # polymorphic association if field.respond_to?(:foreign_key) && field.is_polymorphic? && reflection.respond_to?(:polymorphic?) && reflection.inverse_of.respond_to?(:foreign_key) && reflection.inverse_of.foreign_key == field.reflection.foreign_key is_valid = false end end end is_valid end if panel.present? fields = fields.select do |field| field.panel_name == panel end end # hydrate_fields fields fields.map do |field| field.dup.hydrate(record: @record, view: @view, resource: self) end end
def get_items
def get_items # Each group is built only by standalone items or items that have their own panel, keeping the items order grouped_items = visible_items.slice_when do |prev, curr| # Slice when the item type changes from standalone to panel or vice-versa is_standalone?(prev) != is_standalone?(curr) end.to_a.map do |group| { elements: group, is_standalone: is_standalone?(group.first) } end # Creates a main panel if it's missing and adds first standalone group of items if present if items.none? { |item| item.is_main_panel? } if (standalone_group = grouped_items.find { |group| group[:is_standalone] }).present? calculated_main_panel = Avo::Resources::Items::MainPanel.new hydrate_item calculated_main_panel calculated_main_panel.items_holder.items = standalone_group[:elements] grouped_items[grouped_items.index standalone_group] = { elements: [calculated_main_panel], is_standalone: false } end end # For each standalone group, wrap items in a panel grouped_items.select { |group| group[:is_standalone] }.each do |group| calculated_panel = Avo::Resources::Items::Panel.new calculated_panel.items_holder.items = group[:elements] hydrate_item calculated_panel group[:elements] = calculated_panel end grouped_items.flat_map { |group| group[:elements] } end
def get_preview_fields
def get_preview_fields get_field_definitions.select do |field| field.visible_in_view?(view: :preview) end end
def hydrate_item(item)
def hydrate_item(item) return unless item.respond_to? :hydrate res = self.class.ancestors.include?(Avo::BaseResource) ? self : resource item.hydrate(view: view, resource: res) end
def invalid_fields
def invalid_fields invalid_fields = items_holder.invalid_fields items_holder.items.each do |item| if item.respond_to? :items invalid_fields += item.invalid_fields end end invalid_fields end
def is_empty?
def is_empty? visible_items.blank? end
def is_standalone?(item)
def is_standalone?(item) item.is_field? && !item.has_own_panel? end
def items
def items items_holder&.items || [] end
def items_holder
def items_holder @items_holder || Avo::Resources::Items::Holder.new end
def only_fields(only_root: false)
def only_fields(only_root: false) fields = [] items.each do |item| next if item.nil? unless only_root # Dive into panels to fetch their fields if item.is_panel? fields << extract_fields(item) end # Dive into tabs to fetch their fields if item.is_tab_group? item.items.map do |tab| fields << extract_fields(tab) end end # Dive into sidebar to fetch their fields if item.is_sidebar? fields << extract_fields(item) end else # When `item.is_main_panel? == true` then also `item.is_panel? == true` # But when only_root == true we want to extract main_panel items # In all other circumstances items will get extracted when checking for `item.is_panel?` if item.is_main_panel? fields << extract_fields(item) end end if item.is_field? fields << item end if item.is_row? fields << extract_fields(tab) end end fields.flatten end
def panel(name = nil, **args, &block)
def panel(name = nil, **args, &block) deprecated_dsl_api __method__, "fields" end
def row(**args, &block)
def row(**args, &block) deprecated_dsl_api __method__, "fields" end
def set_target_to_top(fields)
def set_target_to_top(fields) fields.each do |field| field.target = :_top end end
def sidebar(**args, &block)
def sidebar(**args, &block) deprecated_dsl_api __method__, "fields" end
def tab_groups
def tab_groups self.class.tab_groups end
def tabs(**args, &block)
def tabs(**args, &block) deprecated_dsl_api __method__, "fields" end
def tool(klass, **args)
def tool(klass, **args) deprecated_dsl_api __method__, "fields" end
def visible_items
def visible_items items .map do |item| hydrate_item item if item.is_a? Avo::Resources::Items::TabGroup # Set the target to _top for all belongs_to fields in the tab group item.items.grep(Avo::Resources::Items::Tab).each do |tab| tab.items.grep(Avo::Resources::Items::Panel).each do |panel| set_target_to_top panel.items.grep(Avo::Fields::BelongsToField) panel.items.grep(Avo::Resources::Items::Row).each do |row| set_target_to_top row.items.grep(Avo::Fields::BelongsToField) end end end end item end .select do |item| item.visible? end .select do |item| if item.respond_to?(:visible_in_view?) item.visible_in_view? view: view else true end end .select do |item| # Check if record has the setter method # Next if the view is not on forms next true if !view.in?(%w[edit update new create]) # Skip items that don't have an id next true if !item.respond_to?(:id) # Skip tab groups and tabs # Skip headings # Skip location fields # On location field we can have field coordinates and setters with different names # like latitude and longitude next true if item.is_a?(Avo::Resources::Items::TabGroup) || item.is_a?(Avo::Resources::Items::Tab) || item.is_heading? || item.is_a?(Avo::Fields::LocationField) item.resource.record.respond_to?(:"#{item.try(:for_attribute) || item.id}=") end .select do |item| # Check if the user is authorized to view it. # This is usually used for has_* fields if item.respond_to? :authorized? item.authorized? else true end end .select do |item| !item.is_a?(Avo::Resources::Items::Sidebar) end.compact end