Skip to content

Commit

Permalink
Merge pull request #5617 from martinpovolny/menu_settings
Browse files Browse the repository at this point in the history
Menu item adding through settings
  • Loading branch information
mzazrivec authored Jun 21, 2019
2 parents c594273 + 99d5cb8 commit 5c66951
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 13 deletions.
20 changes: 18 additions & 2 deletions app/presenters/menu/item.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Menu
Item = Struct.new(:id, :name, :feature, :rbac_feature, :href, :type, :parent_id, :defaults) do
Item = Struct.new(:id, :name, :feature, :rbac_feature, :href, :type, :parent_id, :defaults, :icon) do
extend ActiveModel::Naming

def self.base_class
Expand All @@ -10,7 +10,7 @@ def self.base_model
model_name
end

def initialize(an_id, a_name, features, rbac_feature, href, type = :default, parent_id = nil, defaults = nil)
def initialize(an_id, a_name, features, rbac_feature, href, type = :default, parent_id = nil, defaults = nil, icon = nil)
super
@parent = nil
@name = a_name.kind_of?(Proc) ? a_name : -> { a_name }
Expand Down Expand Up @@ -58,5 +58,21 @@ def parent_path
def item(item_id)
item_id == id ? self : nil
end

def placement
@parent&.placement || :default
end

def contains_item_id?(item_id)
item_id == id
end

def subsection?
false
end

def items
[]
end
end
end
15 changes: 10 additions & 5 deletions app/presenters/menu/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def items

def item(item_id)
@menu.each do |menu_section|
return menu_section if menu_section.kind_of?(Menu::Item) && menu_section.id == item_id

menu_section.items.each do |el|
the_item = el.item(item_id)
return the_item if the_item.present?
Expand Down Expand Up @@ -73,6 +75,7 @@ def initialize
load_default_items
load_custom_items(Menu::YamlLoader)
load_custom_items(Menu::CustomLoader)
load_custom_items(Menu::SettingsLoader)
end

def merge_sections(sections)
Expand Down Expand Up @@ -100,10 +103,12 @@ def merge_sections(sections)
def merge_items(items)
items.each do |item|
parent = @id_to_section[item.parent_id]
raise InvalidMenuDefinition, 'Invalid parent' if parent.nil?

parent.items << item
item.parent = parent
if parent.nil?
@menu << item
else
parent.items << item
item.parent = parent
end
end
end

Expand All @@ -123,7 +128,7 @@ def preprocess_sections
@id_to_section = @menu.index_by(&:id)
# recursively add subsections to the @id_to_section hash
@menu.each do |section|
section.preprocess_sections(@id_to_section)
section.preprocess_sections(@id_to_section) if section.respond_to?(:preprocess_sections)
end
end

Expand Down
39 changes: 39 additions & 0 deletions app/presenters/menu/settings_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Menu
class SettingsLoader < YamlLoader
include Singleton
include Vmdb::Logging

def self.load
instance.load_from_settings
end

def load_from_settings
begin
settings = ::Settings.ui.custom_menu
items = (settings || []).map { |i| create_custom_item(HashWithIndifferentAccess.new(i)) }
rescue StandardError => e
# if we encounter an error while loading the menus, we ignore the whole settings
_log.error("Error loading custom menu from settings: #{e}")
_log.error("Settings were: #{settings}")
return [[], []]
end
[[], items]
end

private

def create_custom_item(item)
# only alow:
# * items,
# * displayed in the iframe,
# * and at the top menu level.
create_custom_menu_item(
item.merge(
'type' => 'items',
'item_type' => 'big_iframe',
'parent' => nil
)
)
end
end
end
22 changes: 18 additions & 4 deletions app/presenters/menu/yaml_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,36 @@ def load_custom_item(file_name)
end
end

# In case `rbac` is a Hash, convert keys to symbols.
# Example: { :feature => 'vm_explorer', :any => true }
#
# Else assume string and return:
# { :feature => rbac }
def parse_rbac_property(rbac)
rbac.kind_of?(Hash) ? rbac.symbolize_keys : { :feature => rbac }
end

def create_custom_menu_item(properties)
rbac = properties['rbac'].each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
item_type = properties.key?('item_type') ? properties['item_type'].to_sym : :default
%w[id name rbac parent].each do |property|
%w[id name rbac].each do |property|
if properties[property].blank?
raise Menu::Manager::InvalidMenuDefinition,
"incomplete definition -- missing #{property}"
end
end

rbac = parse_rbac_property(properties['rbac'])
item_type = properties.fetch('item_type', :default).to_sym

item = Item.new(
properties['id'],
properties['name'],
properties['feature'],
rbac,
properties['href'],
item_type,
properties['parent'].to_sym
properties['parent']&.to_sym,
nil,
properties['icon']
)
item
end
Expand All @@ -53,6 +66,7 @@ def create_custom_menu_section(properties)
before = properties.key?('before') ? properties['before'].to_sym : nil
section_type = properties.key?('section_type') ? properties['section_type'].to_sym : :default
href = properties.key?('href') ? properties['href'].to_sym : nil
# no parent_id here?
Section.new(properties['id'].to_sym, properties['name'], icon, [], placement, before, section_type, href)
end
end
Expand Down
4 changes: 3 additions & 1 deletion app/presenters/tree_builder_ops_rbac_features.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ def initialize(name, sandbox, build, **params)
private

def x_get_tree_roots(count_only = false, _options)
top_nodes = Menu::Manager.items.select { |section| Vmdb::PermissionStores.instance.can?(section.id) }
top_nodes = Menu::Manager.items.select do |section|
Vmdb::PermissionStores.instance.can?(section.id) && !section.kind_of?(Menu::Item)
end

top_nodes += %w[all_vm_rules api_exclusive sui ops_explorer].collect do |additional_feature|
MiqProductFeature.obj_features[additional_feature] &&
Expand Down
10 changes: 9 additions & 1 deletion app/views/layouts/_vertical_navbar.html.haml
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
.nav-pf-vertical.nav-pf-vertical-with-sub-menus.nav-pf-vertical-collapsible-menus
%ul#maintab.list-group
- Menu::Manager.menu do |menu_section|
- if menu_section.visible?
- next unless menu_section.visible?
- if Menu::Item === menu_section
%li.list-group-item{:class => item_nav_class(menu_section), :id => "menu_item_#{menu_section.id}"}
%a{menu_section.link_params}
%span{:class => menu_section.icon}
%span.list-group-item-value
= _(menu_section.name)

- else
%li.list-group-item.secondary-nav-item-pf{"data-target" => "#menu-#{menu_section.id}", :class => section_nav_class(menu_section)}
%a{menu_section.link_params}
%span{:class => menu_section.icon}
Expand Down
2 changes: 2 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:ui:
:custom_menu:
11 changes: 11 additions & 0 deletions spec/presenters/menu/menu_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,15 @@
end
end
end

context "menu" do
it "knows about custom items from settings" do
::Settings.ui.custom_menu = settings_custom_items
count = 0
Menu::Manager.menu do |item|
count += 1 if item.kind_of?(Menu::Item) && item.name =~ /^Custom Item/
end
expect(count).to eq(2)
end
end
end
10 changes: 10 additions & 0 deletions spec/presenters/menu/settings_loader_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
describe Menu::SettingsLoader do
include Spec::Support::MenuHelper
it "loads custom menu items" do
::Settings.ui.custom_menu = settings_custom_items
sections, items = described_class.load

expect(sections.length).to be(0)
expect(items.length).to be(2)
end
end
21 changes: 21 additions & 0 deletions spec/support/menu_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ def section_file
def item_file
create_temp_file(ITEM_DEF)
end

def settings_custom_items
[
{
:type => 'item',
:icon => 'fa fa-bug',
:id => 'custom_i1',
:name => 'Custom Item 1',
:href => 'https://www.redhat.com',
:rbac => 'vm_explorer'
},
{
:type => 'item',
:icon => 'pficon pficon-help',
:id => 'custom_i2',
:name => 'Custom Item 2',
:href => 'https://www.hmpf.cz',
:rbac => 'vm_explorer'
}
]
end
end
end
end

0 comments on commit 5c66951

Please sign in to comment.