app/models/dialog.rb
class Dialog < ApplicationRecord
DIALOG_DIR_PLUGIN = 'content/service_dialogs'.freeze
# The following gets around a glob symbolic link issue
YAML_FILES_PATTERN = "{,*/**/}*.{yaml,yml}".freeze
default_value_for :system, false
has_many :dialog_tabs, -> { order(:position) }, :dependent => :destroy
validate :validate_children
include DialogMixin
has_many :resource_actions
virtual_has_one :content, :class_name => "Hash"
before_destroy :reject_if_has_resource_actions
validates :name, :unique_within_region => true
alias_attribute :name, :label
attr_accessor :target_resource
def self.seed
dialog_import_service = DialogImportService.new
Vmdb::Plugins.each do |plugin|
Dir.glob(plugin.root.join(DIALOG_DIR_PLUGIN, YAML_FILES_PATTERN)).each do |file|
dialog_import_service.import_all_service_dialogs_from_yaml_file(file)
end
end
end
def each_dialog_field(&block)
dialog_fields.each(&block)
end
def dialog_fields
dialog_tabs.flat_map(&:dialog_fields)
end
def field_name_exist?(name)
dialog_fields.any? { |df| df.name == name }
end
def dialog_resources
dialog_tabs
end
def automate_values_hash
dialog_fields.each_with_object({}) do |df, result|
if df.options.include?("multiple")
result[MiqAeEngine.create_automation_attribute_array_key(df.automate_key_name)] = df.automate_output_value
else
result[df.automate_key_name] = df.automate_output_value
end
end
end
def validate_children
# To remove the meaningless error message like "Dialog tabs is invalid" when child's validation fails
errors.delete(:dialog_tabs)
if dialog_tabs.blank?
errors.add(:base, _("Dialog %{dialog_label} must have at least one Tab") % {:dialog_label => label})
end
duplicate_field_names = dialog_fields.collect(&:name).duplicates
if duplicate_field_names.present?
errors.add(:base, _("Dialog field name cannot be duplicated on a dialog: %{duplicates}") % {:duplicates => duplicate_field_names.join(', ')})
end
dialog_tabs.each do |dt|
next if dt.valid?
dt.errors.full_messages.each do |err_msg|
errors.add(:base, _("Dialog %{dialog_label} / %{error_message}") %
{:dialog_label => label, :error_message => err_msg})
end
end
end
def validate_field_data
dialog_tabs.each_with_object([]) do |dt, result|
dt.dialog_groups.each do |dg|
dg.dialog_fields.each do |df|
err_msg = df.validate_field_data(dt, dg)
result << err_msg if err_msg.present?
end
end
end
end
def load_values_into_fields(values, ignore_nils = true)
values = values.with_indifferent_access
dialog_field_hash.each_value do |field|
field.dialog = self
new_value = values[field.automate_key_name] || values[field.name] || values.dig("parameters", field.name)
new_value ||= field.value unless ignore_nils
field.value = new_value
end
end
def initialize_with_given_values(values)
dialog_field_hash.each_value do |field|
field.dialog = self
field.value = values[field.automate_key_name] || values[field.name]
end
dialog_field_hash.each_value do |field|
given_value = values[field.automate_key_name] || values[field.name]
field.initialize_with_given_value(given_value)
end
end
def initialize_value_context(_values)
dialog_field_hash.each_value do |field|
field.dialog = self
end
dialog_field_hash.each_value(&:initialize_value_context)
end
def initialize_static_values
dialog_field_hash.each_value do |field|
field.dialog = self
end
dialog_field_hash.each_value(&:initialize_static_values)
end
def init_fields_with_values_for_request(values)
values = values.with_indifferent_access
dialog_field_hash.each do |_key, field|
field.value = values[field.automate_key_name] || values[field.name]
end
end
def field(name)
dialog_field_hash[name.to_s]
end
def content(target = nil, resource_action = nil, all_attributes = false)
return DialogSerializer.new.serialize(Array[self], all_attributes) if target.nil? && resource_action.nil?
workflow = ResourceActionWorkflow.new({}, User.current_user, resource_action, :target => target)
DialogSerializer.new.serialize(Array[workflow.dialog], all_attributes)
end
# Allows you to pass dialog tabs as a hash
# Will update any item passed with an ID,
# Creates a new item without an ID,
# Removes any items not passed in the content.
def update_tabs(tabs)
association_list = dialog_import_service.build_association_list("dialog_tabs" => tabs)
transaction do
updated_tabs = []
tabs.each do |dialog_tab|
if dialog_tab.key?('id')
DialogTab.find(dialog_tab['id']).tap do |tab|
tab.update(dialog_tab.except('id', 'href', 'dialog_id', 'dialog_groups'))
tab.update_dialog_groups(dialog_tab['dialog_groups'])
updated_tabs << tab
end
else
updated_tabs << dialog_import_service.build_dialog_tabs('dialog_tabs' => [dialog_tab]).first
end
end
self.dialog_tabs = updated_tabs
end
transaction do
dialog_import_service.build_associations(self, association_list)
end
end
def deep_copy(new_attributes = {})
new_dialog = dup
new_dialog.dialog_tabs = dialog_tabs.collect(&:deep_copy)
new_attributes.each do |attr, value|
new_dialog.send(:"#{attr}=", value)
end
new_dialog
end
private
def dialog_field_hash
@dialog_field_hash ||= dialog_fields.index_by { |df| df.name }
end
def reject_if_has_resource_actions
connected_components = resource_actions.collect do |ra|
# not find - we need nil when not found
ra.resource_type.constantize.find_by(:id => ra.resource_id)
end.compact
if connected_components.length > 0
errors.add(:base, _("Dialog cannot be deleted because it is connected to other components: %{components}") % {:components => connected_components.map { |cc| cc.class.name + ":" + cc.id.to_s + " - " + cc.try(:name) }})
throw :abort
end
end
def dialog_import_service
@dialog_import_service ||= DialogImportService.new
end
end