class Avo::BaseAction
def action_name
def action_name if name.present? return Avo::ExecutionContext.new( target: name, resource: @resource, record: @record, view: @view, arguments: @arguments, query: @query ).handle end self.class.to_s.demodulize.underscore.humanize(keep_id_suffix: true) end
def add_message(body, type = :info, timeout: nil)
def add_message(body, type = :info, timeout: nil) response[:messages] << { type: type, body: body&.truncate(320), timeout: timeout } end
def append_to_response(turbo_stream)
def append_to_response(turbo_stream) @appended_turbo_streams = turbo_stream end
def authorized?
def authorized? Avo::ExecutionContext.new( target: authorize, action: self, resource: @resource, view: @view, arguments: arguments ).handle end
def cancel_button_label
def cancel_button_label Avo::ExecutionContext.new( target: self.class.cancel_button_label, resource: @resource, record: @record, view: @view, arguments: @arguments, query: @query ).handle end
def close_modal
def close_modal response[:type] = :close_modal self end
def confirm_button_label
def confirm_button_label Avo::ExecutionContext.new( target: self.class.confirm_button_label, resource: @resource, record: @record, view: @view, arguments: @arguments, query: @query ).handle end
def current_user
def current_user Avo::Current.user end
def decode_arguments(arguments)
def decode_arguments(arguments) return if arguments.blank? Avo::Services::EncryptionService.decrypt( message: Base64.decode64(arguments), purpose: :action_arguments ) end
def disabled?
def disabled? !enabled? end
def download(path, filename)
def download(path, filename) response[:type] = :download response[:path] = path response[:filename] = filename self end
def enabled?
def enabled? self.class.standalone || @record&.to_param.present? end
def encode_arguments(arguments)
EncryptionService can generate special characters that can break the URL.
Encrypt the arguments so we can pass sensible data as a query param.
def encode_arguments(arguments) return if arguments.blank? Base64.encode64 Avo::Services::EncryptionService.encrypt( message: arguments, purpose: :action_arguments ) end
def error(text, timeout: nil)
def error(text, timeout: nil) add_message text, :error, timeout: timeout self end
def fields
def fields end
def form_data_attributes
def form_data_attributes { turbo: turbo, turbo_frame: :_top }.compact end
def get_message
def get_message Avo::ExecutionContext.new( target: self.class.message, resource: @resource, record: @record, view: @view, arguments: @arguments, query: @query ).handle end
def handle_action(**args)
def handle_action(**args) processed_fields = if args[:fields].present? # Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method. action_fields = get_field_definitions.map do |field| field.hydrate(resource: @resource) [field.id, field] end.to_h # For some fields, like belongs_to, the id and database_id differ (user vs user_id). # That's why we need to fetch the database_id for when we process the action. action_fields_by_database_id = action_fields.map do |id, value| [value.database_id.to_sym, value] end.to_h args[:fields].to_unsafe_h.map do |name, value| field = action_fields_by_database_id[name.to_sym] next if field.blank? [name, field.resolve_attribute(value)] end.reject(&:blank?).to_h else {} end handle( fields: processed_fields.with_indifferent_access, current_user: args[:current_user], resource: args[:resource], records: args[:query], query: args[:query] ) self end
def inform(text, timeout: nil)
def inform(text, timeout: nil) add_message text, :info, timeout: timeout self end
def initialize(record: nil, resource: nil, user: nil, view: nil, arguments: {}, icon: :play, query: nil, index_query: nil)
def initialize(record: nil, resource: nil, user: nil, view: nil, arguments: {}, icon: :play, query: nil, index_query: nil) @record = record @resource = resource @user = user @view = Avo::ViewInquirer.new(view) @icon = icon @arguments = Avo::ExecutionContext.new( target: arguments, resource: resource, record: record ).handle.with_indifferent_access @query = query @index_query = index_query self.class.message ||= I18n.t("avo.are_you_sure_you_want_to_run_this_option") self.class.confirm_button_label ||= I18n.t("avo.run") self.class.cancel_button_label ||= I18n.t("avo.cancel") self.items_holder = Avo::Resources::Items::Holder.new @response ||= {} @response[:messages] = [] if may_download_file.present? puts "[Avo->] WARNING! Since version 3.2.2 'may_download_file' is unecessary and deprecated on actions. Can be safely removed from #{self.class.name}" end end
def keep_modal_open
def keep_modal_open response[:type] = :keep_modal_open self end
def link_arguments(resource:, arguments: {}, **args)
def link_arguments(resource:, arguments: {}, **args) [path(resource:, arguments:, **args), DATA_ATTRIBUTES] end
def navigate_to_action(action, **kwargs)
def navigate_to_action(action, **kwargs) response[:type] = :navigate_to_action response[:action] = action response[:navigate_to_action_args] = kwargs self end
def no_confirmation?
def no_confirmation? Avo::ExecutionContext.new( target: no_confirmation, action: self, resource: @resource, view: @view, arguments: ).handle end
def path(resource:, arguments: {}, **args)
def path(resource:, arguments: {}, **args) Avo::Services::URIService.parse(resource.record&.to_param.present? ? resource.record_path : resource.records_path) .append_paths("actions") .append_query( **{ action_id: to_param, arguments: encode_arguments(arguments), resource_view: resource.view, view_type: resource.view_type, **args }.compact ) .to_s end
def redirect_to(path = nil, **args, &block)
def redirect_to(path = nil, **args, &block) response[:type] = :redirect response[:redirect_args] = args response[:path] = if block.present? block else path end self end
def reload
def reload response[:type] = :reload self end
def reload_grid_items
def reload_grid_items append_to_response -> { component_to_replace = @resource.resolve_component(Avo::Index::GridItemComponent) @action.records_to_reload.map do |record| turbo_stream.replace( "#{component_to_replace.name.underscore}_#{record.to_param}", component_to_replace.new(resource: @resource.dup.hydrate(record:, view: :index), grid_item_checked: @action.records_to_reload.include?(record)) ) end } end
def reload_record(records)
def reload_record(records) # Force close modal to avoid default redirect to # Redirect is 100% not wanted when using reload_record close_modal @records_to_reload = Array(records) case @resource.view_type.to_sym when :table, :map reload_row_items when :grid reload_grid_items end end
def reload_row_items
def reload_row_items append_to_response -> { table_row_components = [] header_fields = [] component_to_replace = @resource.resolve_component(Avo::Index::TableRowComponent) @action.records_to_reload.each do |record| resource = @resource.dup resource.hydrate(record:, view: :index) resource.detect_fields row_fields = resource.get_fields(only_root: true) header_fields.concat row_fields table_row_components << component_to_replace.new( resource: resource, header_fields: row_fields.map(&:table_header_label), fields: row_fields, row_selector_checked: @action.records_to_reload.include?(record) ) end header_fields.uniq!(&:table_header_label) header_fields_ids = header_fields.map(&:table_header_label) table_row_components.map.with_index do |table_row_component, index| table_row_component.header_fields = header_fields_ids turbo_stream.replace( "#{component_to_replace.name.underscore}_#{@action.records_to_reload[index].to_param}", table_row_component ) end } end
def silent
def silent add_message nil, :silent self end
def succeed(text, timeout: nil)
def succeed(text, timeout: nil) add_message text, :success, timeout: timeout self end
def to_param
def to_param to_s end
def visible_in_view(parent_resource: nil)
def visible_in_view(parent_resource: nil) Avo::ExecutionContext.new( target: visible, params: params, parent_resource: parent_resource, resource: @resource, view: @view, arguments: arguments ).handle && authorized? end
def warn(text, timeout: nil)
def warn(text, timeout: nil) add_message text, :warning, timeout: timeout self end