<commit_before>class ApplicationHelper::ToolbarBuilder
  include MiqAeClassHelper
  include RestfulControllerMixin

  def call(toolbar_name)
    build_toolbar(toolbar_name)
  end

  # Loads the toolbar sent in parameter `toolbar_name`, and builds the buttons
  # in the toolbar, unless the group of buttons is meant to be skipped.
  #
  # Returns built toolbar loaded in instance variable `@toolbar`, or `nil`, if
  # no buttons should be in the toolbar.
  def build_toolbar(toolbar_name)
    @toolbar = []
    @groups_added = []
    @sep_needed = false
    @sep_added = false

    toolbar_class = toolbar_class(toolbar_name)
    toolbar_class.definition.each_with_index do |(name, group), group_index|
      next if group_skipped?(name)

      @sep_added = false
      @groups_added.push(group_index)
      case group
      when ApplicationHelper::Toolbar::Group
        group.buttons.each do |bgi|
          build_button(bgi, group_index)
        end
      when ApplicationHelper::Toolbar::Custom
        rendered_html = group.render(@view_context).tr('\'', '"')
        group[:args][:html] = ERB::Util.html_escape(rendered_html).html_safe
        @toolbar << group
      end
    end

    @toolbar = nil if @toolbar.empty?
    @toolbar
  end

  private

  delegate :request, :current_user, :to => :@view_context
  delegate :get_vmdb_config, :role_allows?, :model_for_vm, :rbac_common_feature_for_buttons, :to => :@view_context
  delegate :x_tree_history, :x_node, :x_active_tree, :to => :@view_context
  delegate :is_browser?, :is_browser_os?, :to => :@view_context

  def initialize(view_context, view_binding, instance_data)
    @view_context = view_context
    @view_binding = view_binding
    @instance_data = instance_data

    instance_data.each do |name, value|
      instance_variable_set(:"@#{name}", value)
    end
  end

  def eval(code)
    @view_binding.eval(code)
  end

  def safer_eval(code)
    code.to_s =~ /\#{/ ? eval("\"#{code}\"") : code
  end

  # Parses the generic toolbars name and returns his class
  def predefined_toolbar_class(tb_name)
    class_name = 'ApplicationHelper::Toolbar::' + ActiveSupport::Inflector.camelize(tb_name.sub(/_tb$/, ''))
    class_name.constantize
  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)
    else
      predefined_toolbar_class(toolbar_name)
    end
  end

  # Creates a button and sets it's properties
  def toolbar_button(inputs, props)
    button_class = inputs[:klass] || ApplicationHelper::Button::Basic
    props[:options] = inputs[:options] if inputs[:options]
    button = button_class.new(@view_context, @view_binding, @instance_data, props)
    button.skipped? ? nil : apply_common_props(button, inputs)
  end

  # Build select button and its child buttons
  def build_select_button(bgi, index)
    bs_children = false
    props = toolbar_button(
      bgi,
      :id     => bgi[:id],
      :type   => :buttonSelect,
      :img    => img = img_value(bgi),
      :imgdis => img,
    )
    return nil if props.nil?

    current_item = props
    current_item[:items] ||= []
    any_visible = false
    bgi[:items].each_with_index do |bsi, bsi_idx|
      if bsi.key?(:separator)
        props = ApplicationHelper::Button::Separator.new(:id => "sep_#{index}_#{bsi_idx}", :hidden => !any_visible)
      else
        next if hide_button?(bsi[:pressed] || bsi[:id]) # Use pressed, else button id
        bs_children = true
        props = toolbar_button(
          bsi,
          :child_id => bsi[:id],
          :id       => bgi[:id] + "__" + bsi[:id],
          :type     => :button,
          :img      => img = img_value(bsi),
          :img_url  => ActionController::Base.helpers.image_path("toolbars/#{img}"),
          :imgdis   => img,
        )
        next if props.nil?
      end
      update_common_props(bsi, props) unless bsi.key?(:separator)
      current_item[:items] << props

      any_visible ||= !props[:hidden] && props[:type] != :separator
    end
    current_item[:items].reverse_each do |item|
      break if !item[:hidden] && item[:type] != :separator
      item[:hidden] = true if item[:type] == :separator
    end
    current_item[:hidden] = !any_visible

    if bs_children
      @sep_added = true # Separator has officially been added
      @sep_needed = true # Need a separator from now on
    end
    current_item
  end

  # Set properties for button
  def apply_common_props(button, input)
    button.update(
      :icon    => input[:icon],
      :name    => button[:id],
      :hidden  => button[:hidden] || !!input[:hidden],
      :pressed => input[:pressed],
      :onwhen  => input[:onwhen],
      :data    => input[:data]
    )

    button[:enabled] = input[:enabled]
    %i(title text confirm).each do |key|
      unless input[key].blank?
        button[key] = button.localized(key, input[key])
      end
    end
    button[:url_parms] = update_url_parms(safer_eval(input[:url_parms])) unless input[:url_parms].blank?

    if input[:popup] # special behavior: button opens window_url in a new window
      button[:popup] = true
      button[:window_url] = "/#{request.parameters["controller"]}#{input[:url]}"
    end

    if input[:association_id] # special behavior to pass in id of association
      button[:url_parms] = "?show=#{request.parameters[:show]}"
    end

    dis_title = disable_button(button[:child_id] || button[:id])
    if dis_title
      button[:enabled] = false
      if dis_title.kind_of? String
        button[:title] = button.localized(:title, dis_title)
      end
    end
    button.calculate_properties
    button
  end

  # Build single button
  def build_normal_button(bgi, index)
    button_hide = hide_button?(bgi[:id])
    if button_hide
      # These buttons need to be present even if hidden as we show/hide them dynamically
      return nil unless %w(timeline_txt timeline_csv timeline_pdf).include?(bgi[:id])
    end

    @sep_needed = true unless button_hide
    props = toolbar_button(
      bgi,
      :id      => bgi[:id],
      :type    => :button,
      :img     => img = "#{get_image(bgi[:image], bgi[:id]) ? get_image(bgi[:image], bgi[:id]) : bgi[:id]}.png",
      :img_url => ActionController::Base.helpers.image_path("toolbars/#{img}"),
      :imgdis  => "#{bgi[:image] || bgi[:id]}.png",
    )
    return nil if props.nil?

    # set pdf button to be hidden if graphical summary screen is set by default
    props[:hidden] = %w(download_view vm_download_pdf).include?(bgi[:id]) && button_hide

    _add_separator(index)
    props
  end

  def _add_separator(index)
    # Add a separator, if needed, before this button
    if !@sep_added && @sep_needed
      if @groups_added.include?(index) && @groups_added.length > 1
        @toolbar << ApplicationHelper::Button::Separator.new(:id => "sep_#{index}")
        @sep_added = true
      end
    end
    @sep_needed = true # Button was added, need separators from now on
  end

  def img_value(button)
    "#{button[:image] || button[:id]}.png"
  end

  # Build button with more states
  def build_twostate_button(bgi, index)
    return nil if hide_button?(bgi[:id])

    props = toolbar_button(
      bgi,
      :id     => bgi[:id],
      :type   => :buttonTwoState,
      :img    => img = img_value(bgi),
      :imgdis => img,
    )
    return nil if props.nil?

    props[:selected] = twostate_button_selected(bgi[:id])

    _add_separator(index)
    props
  end

  # According to button type in toolbar definition calls appropriate method
  def build_button(bgi, index)
    props = case bgi[:type]
            when :buttonSelect   then build_select_button(bgi, index)
            when :button         then build_normal_button(bgi, index)
            when :buttonTwoState then build_twostate_button(bgi, index)
            end

    unless props.nil?
      @toolbar << update_common_props(bgi, props)
    end
  end

  # @button_group is set in a controller
  #
  def group_skipped?(name)
    @button_group && (!name.starts_with?(@button_group + "_") &&
      !name.starts_with?("custom") && !name.starts_with?("dialog") &&
      !name.starts_with?("miq_dialog") && !name.starts_with?("custom_button") &&
      !name.starts_with?("instance_") && !name.starts_with?("image_")) &&
       !%w(record_summary summary_main summary_download tree_main
           x_edit_view_tb history_main ems_container_dashboard ems_infra_dashboard).include?(name)
  end

  def create_custom_button_hash(input, record, options = {})
    options[:enabled] = true unless options.key?(:enabled)
    button_id = input[:id]
    button_name = input[:name].to_s
    button = {
      :id        => "custom__custom_#{button_id}",
      :type      => :button,
      :icon      => "product product-custom-#{input[:image]} fa-lg",
      :title     => input[:description].to_s,
      :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}"
    }
    button[:text] = button_name if input[:text_display]
    button
  end

  def create_raw_custom_button_hash(cb, record)
    {
      :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
    }
  end

  def custom_buttons_hash(record)
    get_custom_buttons(record).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) }
      }
      props[:text] = group[:text] if group[:text_display]

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

  def custom_toolbar_class(record)
    # each custom toolbar is an anonymous subclass of this class
    toolbar = Class.new(ApplicationHelper::Toolbar::Basic)
    custom_buttons_hash(record).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)
    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
  end

  def service_template_id(record)
    case record
    when Service then         record.service_template_id
    when ServiceTemplate then record.id
    end
  end

  def record_to_service_buttons(record)
    return [] unless record.kind_of?(Service)
    return [] if record.service_template.nil?
    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|
      group = {
        :id           => cbs.id,
        :text         => cbs.name.split("|").first,
        :description  => cbs.description,
        :image        => cbs.set_data[:button_image],
        :text_display => cbs.set_data.key?(:display) ? cbs.set_data[:display] : true
      }

      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
      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
        ordered_buttons = []
        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)
              break
            end
          end
        end
        group[:buttons] = ordered_buttons
      end
      group
    end
  end

  def get_image(img, b_name)
    # to change summary screen button to green image
    return "summary-green" if b_name == "show_summary" && %w(miq_schedule miq_task scan_profile).include?(@layout)
    img
  end

  def hide_button_ops(id)
    case x_active_tree
    when :settings_tree
      return ["schedule_run_now"].include?(id)
    when :diagnostics_tree
      case @sb[:active_tab]
      when "diagnostics_audit_log"
        return !["fetch_audit_log", "refresh_audit_log"].include?(id)
      when "diagnostics_collect_logs"
        return !%(collect_current_logs collect_logs log_depot_edit
                  zone_collect_current_logs zone_collect_logs
                  zone_log_depot_edit).include?(id)
      when "diagnostics_evm_log"
        return !["fetch_log", "refresh_log"].include?(id)
      when "diagnostics_production_log"
        return !["fetch_production_log", "refresh_production_log"].include?(id)
      when "diagnostics_roles_servers", "diagnostics_servers_roles"
        case id
        when "reload_server_tree"
          return false
        when "zone_collect_current_logs", "zone_collect_logs", "zone_log_depot_edit"
          return true
        end
        return false
      when "diagnostics_summary"
        return !["refresh_server_summary", "restart_server"].include?(id)
      when "diagnostics_workers"
        return !%w(restart_workers refresh_workers).include?(id)
      else
        return true
      end
    when :rbac_tree
      return true unless role_allows?(:feature => rbac_common_feature_for_buttons(id))
      return true if %w(rbac_project_add rbac_tenant_add).include?(id) && @record.project?
      return false
    when :vmdb_tree
      return !["db_connections", "db_details", "db_indexes", "db_settings"].include?(@sb[:active_tab])
    else
      return true
    end
  end

  def hide_button_pxe(id)
    case x_active_tree
    when :customization_templates_tree
      return true unless role_allows?(:feature => id)
      nodes = x_node.split('-')
      if nodes.first == "root"
        # show only add button on root node
        id != "customization_template_new"
      elsif nodes.last == "system" || (@record && @record.system)
        # allow only copy button for system customization templates
        id != "customization_template_copy"
      else
        false
      end
    end
  end

  def hide_button_report(id)
    if %w(miq_report_copy miq_report_delete miq_report_edit
          miq_report_new miq_report_run miq_report_schedule_add).include?(id) ||
       x_active_tree == :schedules_tree
      return true unless role_allows?(:feature => id)
    end
    case x_active_tree
    when :widgets_tree
      case id
      when "widget_new"
        return x_node == "root"
      when "widget_generate_content"
        return @sb[:wtype] == "m"
      end
      return false
    when :reports_tree
      case id
      when "saved_report_delete", "reload"
        return @sb[:active_tab] != "saved_reports"
      when "miq_report_edit", "miq_report_delete"
        return @sb[:active_tab] == "report_info" && @record.rpt_type == "Custom" ?
               false : true
      when "miq_report_copy", "miq_report_new", "miq_report_run", "miq_report_only", "miq_report_schedule_add"
        return @sb[:active_tab] == "saved_reports"
      when "view_graph", "view_hybrid", "view_tabular"
        return @ght_type && @report && @report.graph &&
          !(@zgraph || (@ght_type == "tabular" && @html))
      end
    when :savedreports_tree
      if %w(saved_report_delete).include?(id)
        return true unless role_allows?(:feature => id)
      end
      case id
      when "reload"
        return x_node != "root"
      when "view_graph", "view_hybrid", "view_tabular"
        return @ght_type && @report && @report.graph &&
          !(@zgraph || (@ght_type == "tabular" && @html))
      end
    else
      return false
    end
  end

  # Determine if a button should be hidden
  def hide_button?(id)
    return false if id.start_with?('history_')
    return true if id == "blank_button" # Always hide the blank button placeholder

    # Hide configuration buttons for specific Container* entities
    return true if %w(container_node_edit container_node_delete container_node_new).include?(id) &&
                   (@record.kind_of?(ContainerNode) || @record.nil?)

    return true if %w(container_service_edit container_service_delete container_service_new).include?(id) &&
                   (@record.kind_of?(ContainerService) || @record.nil?)

    return true if %w(container_group_edit container_group_delete container_group_new).include?(id) &&
                   (@record.kind_of?(ContainerGroup) || @record.nil?)

    return true if %w(container_edit container_delete container_new).include?(id) &&
                   (@record.kind_of?(Container) || @record.nil?)

    return true if %w(container_replicator_edit container_replicator_delete container_replicator_new).include?(id) &&
                   (@record.kind_of?(ContainerReplicator) || @record.nil?)

    return true if %w(container_image_registry_edit container_image_registry_delete
                      container_image_registry_new).include?(id) &&
                   (@record.kind_of?(ContainerImageRegistry) || @record.nil?)

    return true if %w(persistent_volume_edit persistent_volume_delete persistent_volume_new).include?(id) &&
                   (@record.kind_of?(PersistentVolume) || @record.nil?)

    return true if %w(container_build_edit container_build_delete container_build_new).include?(id) &&
                   (@record.kind_of?(ContainerBuild) || @record.nil?)

    return true if %w(container_template_edit container_template_delete container_template_new).include?(id) &&
                   (@record.kind_of?(ContainerTemplate) || @record.nil?)

    # hide compliance check and comparison buttons rendered for orchestration stack instances
    return true if @record.kind_of?(OrchestrationStack) && @display == "instances" &&
                   %w(instance_check_compliance instance_compare).include?(id)

    # don't hide view buttons in toolbar
    return false if %w(view_grid view_tile view_list view_dashboard view_summary view_topology
      refresh_log fetch_log common_drift download_text download_csv download_pdf download_view vm_download_pdf
      tree_large tree_small).include?(id) && !%w(miq_policy_rsop ops).include?(@layout)

    # dont hide back to summary button button when not in explorer
    return false if id == "show_summary" && !@explorer

    # need to hide add buttons when on sub-list view screen of a CI.
    return true if id.ends_with?("_new", "_discover") &&
                   @lastaction == "show" && !["main", "vms"].include?(@display)

    if id == "summary_reload"                             # Show reload button if
      return @explorer && # we are in explorer and
        ((@record && #    1) we are on a record and
         !["miq_policy_rsop"].include?(@layout) && # @layout is not one of these
         !["details", "item"].include?(@showtype)) || #       not showing list or single sub screen item i.e VM/Users
         @lastaction == "show_list") ? # or 2) selected node shows a list of records
        false : true
    end

    # user can see the buttons if they can get to Policy RSOP/Automate Simulate screen
    return false if ["miq_ae_tools"].include?(@layout)

    # buttons on compare/drift screen are allowed if user has access to compare/drift
    return false if id.starts_with?("compare_", "drift_", "comparemode_", "driftmode_")

    # Allow custom buttons on CI show screen, user can see custom button if they can get to show screen
    return false if id.starts_with?("custom_")

    return false if id == "miq_request_reload" && # Show the request reload button
                    (@lastaction == "show_list" || @showtype == "miq_provisions")

    if @layout == "ops"
      res = hide_button_ops(id)
      return res
    end

    if @layout == "pxe" || id.starts_with?("pxe_", "customization_template_")
      res = hide_button_pxe(id)
      return res
    end

    if @layout == "report"
      res = hide_button_report(id)
      return res
    end

    return false if role_allows?(:feature => "my_settings_time_profiles") && @layout == "configuration" &&
                    @tabform == "ui_4"

    return false if id.starts_with?("miq_capacity_") && @sb[:active_tab] == "report"

    # don't check for feature RBAC if id is miq_request_approve/deny
    unless %w(miq_policy catalogs).include?(@layout)
      return true if !role_allows?(:feature => id) && !["miq_request_approve", "miq_request_deny"].include?(id) &&
                     id !~ /^history_\d*/ &&
                     !id.starts_with?("dialog_") && !id.starts_with?("miq_task_")
    end

    # Check buttons with other restriction logic
    case id
    when "dialog_add_box", "dialog_add_element", "dialog_add_tab", "dialog_res_discard", "dialog_resource_remove"
      return true unless @edit
      return true if id == "dialog_res_discard" && @sb[:edit_typ] != "add"
      return true if id == "dialog_resource_remove" && (@sb[:edit_typ] == "add" || x_node == "root")
      nodes = x_node.split('_')
      return true if id == "dialog_add_tab" && (nodes.length > 2)
      return true if id == "dialog_add_box" && (nodes.length < 2 || nodes.length > 3)
      return true if id == "dialog_add_element" && (nodes.length < 3 || nodes.length > 4)
    when "dialog_copy", "dialog_delete", "dialog_edit", "dialog_new"
      return true if @edit && @edit[:current]
    when "miq_task_canceljob"
      return true unless ["all_tasks", "all_ui_tasks"].include?(@layout)
    when "vm_console"
      type = get_vmdb_config.fetch_path(:server, :remote_console_type)
      return type != 'MKS' || !@record.console_supported?(type)
    when "vm_vnc_console"
      type = get_vmdb_config.fetch_path(:server, :remote_console_type)
      return type != 'VNC' || !@record.console_supported?(type)
    when "vm_vmrc_console"
      type = get_vmdb_config.fetch_path(:server, :remote_console_type)
      return type != 'VMRC' || !@record.console_supported?(type)
    # Check buttons behind SMIS setting
    when "ontap_storage_system_statistics", "ontap_logical_disk_statistics", "ontap_storage_volume_statistics",
        "ontap_file_share_statistics"
      return true unless get_vmdb_config[:product][:smis]
    when "host_register_nodes"
      return true if @record.class != ManageIQ::Providers::Openstack::InfraManager
    when "host_introspect", "host_provide"
      return true unless @record.class == ManageIQ::Providers::Openstack::InfraManager ||
                         @record.class == ManageIQ::Providers::Openstack::InfraManager::Host
      return true if @record.class == ManageIQ::Providers::Openstack::InfraManager::Host &&
                     @record.hardware.provision_state != "manageable"
    when "host_manageable"
      return true unless @record.class == ManageIQ::Providers::Openstack::InfraManager ||
                         @record.class == ManageIQ::Providers::Openstack::InfraManager::Host
      return true if @record.class == ManageIQ::Providers::Openstack::InfraManager::Host &&
                     @record.hardware.provision_state == "manageable"
    end

    # Scale is only supported by OpenStack Infrastructure Provider
    return true if (id == "ems_infra_scale" || id == "ems_infra_scaledown") &&
                   (@record.class != ManageIQ::Providers::Openstack::InfraManager ||
                    !role_allows?(:feature => "ems_infra_scale") ||
                   (@record.class == ManageIQ::Providers::Openstack::InfraManager && @record.orchestration_stacks.count == 0))<commit_msg>use ::Settings in toolbar_builder.rb<commit_after>class ApplicationHelper::ToolbarBuilder
  include MiqAeClassHelper
  include RestfulControllerMixin

  def call(toolbar_name)
    build_toolbar(toolbar_name)
  end

  # Loads the toolbar sent in parameter `toolbar_name`, and builds the buttons
  # in the toolbar, unless the group of buttons is meant to be skipped.
  #
  # Returns built toolbar loaded in instance variable `@toolbar`, or `nil`, if
  # no buttons should be in the toolbar.
  def build_toolbar(toolbar_name)
    @toolbar = []
    @groups_added = []
    @sep_needed = false
    @sep_added = false

    toolbar_class = toolbar_class(toolbar_name)
    toolbar_class.definition.each_with_index do |(name, group), group_index|
      next if group_skipped?(name)

      @sep_added = false
      @groups_added.push(group_index)
      case group
      when ApplicationHelper::Toolbar::Group
        group.buttons.each do |bgi|
          build_button(bgi, group_index)
        end
      when ApplicationHelper::Toolbar::Custom
        rendered_html = group.render(@view_context).tr('\'', '"')
        group[:args][:html] = ERB::Util.html_escape(rendered_html).html_safe
        @toolbar << group
      end
    end

    @toolbar = nil if @toolbar.empty?
    @toolbar
  end

  private

  delegate :request, :current_user, :to => :@view_context
  delegate :role_allows?, :model_for_vm, :rbac_common_feature_for_buttons, :to => :@view_context
  delegate :x_tree_history, :x_node, :x_active_tree, :to => :@view_context
  delegate :is_browser?, :is_browser_os?, :to => :@view_context

  def initialize(view_context, view_binding, instance_data)
    @view_context = view_context
    @view_binding = view_binding
    @instance_data = instance_data

    instance_data.each do |name, value|
      instance_variable_set(:"@#{name}", value)
    end
  end

  def eval(code)
    @view_binding.eval(code)
  end

  def safer_eval(code)
    code.to_s =~ /\#{/ ? eval("\"#{code}\"") : code
  end

  # Parses the generic toolbars name and returns his class
  def predefined_toolbar_class(tb_name)
    class_name = 'ApplicationHelper::Toolbar::' + ActiveSupport::Inflector.camelize(tb_name.sub(/_tb$/, ''))
    class_name.constantize
  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)
    else
      predefined_toolbar_class(toolbar_name)
    end
  end

  # Creates a button and sets it's properties
  def toolbar_button(inputs, props)
    button_class = inputs[:klass] || ApplicationHelper::Button::Basic
    props[:options] = inputs[:options] if inputs[:options]
    button = button_class.new(@view_context, @view_binding, @instance_data, props)
    button.skipped? ? nil : apply_common_props(button, inputs)
  end

  # Build select button and its child buttons
  def build_select_button(bgi, index)
    bs_children = false
    props = toolbar_button(
      bgi,
      :id     => bgi[:id],
      :type   => :buttonSelect,
      :img    => img = img_value(bgi),
      :imgdis => img,
    )
    return nil if props.nil?

    current_item = props
    current_item[:items] ||= []
    any_visible = false
    bgi[:items].each_with_index do |bsi, bsi_idx|
      if bsi.key?(:separator)
        props = ApplicationHelper::Button::Separator.new(:id => "sep_#{index}_#{bsi_idx}", :hidden => !any_visible)
      else
        next if hide_button?(bsi[:pressed] || bsi[:id]) # Use pressed, else button id
        bs_children = true
        props = toolbar_button(
          bsi,
          :child_id => bsi[:id],
          :id       => bgi[:id] + "__" + bsi[:id],
          :type     => :button,
          :img      => img = img_value(bsi),
          :img_url  => ActionController::Base.helpers.image_path("toolbars/#{img}"),
          :imgdis   => img,
        )
        next if props.nil?
      end
      update_common_props(bsi, props) unless bsi.key?(:separator)
      current_item[:items] << props

      any_visible ||= !props[:hidden] && props[:type] != :separator
    end
    current_item[:items].reverse_each do |item|
      break if !item[:hidden] && item[:type] != :separator
      item[:hidden] = true if item[:type] == :separator
    end
    current_item[:hidden] = !any_visible

    if bs_children
      @sep_added = true # Separator has officially been added
      @sep_needed = true # Need a separator from now on
    end
    current_item
  end

  # Set properties for button
  def apply_common_props(button, input)
    button.update(
      :icon    => input[:icon],
      :name    => button[:id],
      :hidden  => button[:hidden] || !!input[:hidden],
      :pressed => input[:pressed],
      :onwhen  => input[:onwhen],
      :data    => input[:data]
    )

    button[:enabled] = input[:enabled]
    %i(title text confirm).each do |key|
      unless input[key].blank?
        button[key] = button.localized(key, input[key])
      end
    end
    button[:url_parms] = update_url_parms(safer_eval(input[:url_parms])) unless input[:url_parms].blank?

    if input[:popup] # special behavior: button opens window_url in a new window
      button[:popup] = true
      button[:window_url] = "/#{request.parameters["controller"]}#{input[:url]}"
    end

    if input[:association_id] # special behavior to pass in id of association
      button[:url_parms] = "?show=#{request.parameters[:show]}"
    end

    dis_title = disable_button(button[:child_id] || button[:id])
    if dis_title
      button[:enabled] = false
      if dis_title.kind_of? String
        button[:title] = button.localized(:title, dis_title)
      end
    end
    button.calculate_properties
    button
  end

  # Build single button
  def build_normal_button(bgi, index)
    button_hide = hide_button?(bgi[:id])
    if button_hide
      # These buttons need to be present even if hidden as we show/hide them dynamically
      return nil unless %w(timeline_txt timeline_csv timeline_pdf).include?(bgi[:id])
    end

    @sep_needed = true unless button_hide
    props = toolbar_button(
      bgi,
      :id      => bgi[:id],
      :type    => :button,
      :img     => img = "#{get_image(bgi[:image], bgi[:id]) ? get_image(bgi[:image], bgi[:id]) : bgi[:id]}.png",
      :img_url => ActionController::Base.helpers.image_path("toolbars/#{img}"),
      :imgdis  => "#{bgi[:image] || bgi[:id]}.png",
    )
    return nil if props.nil?

    # set pdf button to be hidden if graphical summary screen is set by default
    props[:hidden] = %w(download_view vm_download_pdf).include?(bgi[:id]) && button_hide

    _add_separator(index)
    props
  end

  def _add_separator(index)
    # Add a separator, if needed, before this button
    if !@sep_added && @sep_needed
      if @groups_added.include?(index) && @groups_added.length > 1
        @toolbar << ApplicationHelper::Button::Separator.new(:id => "sep_#{index}")
        @sep_added = true
      end
    end
    @sep_needed = true # Button was added, need separators from now on
  end

  def img_value(button)
    "#{button[:image] || button[:id]}.png"
  end

  # Build button with more states
  def build_twostate_button(bgi, index)
    return nil if hide_button?(bgi[:id])

    props = toolbar_button(
      bgi,
      :id     => bgi[:id],
      :type   => :buttonTwoState,
      :img    => img = img_value(bgi),
      :imgdis => img,
    )
    return nil if props.nil?

    props[:selected] = twostate_button_selected(bgi[:id])

    _add_separator(index)
    props
  end

  # According to button type in toolbar definition calls appropriate method
  def build_button(bgi, index)
    props = case bgi[:type]
            when :buttonSelect   then build_select_button(bgi, index)
            when :button         then build_normal_button(bgi, index)
            when :buttonTwoState then build_twostate_button(bgi, index)
            end

    unless props.nil?
      @toolbar << update_common_props(bgi, props)
    end
  end

  # @button_group is set in a controller
  #
  def group_skipped?(name)
    @button_group && (!name.starts_with?(@button_group + "_") &&
      !name.starts_with?("custom") && !name.starts_with?("dialog") &&
      !name.starts_with?("miq_dialog") && !name.starts_with?("custom_button") &&
      !name.starts_with?("instance_") && !name.starts_with?("image_")) &&
       !%w(record_summary summary_main summary_download tree_main
           x_edit_view_tb history_main ems_container_dashboard ems_infra_dashboard).include?(name)
  end

  def create_custom_button_hash(input, record, options = {})
    options[:enabled] = true unless options.key?(:enabled)
    button_id = input[:id]
    button_name = input[:name].to_s
    button = {
      :id        => "custom__custom_#{button_id}",
      :type      => :button,
      :icon      => "product product-custom-#{input[:image]} fa-lg",
      :title     => input[:description].to_s,
      :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}"
    }
    button[:text] = button_name if input[:text_display]
    button
  end

  def create_raw_custom_button_hash(cb, record)
    {
      :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
    }
  end

  def custom_buttons_hash(record)
    get_custom_buttons(record).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) }
      }
      props[:text] = group[:text] if group[:text_display]

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

  def custom_toolbar_class(record)
    # each custom toolbar is an anonymous subclass of this class
    toolbar = Class.new(ApplicationHelper::Toolbar::Basic)
    custom_buttons_hash(record).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)
    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
  end

  def service_template_id(record)
    case record
    when Service then         record.service_template_id
    when ServiceTemplate then record.id
    end
  end

  def record_to_service_buttons(record)
    return [] unless record.kind_of?(Service)
    return [] if record.service_template.nil?
    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|
      group = {
        :id           => cbs.id,
        :text         => cbs.name.split("|").first,
        :description  => cbs.description,
        :image        => cbs.set_data[:button_image],
        :text_display => cbs.set_data.key?(:display) ? cbs.set_data[:display] : true
      }

      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
      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
        ordered_buttons = []
        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)
              break
            end
          end
        end
        group[:buttons] = ordered_buttons
      end
      group
    end
  end

  def get_image(img, b_name)
    # to change summary screen button to green image
    return "summary-green" if b_name == "show_summary" && %w(miq_schedule miq_task scan_profile).include?(@layout)
    img
  end

  def hide_button_ops(id)
    case x_active_tree
    when :settings_tree
      return ["schedule_run_now"].include?(id)
    when :diagnostics_tree
      case @sb[:active_tab]
      when "diagnostics_audit_log"
        return !["fetch_audit_log", "refresh_audit_log"].include?(id)
      when "diagnostics_collect_logs"
        return !%(collect_current_logs collect_logs log_depot_edit
                  zone_collect_current_logs zone_collect_logs
                  zone_log_depot_edit).include?(id)
      when "diagnostics_evm_log"
        return !["fetch_log", "refresh_log"].include?(id)
      when "diagnostics_production_log"
        return !["fetch_production_log", "refresh_production_log"].include?(id)
      when "diagnostics_roles_servers", "diagnostics_servers_roles"
        case id
        when "reload_server_tree"
          return false
        when "zone_collect_current_logs", "zone_collect_logs", "zone_log_depot_edit"
          return true
        end
        return false
      when "diagnostics_summary"
        return !["refresh_server_summary", "restart_server"].include?(id)
      when "diagnostics_workers"
        return !%w(restart_workers refresh_workers).include?(id)
      else
        return true
      end
    when :rbac_tree
      return true unless role_allows?(:feature => rbac_common_feature_for_buttons(id))
      return true if %w(rbac_project_add rbac_tenant_add).include?(id) && @record.project?
      return false
    when :vmdb_tree
      return !["db_connections", "db_details", "db_indexes", "db_settings"].include?(@sb[:active_tab])
    else
      return true
    end
  end

  def hide_button_pxe(id)
    case x_active_tree
    when :customization_templates_tree
      return true unless role_allows?(:feature => id)
      nodes = x_node.split('-')
      if nodes.first == "root"
        # show only add button on root node
        id != "customization_template_new"
      elsif nodes.last == "system" || (@record && @record.system)
        # allow only copy button for system customization templates
        id != "customization_template_copy"
      else
        false
      end
    end
  end

  def hide_button_report(id)
    if %w(miq_report_copy miq_report_delete miq_report_edit
          miq_report_new miq_report_run miq_report_schedule_add).include?(id) ||
       x_active_tree == :schedules_tree
      return true unless role_allows?(:feature => id)
    end
    case x_active_tree
    when :widgets_tree
      case id
      when "widget_new"
        return x_node == "root"
      when "widget_generate_content"
        return @sb[:wtype] == "m"
      end
      return false
    when :reports_tree
      case id
      when "saved_report_delete", "reload"
        return @sb[:active_tab] != "saved_reports"
      when "miq_report_edit", "miq_report_delete"
        return @sb[:active_tab] == "report_info" && @record.rpt_type == "Custom" ?
               false : true
      when "miq_report_copy", "miq_report_new", "miq_report_run", "miq_report_only", "miq_report_schedule_add"
        return @sb[:active_tab] == "saved_reports"
      when "view_graph", "view_hybrid", "view_tabular"
        return @ght_type && @report && @report.graph &&
          !(@zgraph || (@ght_type == "tabular" && @html))
      end
    when :savedreports_tree
      if %w(saved_report_delete).include?(id)
        return true unless role_allows?(:feature => id)
      end
      case id
      when "reload"
        return x_node != "root"
      when "view_graph", "view_hybrid", "view_tabular"
        return @ght_type && @report && @report.graph &&
          !(@zgraph || (@ght_type == "tabular" && @html))
      end
    else
      return false
    end
  end

  # Determine if a button should be hidden
  def hide_button?(id)
    return false if id.start_with?('history_')
    return true if id == "blank_button" # Always hide the blank button placeholder

    # Hide configuration buttons for specific Container* entities
    return true if %w(container_node_edit container_node_delete container_node_new).include?(id) &&
                   (@record.kind_of?(ContainerNode) || @record.nil?)

    return true if %w(container_service_edit container_service_delete container_service_new).include?(id) &&
                   (@record.kind_of?(ContainerService) || @record.nil?)

    return true if %w(container_group_edit container_group_delete container_group_new).include?(id) &&
                   (@record.kind_of?(ContainerGroup) || @record.nil?)

    return true if %w(container_edit container_delete container_new).include?(id) &&
                   (@record.kind_of?(Container) || @record.nil?)

    return true if %w(container_replicator_edit container_replicator_delete container_replicator_new).include?(id) &&
                   (@record.kind_of?(ContainerReplicator) || @record.nil?)

    return true if %w(container_image_registry_edit container_image_registry_delete
                      container_image_registry_new).include?(id) &&
                   (@record.kind_of?(ContainerImageRegistry) || @record.nil?)

    return true if %w(persistent_volume_edit persistent_volume_delete persistent_volume_new).include?(id) &&
                   (@record.kind_of?(PersistentVolume) || @record.nil?)

    return true if %w(container_build_edit container_build_delete container_build_new).include?(id) &&
                   (@record.kind_of?(ContainerBuild) || @record.nil?)

    return true if %w(container_template_edit container_template_delete container_template_new).include?(id) &&
                   (@record.kind_of?(ContainerTemplate) || @record.nil?)

    # hide compliance check and comparison buttons rendered for orchestration stack instances
    return true if @record.kind_of?(OrchestrationStack) && @display == "instances" &&
                   %w(instance_check_compliance instance_compare).include?(id)

    # don't hide view buttons in toolbar
    return false if %w(view_grid view_tile view_list view_dashboard view_summary view_topology
      refresh_log fetch_log common_drift download_text download_csv download_pdf download_view vm_download_pdf
      tree_large tree_small).include?(id) && !%w(miq_policy_rsop ops).include?(@layout)

    # dont hide back to summary button button when not in explorer
    return false if id == "show_summary" && !@explorer

    # need to hide add buttons when on sub-list view screen of a CI.
    return true if id.ends_with?("_new", "_discover") &&
                   @lastaction == "show" && !["main", "vms"].include?(@display)

    if id == "summary_reload"                             # Show reload button if
      return @explorer && # we are in explorer and
        ((@record && #    1) we are on a record and
         !["miq_policy_rsop"].include?(@layout) && # @layout is not one of these
         !["details", "item"].include?(@showtype)) || #       not showing list or single sub screen item i.e VM/Users
         @lastaction == "show_list") ? # or 2) selected node shows a list of records
        false : true
    end

    # user can see the buttons if they can get to Policy RSOP/Automate Simulate screen
    return false if ["miq_ae_tools"].include?(@layout)

    # buttons on compare/drift screen are allowed if user has access to compare/drift
    return false if id.starts_with?("compare_", "drift_", "comparemode_", "driftmode_")

    # Allow custom buttons on CI show screen, user can see custom button if they can get to show screen
    return false if id.starts_with?("custom_")

    return false if id == "miq_request_reload" && # Show the request reload button
                    (@lastaction == "show_list" || @showtype == "miq_provisions")

    if @layout == "ops"
      res = hide_button_ops(id)
      return res
    end

    if @layout == "pxe" || id.starts_with?("pxe_", "customization_template_")
      res = hide_button_pxe(id)
      return res
    end

    if @layout == "report"
      res = hide_button_report(id)
      return res
    end

    return false if role_allows?(:feature => "my_settings_time_profiles") && @layout == "configuration" &&
                    @tabform == "ui_4"

    return false if id.starts_with?("miq_capacity_") && @sb[:active_tab] == "report"

    # don't check for feature RBAC if id is miq_request_approve/deny
    unless %w(miq_policy catalogs).include?(@layout)
      return true if !role_allows?(:feature => id) && !["miq_request_approve", "miq_request_deny"].include?(id) &&
                     id !~ /^history_\d*/ &&
                     !id.starts_with?("dialog_") && !id.starts_with?("miq_task_")
    end

    # Check buttons with other restriction logic
    case id
    when "dialog_add_box", "dialog_add_element", "dialog_add_tab", "dialog_res_discard", "dialog_resource_remove"
      return true unless @edit
      return true if id == "dialog_res_discard" && @sb[:edit_typ] != "add"
      return true if id == "dialog_resource_remove" && (@sb[:edit_typ] == "add" || x_node == "root")
      nodes = x_node.split('_')
      return true if id == "dialog_add_tab" && (nodes.length > 2)
      return true if id == "dialog_add_box" && (nodes.length < 2 || nodes.length > 3)
      return true if id == "dialog_add_element" && (nodes.length < 3 || nodes.length > 4)
    when "dialog_copy", "dialog_delete", "dialog_edit", "dialog_new"
      return true if @edit && @edit[:current]
    when "miq_task_canceljob"
      return true unless ["all_tasks", "all_ui_tasks"].include?(@layout)
    when "vm_console"
      type = ::Settings.server.remote_console_type
      return type != 'MKS' || !@record.console_supported?(type)
    when "vm_vnc_console"
      type = ::Settings.server.remote_console_type
      return type != 'VNC' || !@record.console_supported?(type)
    when "vm_vmrc_console"
      type = ::Settings.server.remote_console_type
      return type != 'VMRC' || !@record.console_supported?(type)
    # Check buttons behind SMIS setting
    when "ontap_storage_system_statistics", "ontap_logical_disk_statistics", "ontap_storage_volume_statistics",
        "ontap_file_share_statistics"
      return true unless ::Settings.product.smis
    when "host_register_nodes"
      return true if @record.class != ManageIQ::Providers::Openstack::InfraManager
    when "host_introspect", "host_provide"
      return true unless @record.class == ManageIQ::Providers::Openstack::InfraManager ||
                         @record.class == ManageIQ::Providers::Openstack::InfraManager::Host
      return true if @record.class == ManageIQ::Providers::Openstack::InfraManager::Host &&
                     @record.hardware.provision_state != "manageable"
    when "host_manageable"
      return true unless @record.class == ManageIQ::Providers::Openstack::InfraManager ||
                         @record.class == ManageIQ::Providers::Openstack::InfraManager::Host
      return true if @record.class == ManageIQ::Providers::Openstack::InfraManager::Host &&
                     @record.hardware.provision_state == "manageable"
    end

    # Scale is only supported by OpenStack Infrastructure Provider
    return true if (id == "ems_infra_scale" || id == "ems_infra_scaledown") &&
                   (@record.class != ManageIQ::Providers::Openstack::InfraManager ||
                    !role_allows?(:feature => "ems_infra_scale") ||
                   (@record.class == ManageIQ::Providers::Openstack::InfraManager && @record.orchestration_stacks.count == 0))
{'fraction_non_alphanumeric': 0.11697566154851356, 'fraction_numerical': 0.00040836327997386475, 'mean_word_length': 3.113640181421132, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 147, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '972065', 'n_tokens_mistral': 15185, 'n_tokens_neox': 14930, 'n_words': 3506}