app/controllers/miq_request_controller.rb
class MiqRequestController < ApplicationController
include Mixins::GenericSessionMixin
include Mixins::BreadcrumbsMixin
before_action :check_privileges, :except => :post_install_callback
before_action :get_session_data
after_action :cleanup_action
after_action :set_session_data
helper ProvisionCustomizeHelper
PROV_STATES = {
"pending_approval" => N_("Pending Approval"),
"approved" => N_("Approved"),
"denied" => N_("Denied")
}.freeze
def index
@request_tab = params[:typ] if params[:typ] # set this to be used to identify which Requests subtab was clicked
redirect_to(:action => 'show_list')
end
# handle buttons pressed on the button bar
def button
params[:page] = @current_page unless @current_page.nil? # Save current page for list refresh
@refresh_div = "main_div" # Default div for button.rjs to refresh
case params[:pressed]
when 'miq_request_delete' then deleterequests
when 'miq_request_edit' then request_edit
when 'miq_request_copy' then request_copy
when 'miq_request_reload' then handle_request_reload
else javascript_flash(:text => _('Button not yet implemented'), :severity => :error)
end
end
def report_data
super
if params[:model_name] == "MiqRequest"
@request_filters = {}
params[:additional_options][:named_scope].each do |option|
case option[0]
when "created_recently"
@request_filters[:selectedPeriod] = option[1]
when "with_approval_state"
@request_filters[:approvalStates] = option[1]
when "with_request_type"
@request_filters[:types] =
if option[1][0].kind_of?(String)
option[1]
else
option[1][0]
end
when "with_reason_like"
@request_filters[:reasonText] = option[1]
when "with_requester"
@request_filters[:selectedUser] = option[1]
end
end
session[:request_filters] = @request_filters
end
end
def request_edit
assert_privileges(rbac_feature_id("miq_request_edit"))
provision_request = MiqRequest.find(params[:id])
if provision_request.workflow_class || provision_request.kind_of?(MiqProvisionConfiguredSystemRequest)
request_edit_settings(provision_request)
handle_request_edit_copy_redirect
else
session[:checked_items] = provision_request.options[:src_ids]
@refresh_partial = "reconfigure"
@_params[:controller] = "vm"
if provision_request.kind_of?(VmCloudReconfigureRequest)
resizevms
else
vm_reconfigure
end
end
end
def request_copy
assert_privileges(rbac_feature_id("miq_request_copy"))
provision_request = MiqRequest.find(params[:id])
@refresh_partial = "prov_copy"
request_settings_for_edit_or_copy(provision_request)
handle_request_edit_copy_redirect
end
# Show the main Requests list view
def show_list
@breadcrumbs = []
bc_name = _("Requests")
@request_tab = params[:typ] if params[:typ] # set this to be used to identify which Requests subtab was clicked
@layout = layout_from_tab_name(@request_tab)
drop_breadcrumb(:name => bc_name, :url => "/miq_request/show_list?typ=#{@request_tab}")
@lastaction = "show_list"
@gtl_url = "/show_list"
if params[:ppsetting] # User selected new per page value
@items_per_page = params[:ppsetting].to_i # Set the new per page value
@settings.store_path(:perpage, PERPAGE_TYPES['list'], @items_per_page) # Set the per page setting for this gtl type
end
@sortcol = session[:request_sortcol].nil? ? 0 : session[:request_sortcol].to_i
@sortdir = session[:request_sortdir].nil? ? "ASC" : session[:request_sortdir]
@no_checkboxes = true # Don't show checkboxes, read_only
kls = @layout == "miq_request_ae" ? AutomationRequest : MiqRequest
scope =
if session[:request_filters].nil?
user_options(params)
else
{
:reason_text => session[:request_filters][:reasonText],
:applied_states => session[:request_filters][:approvalStates],
:type_choice => session[:request_filters][:types],
:user_choice => session[:request_filters][:selectedUser],
:time_period => session[:request_filters][:selectedPeriod],
}
end
@view, @pages = get_view(kls, :named_scope => prov_scope(scope))
@current_page = @pages[:current] unless @pages.nil? # save the current page number
session[:request_sortcol] = @sortcol
session[:request_sortdir] = @sortdir
{:view => @view, :pages => @pages}
end
def show
identify_request
return if record_no_longer_exists?(@miq_request)
@display = params[:display] || "main" unless pagination_or_gtl_request?
@gtl_url = "/show"
@request_tab = params[:typ] || 'service'
if @display == "main"
prov_set_show_vars
elsif @display == "miq_provisions"
@showtype = "miq_provisions"
@no_checkboxes = true
@showlinks = true
@view, @pages = get_view(MiqProvision, :named_scope => [[:with_miq_request_id, @miq_request.id]]) # Get all requests
drop_breadcrumb(:name => _("Provisioned VMs [%{description}]") % {:description => @miq_request.description},
:url => "/miq_request/show/#{@miq_request.id}?display=#{@display}")
end
@lastaction = "show"
end
# Stamp a request with approval or denial
def stamp
assert_privileges(rbac_feature_id("miq_request_approval"))
if params[:button] == "cancel"
if (session[:edit] && session[:edit][:stamp_typ]) == "a"
flash_to_session(_("Request approval was cancelled by the user"))
else
flash_to_session(_("Request denial was cancelled by the user"))
end
@edit = nil
javascript_redirect(:action => @lastaction, :id => session[:edit][:request].id)
elsif params[:button] == "submit"
return unless load_edit("stamp_edit__#{params[:id]}", "show")
stamp_request = MiqRequest.find(@edit[:request].id) # Get the current request record
if @edit[:stamp_typ] == "approve"
stamp_request.approve(current_user, @edit[:reason])
else
stamp_request.deny(current_user, @edit[:reason])
end
flash_to_session(_("Request \"%{name}\" was %{task}") % {:name => stamp_request.description, :task => (session[:edit] && session[:edit][:stamp_typ]) == "approve" ? "approved" : "denied"})
@edit = nil
javascript_redirect(:action => "show_list")
else # First time in, set up @edit hash
identify_request
@edit = {}
@edit[:dialog_mode] = :review
@edit[:request] = @miq_request
@edit[:key] = "stamp_edit__#{@miq_request.id}"
# set approve/deny based upon params[:typ] value
@edit[:stamp_typ] = params[:typ] == 'a' ? 'approve' : 'deny'
show
if @edit[:stamp_typ] == "approve"
drop_breadcrumb(:name => _("Request Approval"), :url => "/miq_request/stamp")
else
drop_breadcrumb(:name => _("Request Denial"), :url => "/miq_request/stamp")
end
render :action => "show"
end
end
# AJAX driven routine to check for changes in ANY field on the form
def stamp_field_changed
return unless load_edit("stamp_edit__#{params[:id]}", "show")
assert_privileges("miq_request_approval")
@edit[:reason] = params[:reason] if params[:reason]
render :update do |page|
page << javascript_prologue
page << javascript_for_miq_button_visibility(@edit[:reason].present?)
end
end
def prov_copy
assert_privileges("miq_request_copy")
org_req = MiqRequest.where(:id => params[:req_id].to_i).first
req = MiqRequest.new(
:approval_state => 'pending_approval',
:description => org_req.description,
:requester => current_user,
:type => org_req.type,
:created_on => org_req.created_on,
:updated_on => org_req.updated_on,
:options => org_req.options.except(:requester_group)
)
if req.kind_of?(ServiceTemplateProvisionRequest)
@dialog_replace_data = req.options[:dialog].map { |key, val| {:name => key.split('dialog_').last, :value => val } }.to_json
@new_dialog = true
template = find_record_with_rbac(ServiceTemplate, req.source_id)
resource_action = template.resource_actions.find { |r| r.action.downcase == 'provision' && r.dialog_id }
@opts = DialogLocalService.new.determine_dialog_locals_for_svc_catalog_provision(resource_action, template, "/miq_request/show_list")
@opts[:cancel_endpoint] = "/miq_request/show_list"
else
prov_set_form_vars(req) # Set vars from existing request
# forcing submit button to stay on for copy request, setting a key in current hash so new and current are different,
# couldn't set this in new hash becasue that's being set by model
@edit[:current][:description] = _("Copy of %{description}") % {:description => org_req.description}
session[:changed] = true # Turn on the submit button
drop_breadcrumb(:name => _("Copy of %{typ} Request") % {:typ => org_req.request_type_display})
@in_a_form = true
end
render :action => "prov_edit"
end
# To handle Continue button
def prov_continue
assert_privileges("miq_request_edit")
if params[:button] == "continue" # Continue the request from the workflow with the new options
id = params[:id] || "new"
return unless load_edit("prov_edit__#{id}", "show_list")
@edit[:wf].continue_request(@edit[:new]) # Continue the workflow with new field values based on options
@edit[:wf].init_from_dialog(@edit[:new]) # Create a new provision workflow for this edit session
@edit[:buttons] = @edit[:wf].get_buttons
@edit[:wf].get_dialog_order.each do |d| # Go thru all dialogs, in order that they are displayed
@edit[:wf].get_all_fields(d).each_key do |field| # Go thru all field
if field[:error].present?
@error_div ||= "#{d}_div"
add_flash(field[:error], :error)
end
end
end
# setting active tab to first visible tab
@edit[:wf].get_dialog_order.each do |d|
next unless @edit[:wf].get_dialog(d)[:display] == :show
@edit[:new][:current_tab_key] = d
@tabactive = d # Use JS to update the display
break
end
render :update do |page|
page << javascript_prologue
if @error_div
page.replace("flash_msg_div", :partial => "layouts/flash_msg")
page << "miqScrollTop();" if @flash_array.present?
else
page.replace("prov_wf_div", :partial => "prov_wf")
end
page.replace("buttons_div", :partial => "miq_request/prov_form_buttons")
end
end
end
def prov_load_tab
assert_privileges("miq_request_edit")
if @options && @options[:current_tab_key] == :purpose # Need to build again for purpose tab
build_tags_for_provisioning(@options[:wf], @options[:vm_tags], false)
end
# need to build host list view, to display on show screen
@options[:host_sortdir] = "ASC"
@options[:host_sortcol] = "name"
# only build host grid if that field is visible/exists in dialog
if @options[:wf].get_field(:src_host_ids, :service).present? ||
@options[:wf].get_field(:placement_host_name, :environment).present?
build_host_grid(@options[:wf].allowed_hosts, @options[:host_sortdir], @options[:host_sortcol])
end
render :update do |page|
page << javascript_prologue
page.replace_html(
@options[:current_tab_key],
:partial => dialog_partial_for_workflow,
:locals => {:wf => @options[:wf], :dialog => @options[:current_tab_key], :isDisabled => true}
)
# page << javascript_show("hider_#{@options[:current_tab_key].to_s}_div")
page << "miqSparkle(false);"
end
end
WORKFLOW_METHOD_WHITELIST = {'retrieve_ldap' => :retrieve_ldap}.freeze
def retrieve_email
assert_privileges("miq_request_edit")
@edit = session[:edit]
begin
method = WORKFLOW_METHOD_WHITELIST[params[:field]]
@edit[:wf].send(method, @edit[:new]) unless method.nil?
rescue StandardError => bang
add_flash(_("Error retrieving LDAP info: %{error_message}") % {:error_message => bang.message}, :error)
javascript_flash
else
render :update do |page|
page << javascript_prologue
page.replace_html(:requester, :partial => "shared/views/prov_dialog",
:locals => {:wf => @edit[:wf], :dialog => :requester})
page.replace("flash_msg_div", :partial => "layouts/flash_msg")
page << "miqScrollTop();" if @flash_array.present?
end
end
end
def post_install_callback
MiqRequestTask.post_install_callback(params["task_id"]) if params["task_id"]
head :ok
end
# Caution: The params[:typ] argument needs to match value from ?typ=VALUE
# from app/presenters/menu/default_menu.rb
# Example: '/miq_request?typ=service' --> "'service'".
# The returned value needs to be equal to the first argument to Menu::Section.new(...)
# Example: Menu::Section.new(:clo, N_("Clouds"), 'fa fa-plus', [ ... --> ":clo"
def menu_section_id(parms = {})
parms[:typ] == 'ae' ? :automate : :svc
end
private
def replace_gtl
render :update do |page|
page << javascript_prologue
page.replace('gtl_div', :partial => 'layouts/gtl', :locals => {:no_flash_div => true})
end
end
def handle_request_edit_copy_redirect
javascript_redirect(:controller => @redirect_controller,
:action => @refresh_partial,
:id => @redirect_id,
:prov_type => @prov_type,
:req_id => @req_id,
:org_controller => @org_controller,
:prov_id => @prov_id)
end
def handle_request_reload
if @display == "main" && params[:id].present?
show
render :update do |page|
page << javascript_prologue
page.replace("main_div", :template => "miq_request/show")
page << javascript_reload_toolbars
end
elsif @display == "miq_provisions"
show
replace_gtl
else
show_list
replace_gtl
end
end
# Create a condition from the passed in options
def prov_scope(opts)
scope = []
# Request date (created since X days ago)
scope << [:created_recently, opts[:time_period].to_i] if opts[:time_period].present?
# Select requester user across regions
scope << [:with_requester, current_user.id] unless approver?
scope << [:with_requester, opts[:user_choice]] if opts[:user_choice] && opts[:user_choice] != "all"
scope << [:with_approval_state, opts[:applied_states]] if opts[:applied_states].present?
scope << [:with_type, MiqRequest::MODEL_REQUEST_TYPES[model_request_type_from_layout].keys]
scope << [:with_request_type, opts[:type_choice]] if opts[:type_choice] && opts[:type_choice] != "all"
scope << [:with_reason_like, opts[:reason_text]] if opts[:reason_text].present?
scope
end
def model_request_type_from_layout
@layout == "miq_request_ae" ? :Automate : :Service
end
def request_types_hash
MiqRequest::MODEL_REQUEST_TYPES[model_request_type_from_layout].values.reduce({}) do |hash, item|
hash.merge(item) { |_key, val| _(val) }
end
end
def requester_label(request)
if request.requester.nil?
(_("%{name} (no longer exists)") % {:name => request.requester_name})
else
request.requester_name
end
end
def requesters_in_30_days
Rbac::Filterer.filtered(MiqRequest.where(:type => MiqRequest::MODEL_REQUEST_TYPES[model_request_type_from_layout].keys,
:created_on => (30.days.ago.utc)..(Time.now.utc))).each_with_object({}) do |r, h|
h[r.requester_id] = requester_label(r)
end
end
def opts_users
requesters = requesters_in_30_days
if approver?
# list all requesters
[label_value_hash_with_all(requesters).sort_by { |object| object[:label] }, 'all']
elsif requesters.value?(current_user.name)
# list just the current user
[[{:value => current_user.id, :label => current_user.name}], current_user.id]
else
# nothing to list
[[], current_user.id]
end
end
def states_for_checkboxes_i18n
PROV_STATES.map do |key, value|
{
:label => _(value),
:checked => true,
:value => key,
}
end
end
def user_options(params)
opts = {
:reason_text => params["reasonText"],
:applied_states => params["approvalStates"],
:type_choice => params["types"],
:user_choice => params["selectedUser"],
:time_period => params["selectedPeriod"],
}.compact
prov_set_default_options.merge(opts)
end
# FIXME: this has a big overlap with miq_request_initial_options.
# It is needed because the firts load of the GTL is done throught a different
# mechanism than the subsequent reloads.
def prov_set_default_options
{
:reason_text => nil,
:applied_states => PROV_STATES.keys,
:type_choice => 'all',
:user_choice => approver? ? 'all' : current_user.id,
:time_period => 7,
}
end
PROV_TIME_PERIODS = {
1 => N_("Last 24 Hours"),
7 => N_("Last 7 Days"),
30 => N_("Last 30 Days")
}.freeze
def time_periods_for_select_i18n
PROV_TIME_PERIODS.map { |k, v| {:label => _(v), :value => k} }
end
def label_value_hash_with_all(array)
array.each_with_object([{:label => _('All'), :value => 'all'}]) do |(value, label), a|
a << {:label => label, :value => value }
end
end
def miq_request_initial_options
users, selected_user = opts_users
{
:states => states_for_checkboxes_i18n,
:users => users,
:selectedUser => selected_user,
:types => label_value_hash_with_all(request_types_hash),
:selectedType => 'all',
:timePeriods => time_periods_for_select_i18n,
:selectedPeriod => 7,
:reasonText => nil,
:requestType => MiqRequest::MODEL_REQUEST_TYPES[model_request_type_from_layout].keys,
:savedFilters => session[:request_filters],
}
end
helper_method :miq_request_initial_options
# Find the request that was chosen
def identify_request
klass = @layout == "miq_request_ae" ? AutomationRequest : MiqRequest
@miq_request = @record = identify_record(params[:id], klass)
end
def approver?
# TODO: this should be current_user
User.current_user.role_allows?(:identifier => rbac_feature_id('miq_request_approval'))
end
def rbac_feature_id(feature_id)
# set this to be used to identify which Requests subtab was clicked
@request_tab = params[:typ] || session[:request_tab] if @request_tab.nil?
return feature_id if session[:request_tab] != "ae"
"#{@request_tab}_#{feature_id}"
end
# Delete all selected or single displayed action(s)
def deleterequests
assert_privileges(rbac_feature_id("miq_request_delete"))
miq_requests = find_records_with_rbac(MiqRequest, checked_or_params)
if miq_requests.empty?
add_flash(_("No Requests were selected for deletion"), :error)
else
destroy_requests(miq_requests)
end
unless flash_errors?
if @lastaction == "show_list" # showing a list
add_flash(_("The selected Requests were deleted"))
else
@single_delete = true
add_flash(_("The selected Request was deleted"))
end
end
if @flash_array.present?
flash_to_session
javascript_redirect(:action => 'show_list')
else
show_list
replace_gtl
end
end
def destroy_requests(miq_requests)
miq_requests.each do |miq_request|
request_name = miq_request.description
audit = {:event => "MiqRequest_record_delete",
:message => "[#{request_name}] Record deleted",
:target_id => miq_request.id,
:target_class => "MiqRequest",
:userid => session[:userid]}
begin
miq_request.destroy
rescue StandardError => bang
add_flash(_("Request \"%{name}\": Error during 'destroy': %{message}") %
{:name => request_name,
:message => bang.message},
:error)
else
AuditEvent.success(audit)
add_flash(_("Request \"%{name}\": Delete successful") % {:name => request_name})
end
end
end
def request_settings_for_edit_or_copy(provision_request)
@org_controller = provision_request.originating_controller
@redirect_controller = "miq_request"
@req_id = provision_request.id
@prov_type = provision_request.resource_type
@prov_id = provision_request.options[:src_configured_system_ids]
end
def request_edit_settings(provision_request)
@refresh_partial = "prov_edit"
request_settings_for_edit_or_copy(provision_request)
end
def get_session_data
super
@title = _("Requests")
@request_tab = session[:request_tab] if session[:request_tab]
@request_filters = session[:request_filters]
@layout = layout_from_tab_name(@request_tab)
@options = session[:prov_options] if session[:prov_options].present?
end
def set_session_data
super
session[:edit] = @edit unless @edit.nil?
session[:request_tab] = @request_tab unless @request_tab.nil?
session[:request_filters] = @request_filters
session[:prov_options] = @options if @options
end
def gtl_url
"/show"
end
def breadcrumbs_options
if @layout == "miq_request_ae" || @record&.type == "AutomationRequest"
{
:breadcrumbs => [
{:title => _("Automation")},
{:title => _("Automate"), :url => controller_url},
],
:record_title => :description,
}
else
{
:breadcrumbs => [
{:title => _("Services")},
{:title => _("Requests"), :url => controller_url},
],
:record_info => (MiqRequest.find(params[:req_id]) if params[:req_id]),
:record_title => :description,
}
end
end
end