Add support for "nested" menu items

some menus were just getting too long, so this gives us a way to collapse
certain items, which user can expand as needed
This commit is contained in:
Lance Edgar 2021-02-01 11:57:12 -06:00
parent 329e75ee82
commit fe80028c07
3 changed files with 139 additions and 38 deletions

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar
# Copyright © 2010-2021 Lance Edgar
#
# This file is part of Rattail.
#
@ -33,6 +33,7 @@ from rattail.util import import_module_path
class MenuGroup(Object):
title = None
items = None
is_menu = True
is_link = False
@ -41,10 +42,19 @@ class MenuItem(Object):
url = None
target = None
is_link = True
is_menu = False
is_sep = False
class MenuItemMenu(Object):
title = None
items = None
is_menu = True
is_sep = False
class MenuSeparator(object):
is_menu = False
is_sep = True
@ -61,55 +71,108 @@ def make_simple_menus(request):
# collect "simple" menus definition, but must refine that somewhat to
# produce our final menus
raw_menus = menus_module.simple_menus(request)
mark_allowed(request, raw_menus)
final_menus = []
for topitem in raw_menus:
if topitem.get('type') == 'link':
final_menus.append(
MenuItem(title=topitem['title'],
url=topitem['url'],
target=topitem.get('target')))
if topitem['allowed']:
else: # assuming 'menu' type
if topitem.get('type') == 'link':
final_menus.append(make_menu_entry(topitem))
# figure out which ones the user has permission to access
allowed = []
for item in topitem['items']:
else: # assuming 'menu' type
if item.get('type') == 'sep':
allowed.append(item)
if item.get('perm'):
if request.has_perm(item['perm']):
allowed.append(item)
else:
allowed.append(item)
if allowed:
# user must have access to something; construct items for the menu
menu_items = []
for item in allowed:
for item in topitem['items']:
if not item['allowed']:
continue
# separator
if item.get('type') == 'sep':
# nested submenu
if item.get('type') == 'menu':
submenu_items = []
for subitem in item['items']:
if subitem['allowed']:
submenu_items.append(make_menu_entry(subitem))
menu_items.append(MenuItemMenu(
title=item['title'],
items=submenu_items))
elif item.get('type') == 'sep':
# we only want to add a sep, *if* we already have some
# menu items (i.e. there is something to separate)
# *and* the last menu item is not a sep (avoid doubles)
if menu_items and not menu_items[-1].is_sep:
menu_items.append(MenuSeparator())
menu_items.append(make_menu_entry(item))
# menu item
else:
menu_items.append(
MenuItem(title=item['title'],
url=item['url'],
target=item.get('target')))
else: # standard menu item
menu_items.append(make_menu_entry(item))
# remove final separator if present
if menu_items and menu_items[-1].is_sep:
menu_items.pop()
# only add if we wound up with something
assert menu_items
if menu_items:
final_menus.append(
MenuGroup(title=topitem['title'], items=menu_items))
final_menus.append(MenuGroup(
title=topitem['title'],
items=menu_items))
return final_menus
def make_menu_entry(item):
"""
Convert a simple menu entry dict, into a proper menu-related object, for
use in constructing final menu.
"""
# separator
if item.get('type') == 'sep':
return MenuSeparator()
# standard menu item
return MenuItem(
title=item['title'],
url=item['url'],
target=item.get('target'))
def is_allowed(request, item):
"""
Logic to determine if a given menu item is "allowed" for current user.
"""
perm = item.get('perm')
if perm:
return request.has_perm(perm)
return True
def mark_allowed(request, menus):
"""
Traverse the menu set, and mark each item as "allowed" (or not) based on
current user permissions.
"""
for topitem in menus:
if topitem.get('type', 'menu') == 'menu':
topitem['allowed'] = False
for item in topitem['items']:
if item.get('type') == 'menu':
for subitem in item['items']:
subitem['allowed'] = is_allowed(request, subitem)
item['allowed'] = False
for subitem in item['items']:
if subitem['allowed'] and subitem.get('type') != 'sep':
item['allowed'] = True
break
else:
item['allowed'] = is_allowed(request, item)
for item in topitem['items']:
if item['allowed'] and item.get('type') != 'sep':
topitem['allowed'] = True
break