Skip to content

Commit

Permalink
Merge pull request #4775 from romanblanco/bz1635738-master
Browse files Browse the repository at this point in the history
Display custom buttons after comming from relationship table
  • Loading branch information
himdel authored Oct 30, 2018
2 parents f18f976 + 1623721 commit 98b7dd0
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 31 deletions.
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class ApplicationController < ActionController::Base
include Mixins::CustomButtons
include Mixins::CheckedIdMixin
include ParamsHelper
include ApplicationHelper::Toolbar::Mixins::CustomButtonToolbarMixin

helper ToolbarHelper
helper JsHelper
Expand Down
40 changes: 10 additions & 30 deletions app/controllers/application_controller/buttons.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,28 +262,6 @@ def open_url_after_dialog
private

BASE_MODEL_EXPLORER_CLASSES = [MiqGroup, MiqTemplate, Service, Switch, Tenant, User, Vm].freeze
APPLIES_TO_CLASS_BASE_MODELS = %w(AvailabilityZone CloudNetwork CloudObjectStoreContainer CloudSubnet CloudTenant
CloudVolume ContainerGroup ContainerImage ContainerNode ContainerProject
ContainerTemplate ContainerVolume EmsCluster ExtManagementSystem
GenericObject GenericObjectDefinition Host LoadBalancer
MiqGroup MiqTemp MiqTemplate NetworkRouter OrchestrationStack SecurityGroup Service
ServiceTemplate Storage Switch Tenant User Vm VmOrTemplate).freeze
def applies_to_class_model(applies_to_class)
# TODO: Give a better name for this concept, including ServiceTemplate using Service
# This should probably live in the model once this concept is defined.
unless APPLIES_TO_CLASS_BASE_MODELS.include?(applies_to_class)
raise ArgumentError, "Received: #{applies_to_class}, expected one of #{APPLIES_TO_CLASS_BASE_MODELS}"
end

case applies_to_class
when "ServiceTemplate"
Service
when "GenericObjectDefinition"
GenericObject
else
applies_to_class.constantize
end
end

def custom_button_done
url = SystemConsole.find_by(:vm => params[:id]).try(:url)
Expand Down Expand Up @@ -321,17 +299,19 @@ def sync_playbook_dialog(button)

def custom_buttons(ids = nil, display_options = {})
button = CustomButton.find(params[:button_id])
cls = applies_to_class_model(button.applies_to_class)
cls = custom_button_class_model(button.applies_to_class)
@explorer = true if BASE_MODEL_EXPLORER_CLASSES.include?(cls)
ids ||= params[:id]
if ids.to_s == 'LIST'
objs = Rbac.filtered(cls.where(:id => find_checked_items))
obj = objs.first
else
obj = Rbac.filtered_object(cls.find(ids.to_i))
objs = [obj]
ids ||= params[:id] unless relationship_table_screen? && @record.nil?
ids = find_checked_items if ids == 'LIST' || ids.nil?

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

objs = Rbac.filtered(cls.where(:id => ids))
obj = objs.first

if objs.empty?
render_flash(_("Error executing custom button: No item was selected."), :error)
return
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/mixins/custom_buttons.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def custom_toolbar_simple
Mixins::CustomButtons::Result.new(:single)
elsif @lastaction == "show_list"
Mixins::CustomButtons::Result.new(:list)
elsif relationship_table_screen?
Mixins::CustomButtons::Result.new(:list)
elsif @display == 'generic_objects'
@lastaction == 'generic_object' ? Mixins::CustomButtons::Result.new(:single) : Mixins::CustomButtons::Result.new(:list)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module ApplicationHelper::Toolbar::Mixins::CustomButtonToolbarMixin
# FIXME: replace with CustomButton.button_classes
# (ServiceTemplate, VmOrTemplate, GenericObjectDefinition are exceptions)
APPLIES_TO_CLASS_BASE_MODELS = %w(AvailabilityZone CloudNetwork CloudObjectStoreContainer CloudSubnet CloudTenant
CloudVolume ContainerGroup ContainerImage ContainerNode ContainerProject
ContainerTemplate ContainerVolume EmsCluster ExtManagementSystem
GenericObject GenericObjectDefinition Host LoadBalancer
MiqGroup MiqTemplate NetworkRouter OrchestrationStack
SecurityGroup Service ServiceTemplate Storage Switch Tenant
User Vm VmOrTemplate).freeze

def custom_button_appliable_class?(model)
# FIXME: merge with model replacement in 'custom_button_class_model'
model = "MiqTemplate" if model == "Image"
model = "Vm" if model == "Instance"
model = "ContainerVolume" if model == "PersistentVolume"
model = "ExtManagementSystem" if model == "StorageManager"
APPLIES_TO_CLASS_BASE_MODELS.include?(model)
end

def custom_button_class_model(applies_to_class)
# TODO: Give a better name for this concept, including ServiceTemplate using Service
# 'custom_button_appliable_class' -> 'display_to_model'
# This should probably live in the model once this concept is defined.
unless custom_button_appliable_class?(applies_to_class)
raise ArgumentError, "Received: #{applies_to_class}, expected one of #{APPLIES_TO_CLASS_BASE_MODELS}"
end

case applies_to_class
when "ServiceTemplate"
Service
when "PersistentVolume"
ContainerVolume
when "GenericObjectDefinition"
GenericObject
when "Instance"
Vm
when "Image"
MiqTemplate
when "StorageManager"
ExtManagementSystem
else
applies_to_class.constantize
end
end

# Indicates, whether the user has came from providers relationship screen
# or not
#
# Used to
# - indicate if the custom buttons should be rendered
# - decide where to look for id of checked records
def relationship_table_screen?
return false if @display.nil?
display_class = @display.camelize.singularize
return false unless custom_button_appliable_class?(display_class)

show_action = @lastaction == "show"
display_model = custom_button_class_model(display_class)
# method is accessed twice from a different location - from toolbar builder
# and custom button mixin - and so controller class changes
ctrl = self.class == ApplicationHelper::ToolbarBuilder ? controller : self
controller_model = ctrl.class.model

display_model != controller_model && show_action
end
end
9 changes: 8 additions & 1 deletion app/helpers/application_helper/toolbar_builder.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class ApplicationHelper::ToolbarBuilder
include MiqAeClassHelper
include RestfulControllerMixin
include ApplicationHelper::Toolbar::Mixins::CustomButtonToolbarMixin

def call(toolbar_name)
build_toolbar(toolbar_name)
Expand Down Expand Up @@ -233,7 +234,11 @@ def create_custom_button(input, model, record)
}
button[:text] = button_name if input[:text_display]
button[:onwhen] = '1+' if cb_enabled_for_nested
button[:send_checked] = true if record_id == 'LIST'
if record_id == 'LIST' ||
(@display.present? &&
custom_button_appliable_class?(@display.camelize.singularize))
button[:send_checked] = true
end
button
end

Expand Down Expand Up @@ -283,6 +288,8 @@ def custom_toolbar_class(toolbar_result)
if @display == 'generic_objects'
model = GenericObjectDefinition
record = GenericObject.find_by(:id => @sb[:rec_id])
elsif relationship_table_screen?
model = custom_button_class_model(@display.camelize.singularize)
else
model = @record ? @record.class : model_for_custom_toolbar
end
Expand Down
5 changes: 5 additions & 0 deletions spec/controllers/ems_cloud_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -726,4 +726,9 @@ def verify_password_and_confirm(password, verify)
expect(response.body).not_to have_selector("button[title*='Select a filter to set it as my default']", :text => "Set Default")
end
end

%w(availability_zones cloud_tenants security_groups instances images
orchestration_stacks storage_managers).each do |custom_button_class|
include_examples "relationship table screen with custom buttons", custom_button_class
end
end
4 changes: 4 additions & 0 deletions spec/controllers/ems_container_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,8 @@ def test_setting_few_fields
end

include_examples '#download_summary_pdf', :ems_kubernetes
%w(container_projects container_nodes container_images container_volumes
container_templates).each do |custom_button_class|
include_examples "relationship table screen with custom buttons", custom_button_class
end
end
32 changes: 32 additions & 0 deletions spec/controllers/ems_infra_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,38 @@

subject { get :show, :params => {:id => @ems.id}.merge(url_params) }

context "display=hosts" do
it 'renders custom buttons for hosts accessed from relationship screen' do
custom_button = FactoryGirl.create(
:custom_button, :name => "My Button", :applies_to_class => "Host",
:visibility => {:roles => ["_ALL_"]},
:options => {:display => true, :open_url => false, :display_for => "both"}
)
custom_button_set = FactoryGirl.create(
:custom_button_set, :set_data => {
:applies_to_class => "Host", :button_order => [custom_button.id]
}
)
custom_button_set.add_member(custom_button)
controller.instance_variable_set(:@record, @ems)
allow(controller).to receive(:controller).and_return(controller)

# format js to avoid redirection (application_controller/explorer.rb#generic_x_show)
get :show, :params => {:id => @ems.id, :display => 'hosts', :format => :js}

toolbar = controller.send(:toolbar_from_hash)
toolbar_custom_button = toolbar.select do |button_group|
button_group&.map do |button|
button if button[:id].starts_with?("custom_")
end&.compact.present?
end.flatten.first

expect(toolbar_custom_button).not_to be_nil
expect(toolbar_custom_button[:items].count).to eql(1)
expect(response.status).to eq(200)
end
end

context "display=timeline" do
let(:url_params) { {:display => 'timeline'} }
it { is_expected.to have_http_status 200 }
Expand Down
4 changes: 4 additions & 0 deletions spec/controllers/ems_network_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
include_examples :shared_examples_for_ems_network_controller, %w(openstack azure google amazon)

it_behaves_like "controller with custom buttons"
%w(cloud_tenants cloud_networks cloud_subnets network_routers security_groups
load_balancers).each do |custom_button_class|
include_examples "relationship table screen with custom buttons", custom_button_class
end
end
4 changes: 4 additions & 0 deletions spec/controllers/vm_infra_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -667,4 +667,8 @@
controller.report_data
expect(assigns(:edit)[:new]).to_not include(:expression)
end

%w(ems_clusters hosts storages vms miq_templates).each do |custom_button_class|
include_examples "relationship table screen with custom buttons", custom_button_class
end
end
11 changes: 11 additions & 0 deletions spec/shared/controllers/shared_example_for_custom_buttons.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,14 @@
expect(controller.custom_toolbar).to be_a_kind_of Mixins::CustomButtons::Result
end
end

shared_examples 'relationship table screen with custom buttons' do |display|
context "displayed entity is #{display}" do
it "has custom toolbar when navigating through relationship table" do
controller.instance_variable_set(:@display, display)
controller.instance_variable_set(:@lastaction, 'show')
controller.instance_variable_set(:@record, true) # @record is the provider
expect(controller.custom_toolbar).to be_a_kind_of Mixins::CustomButtons::Result
end
end
end

0 comments on commit 98b7dd0

Please sign in to comment.