Skip to content

Commit

Permalink
Merge pull request #796 from martinpovolny/custom_buttons_list
Browse files Browse the repository at this point in the history
Custom buttons for list views
(cherry picked from commit 631a325)
  • Loading branch information
himdel authored and simaishi committed Apr 13, 2017
1 parent 3887048 commit 5765bd9
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 74 deletions.
4 changes: 4 additions & 0 deletions app/assets/javascripts/miq_application.js
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,10 @@ function miqToolbarOnClick(_e) {
} else {
params = miqSerializeForm(button.data('url_parms'));
}
} else if (button.data('url_parms').match("id=LIST")) {
// this is used by custom buttons in lists
params = button.data('url_parms').split("?")[1] +
"&miq_grid_checks=" + ManageIQ.gridChecks.join(',');
} else {
params = button.data('url_parms').split("?")[1];
}
Expand Down
57 changes: 47 additions & 10 deletions app/controllers/application_controller/buttons.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,14 @@ def automate_button_field_changed
@edit[:new][:name] = params[:name] if params[:name]
@edit[:new][:display] = params[:display] == "1" if params[:display]
@edit[:new][:open_url] = params[:open_url] == "1" if params[:open_url]
@edit[:new][:display_for] = params[:display_for] if params[:display_for]
@edit[:new][:submit_how] = params[:submit_how] if params[:submit_how]
@edit[:new][:description] = params[:description] if params[:description]
@edit[:new][:button_image] = params[:button_image].to_i if params[:button_image]
@edit[:new][:dialog_id] = params[:dialog_id] if params[:dialog_id]
visibility_box_edit
end

render :update do |page|
page << javascript_prologue
if params.key?(:instance_name) || params.key?(:other_name) || params.key?(:target_class)
Expand Down Expand Up @@ -237,7 +240,7 @@ def ab_group_delete
end
end

private ###########
private

BASE_MODEL_EXPLORER_CLASSES = [Vm, MiqTemplate, Service].freeze
APPLIES_TO_CLASS_BASE_MODELS = %w(CloudTenant EmsCluster ExtManagementSystem Host MiqTemplate Service ServiceTemplate Storage Vm).freeze
Expand All @@ -260,28 +263,58 @@ def custom_button_done
render_flash(_('No url was returned from automate.'), :error)
end
end
private :custom_button_done

def custom_buttons_invoke(button, objs)
if objs.length > 1 &&
(button.options && button.options.key?(:submit_how) && button.options[:submit_how].to_s == 'all')
# FIXME: wee need something like this from the core/automate:
# button.invoke(:object_id => objs.map(&:id), :object_type => objs[0].class.base_class.name)
raise "Not implemented."
else
objs.each { |obj| button.invoke(obj) }
end
end

def custom_buttons
button = CustomButton.find_by_id(params[:button_id])
cls = applies_to_class_model(button.applies_to_class)

@explorer = true if BASE_MODEL_EXPLORER_CLASSES.include?(cls)

obj = cls.find_by_id(params[:id].to_i)
if params[:id].to_s == 'LIST'
objs = Rbac.filtered(cls.where(:id => find_checked_items))
obj = objs.first
else
obj = Rbac.filtered_object(cls.find(params[:id].to_i))
objs = [obj]
end

if objs.empty?
render_flash(_("Error executing custom button: No item was selected."), :error)
return
end

@right_cell_text = _("%{record} - \"%{button_text}\"") % {:record => obj.name, :button_text => button.name}

if button.resource_action.dialog_id
options = {}
options[:header] = @right_cell_text
options[:target_id] = obj.id
options[:target_kls] = obj.class.name
options = {
:header => @right_cell_text,
:target_id => obj.id,
:target_ids => objs.collect(&:id),
:target_kls => obj.class.name,
}

dialog_initialize(button.resource_action, options)

elsif button.options && button.options.key?(:open_url) && button.options[:open_url]
# not supported for objs: cannot do wait for task for multiple tasks
task_id = button.invoke_async(obj)
initiate_wait_for_task(:task_id => task_id, :action => :custom_button_done)

else
begin
button.invoke(obj)
custom_buttons_invoke(button, objs)

rescue StandardError => bang
add_flash(_("Error executing: \"%{task_description}\" %{error_message}") %
{:task_description => params[:desc], :error_message => bang.message}, :error)
Expand Down Expand Up @@ -757,8 +790,10 @@ def button_set_record_vars(button)
button[:options][:button_image] ||= {}
button[:options][:button_image] = @edit[:new][:button_image]
end
button[:options][:display] = @edit[:new][:display]
button[:options][:open_url] = @edit[:new][:open_url]

%i(display open_url display_for submit_how).each do |key|
button[:options][key] = @edit[:new][key]
end
button.visibility ||= {}
if @edit[:new][:visibility_typ] == "role"
roles = []
Expand Down Expand Up @@ -846,6 +881,8 @@ def button_set_form_vars
:button_image => @custom_button.options.try(:[], :button_image).to_s,
:display => @custom_button.options.try(:[], :display).nil? ? true : @custom_button.options[:display],
:open_url => @custom_button.options.try(:[], :open_url) ? @custom_button.options[:open_url] : false,
:display_for => @custom_button.options.try(:[], :display_for) ? @custom_button.options[:display_for] : 'single',
:submit_how => @custom_button.options.try(:[], :submit_how) ? @custom_button.options[:submit_how] : 'one',
:object_message => @custom_button.uri_message || "create",
)
@edit[:current] = copy_hash(@edit[:new])
Expand Down
27 changes: 15 additions & 12 deletions app/controllers/mixins/custom_buttons.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
module Mixins::CustomButtons
extend ActiveSupport::Concern

def custom_toolbar?
def custom_toolbar
return nil unless self.class.instance_eval { @custom_buttons }

@explorer ? custom_toolbar_explorer? : custom_toolbar_simple?
@explorer ? custom_toolbar_explorer : custom_toolbar_simple
end

def custom_toolbar_explorer?
if x_tree # Make sure we have the trees defined
if x_node == "root" || # If on a root, create placeholder toolbar
!@record # or no record showing
:blank
elsif @display == "main"
true
def custom_toolbar_explorer
if x_tree
if @display == "main" && @record
Mixins::CustomButtons::Result.new(:single)
elsif @lastaction == "show_list"
Mixins::CustomButtons::Result.new(:list)
else
:blank
'blank_view_tb'
end
end
end

def custom_toolbar_simple?
@record && @lastaction == "show" && @display == "main"
def custom_toolbar_simple
if @record && @lastaction == "show" && @display == "main"
Mixins::CustomButtons::Result.new(:single)
elsif @lastaction == "show_list"
Mixins::CustomButtons::Result.new(:list)
end
end

class_methods do
Expand Down
13 changes: 13 additions & 0 deletions app/controllers/mixins/custom_buttons/result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Mixins::CustomButtons
Result = Struct.new(:plural_form) do
def plural_form_matches(button)
if plural_form == :list
%w(list both).include?(button.options.try(:[], :display_for))

else # :single
[nil, 'single', 'both'].include?(button.options.try(:[], :display_for))

end
end
end
end
3 changes: 2 additions & 1 deletion app/controllers/service_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,9 @@ def replace_right_cell(options = {})
record_showing = type && ["Service"].include?(TreeBuilder.get_model_for_prefix(type))
if x_active_tree == :svcs_tree && !@in_a_form && !@sb[:action]
if record_showing && @sb[:action].nil?
cb_tb = build_toolbar("custom_buttons_tb")
cb_tb = build_toolbar(Mixins::CustomButtons::Result.new(:single))
else
cb_tb = build_toolbar(Mixins::CustomButtons::Result.new(:list))
v_tb = build_toolbar("x_gtl_view_tb")
end
c_tb = build_toolbar(center_toolbar_filename)
Expand Down
8 changes: 7 additions & 1 deletion app/controllers/vm_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,11 @@ def get_node_info(treenodeid)
end
# Add adv search filter to header
@right_cell_text += @edit[:adv_search_applied][:text] if @edit && @edit[:adv_search_applied]

# save model being displayed for custom buttons
@tree_selected_model = if model.present?
model.constantize
end
end

if @edit && @edit.fetch_path(:adv_search_applied, :qs_exp) # If qs is active, save it in history
Expand Down Expand Up @@ -1198,9 +1203,10 @@ def replace_right_cell(options = {})
record_showing = type && ["Vm", "MiqTemplate"].include?(TreeBuilder.get_model_for_prefix(type))
c_tb = build_toolbar(center_toolbar_filename) # Use vm or template tb
if record_showing
cb_tb = build_toolbar("custom_buttons_tb")
cb_tb = build_toolbar(Mixins::CustomButtons::Result.new(:single))
v_tb = build_toolbar("x_summary_view_tb")
else
cb_tb = build_toolbar(Mixins::CustomButtons::Result.new(:list))
v_tb = build_toolbar("x_gtl_view_tb")
end
elsif ["compare", "drift"].include?(@sb[:action])
Expand Down
6 changes: 1 addition & 5 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -715,11 +715,7 @@ def calculate_toolbars
end

toolbars['center_tb'] = center_toolbar_filename

custom_toolbar = controller.custom_toolbar?
if custom_toolbar
toolbars['custom_tb'] = custom_toolbar == :blank ? 'blank_view_tb' : 'custom_buttons_tb'
end
toolbars['custom_tb'] = controller.custom_toolbar

toolbars['view_tb'] = inner_layout_present? ? x_view_toolbar_filename : view_toolbar_filename
toolbars
Expand Down
75 changes: 49 additions & 26 deletions app/helpers/application_helper/toolbar_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,19 @@ def predefined_toolbar_class(tb_name)
class_name.constantize
end

def controller
@view_context.respond_to?(:controller) ? @view_context.controller : @view_context
end

def model_for_custom_toolbar
controller.instance_eval { @tree_selected_model } || controller.class.model
end

# According to toolbar name in parameter `toolbar_name` either returns class
# for generic toolbar, or starts building custom toolbar
def toolbar_class(toolbar_name)
if toolbar_name == "custom_buttons_tb"
custom_toolbar_class(@record)
if Mixins::CustomButtons::Result === toolbar_name
custom_toolbar_class(toolbar_name)
else
predefined_toolbar_class(toolbar_name)
end
Expand Down Expand Up @@ -227,10 +235,11 @@ def group_skipped?(name)
x_edit_view_tb history_main ems_container_dashboard ems_infra_dashboard).include?(name)
end

def create_custom_button_hash(input, record, options = {})
def create_custom_button(input, model, record, options = {})
options[:enabled] = true unless options.key?(:enabled)
button_id = input[:id]
button_name = input[:name].to_s
record_id = record.present? ? record.id : 'LIST'
button = {
:id => "custom__custom_#{button_id}",
:type => :button,
Expand All @@ -239,62 +248,73 @@ def create_custom_button_hash(input, record, options = {})
:enabled => options[:enabled],
:klass => ApplicationHelper::Button::ButtonWithoutRbacCheck,
:url => "button",
:url_parms => "?id=#{record.id}&button_id=#{button_id}&cls=#{record.class}&pressed=custom_button&desc=#{button_name}"
:url_parms => "?id=#{record_id}&button_id=#{button_id}&cls=#{model}&pressed=custom_button&desc=#{button_name}"
}
button[:text] = button_name if input[:text_display]
button
end

def create_raw_custom_button_hash(cb, record)
record_id = record.present? ? record.id : 'LIST'
{
:id => cb.id,
:class => cb.applies_to_class,
:description => cb.description,
:name => cb.name,
:image => cb.options[:button_image],
:text_display => cb.options.key?(:display) ? cb.options[:display] : true,
:target_object => record.id.to_i
:target_object => record_id
}
end

def custom_buttons_hash(record)
get_custom_buttons(record).collect do |group|
def custom_button_selects(model, record, toolbar_result)
get_custom_buttons(model, record, toolbar_result).collect do |group|
props = {
:id => "custom_#{group[:id]}",
:type => :buttonSelect,
:icon => "product product-custom-#{group[:image]} fa-lg",
:title => group[:description],
:enabled => true,
:items => group[:buttons].collect { |b| create_custom_button_hash(b, record) }
:items => group[:buttons].collect { |b| create_custom_button(b, model, record) }
}
props[:text] = group[:text] if group[:text_display]

{:name => "custom_buttons_#{group[:text]}", :items => [props]}
end
end

def custom_toolbar_class(record)
def custom_toolbar_class(toolbar_result)
model = @record ? @record.class : model_for_custom_toolbar
build_custom_toolbar_class(model, @record, toolbar_result)
end

def build_custom_toolbar_class(model, record, toolbar_result)
# each custom toolbar is an anonymous subclass of this class

toolbar = Class.new(ApplicationHelper::Toolbar::Basic)
custom_buttons_hash(record).each do |button_group|

# This creates several drop-down (select) with custom buttons.
# Each select is placed into a separate group.
custom_button_selects(model, record, toolbar_result).each do |button_group|
toolbar.button_group(button_group[:name], button_group[:items])
end

service_buttons = record_to_service_buttons(record)
unless service_buttons.empty?
buttons = service_buttons.collect { |b| create_custom_button_hash(b, record, :enabled => nil) }
toolbar.button_group("custom_buttons_", buttons)
# For Service, we include buttons for ServiceTemplate.
# These buttons are added as a single group with multiple buttons
if record.present?
service_buttons = record_to_service_buttons(record)
unless service_buttons.empty?
buttons = service_buttons.collect { |b| create_custom_button(b, model, record, :enabled => nil) }
toolbar.button_group("custom_buttons_", buttons)
end
end

toolbar
end

def button_class_name(record)
case record
when Service then "ServiceTemplate" # Service Buttons are defined in the ServiceTemplate class
when VmOrTemplate then record.class.base_model.name
else record.class.base_class.name
end
def button_class_name(model)
# Service Buttons are defined in the ServiceTemplate class
model >= Service ? "ServiceTemplate" : model.base_model.name
end

def service_template_id(record)
Expand All @@ -310,9 +330,9 @@ def record_to_service_buttons(record)
record.service_template.custom_buttons.collect { |cb| create_raw_custom_button_hash(cb, record) }
end

def get_custom_buttons(record)
cbses = CustomButtonSet.find_all_by_class_name(button_class_name(record), service_template_id(record))
cbses.sort_by { |cbs| cbs[:set_data][:group_index] }.collect do |cbs|
def get_custom_buttons(model, record, toolbar_result)
cbses = CustomButtonSet.find_all_by_class_name(button_class_name(model), service_template_id(record))
cbses.sort_by { |cbs| cbs.set_data[:group_index] }.collect do |cbs|
group = {
:id => cbs.id,
:text => cbs.name.split("|").first,
Expand All @@ -322,11 +342,14 @@ def get_custom_buttons(record)
}

available = CustomButton.available_for_user(current_user, cbs.name) # get all uri records for this user for specified uri set
available = available.select { |b| cbs.members.include?(b) } # making sure available_for_user uri is one of the members
available = available.select do |b|
cbs.members.include?(b) && toolbar_result.plural_form_matches(b)
end

group[:buttons] = available.collect { |cb| create_raw_custom_button_hash(cb, record) }.uniq
if cbs[:set_data][:button_order] # Show custom buttons in the order they were saved
if cbs.set_data[:button_order] # Show custom buttons in the order they were saved
ordered_buttons = []
cbs[:set_data][:button_order].each do |bidx|
cbs.set_data[:button_order].each do |bidx|
group[:buttons].each do |b|
if bidx == b[:id] && !ordered_buttons.include?(b)
ordered_buttons.push(b)
Expand Down
Loading

0 comments on commit 5765bd9

Please sign in to comment.