menu: Sort menu items for all locales

Currently menu items are shown in alphabetical order in applications and
no clear order in system configuration.  This is done using static
weights for menu items based on English names that does not work for
other locales.

Sorting can't be done at the time of adding menu items as users of
multiple locales may use the interface at the same time.

Implement a sorting mechanism based on existing order as well as labels
of menu item.  This allows the flexiblity of grouping menu items in
future as it may be need for system configuration.  In case of help menu

Remove sort order for all modules except for help menu as here we want
that specific order.
This commit is contained in:
Sunil Mohan Adapa 2016-06-04 12:19:42 +05:30 committed by James Valleroy
parent 6f99580f8a
commit ad61028a3a
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
37 changed files with 52 additions and 46 deletions

View File

@ -56,9 +56,9 @@ class Menu(object):
raise KeyError('Menu item not found')
def sort_items(self):
"""Sort the items in self.items by order."""
self.items = sorted(self.items, key=lambda x: x.order, reverse=False)
def sorted_items(self):
"""Return menu items in sorted order according to current locale."""
return sorted(self.items, key=lambda x: (x.order, x.label))
def add_urlname(self, label, icon, urlname, order=50, url_args=None,
url_kwargs=None):
@ -77,7 +77,6 @@ class Menu(object):
"""
item = Menu(label=label, icon=icon, url=url, order=order)
self.items.append(item)
self.sort_items()
return item
def active_item(self, request):

View File

@ -24,7 +24,7 @@ from plinth import cfg
def init():
"""Initailize the apps module"""
cfg.main_menu.add_urlname(_('Apps'), 'glyphicon-download-alt',
'apps:index', 80)
'apps:index')
def index(request):

View File

@ -55,7 +55,7 @@ service = None
def init():
"""Intialize the service discovery module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-lamp', 'avahi:index', 950)
menu.add_urlname(title, 'glyphicon-lamp', 'avahi:index')
global service # pylint: disable=W0603
service = service_module.Service(

View File

@ -148,7 +148,7 @@ def init():
"""Initialize the module"""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(ugettext_lazy('Configure'), 'glyphicon-cog',
'config:index', 10)
'config:index')
# Register domain with Name Services module.
domainname = get_domainname()

View File

@ -47,7 +47,7 @@ service = None
def init():
"""Intialize the date/time module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-time', 'datetime:index', 900)
menu.add_urlname(title, 'glyphicon-time', 'datetime:index')
global service
service = service_module.Service(

View File

@ -50,7 +50,7 @@ description = [
def init():
"""Initialize the Deluge module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-magnet', 'deluge:index', 200)
menu.add_urlname(title, 'glyphicon-magnet', 'deluge:index')
global service
service = service_module.Service(

View File

@ -42,7 +42,7 @@ depends = ['system']
def init():
"""Initialize the module"""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-screenshot', 'diagnostics:index', 30)
menu.add_urlname(title, 'glyphicon-screenshot', 'diagnostics:index')
def diagnose():

View File

@ -52,7 +52,7 @@ description = [
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-refresh', 'dynamicdns:index', 500)
menu.add_urlname(title, 'glyphicon-refresh', 'dynamicdns:index')
def setup(helper, old_version=None):

View File

@ -50,7 +50,7 @@ LOGGER = logging.getLogger(__name__)
def init():
"""Initailze firewall module"""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-fire', 'firewall:index', 50)
menu.add_urlname(title, 'glyphicon-fire', 'firewall:index')
service_enabled.connect(on_service_enabled)

View File

@ -31,7 +31,7 @@ from plinth import cfg, __version__
def init():
"""Initialize the Help module"""
menu = cfg.main_menu.add_urlname(ugettext_lazy('Documentation'),
'glyphicon-book', 'help:index', 101)
'glyphicon-book', 'help:index')
menu.add_urlname(ugettext_lazy('Where to Get Help'), 'glyphicon-search',
'help:index_explicit', 5)
menu.add_urlname(ugettext_lazy('Manual'), 'glyphicon-info-sign',

View File

@ -44,7 +44,7 @@ description = [
def init():
"""Initialize the ikiwiki module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-edit', 'ikiwiki:index', 1100)
menu.add_urlname(title, 'glyphicon-edit', 'ikiwiki:index')
global service
service = service_module.Service(

View File

@ -59,7 +59,7 @@ def init():
"""Intialize the module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(_('Certificates (Let\'s Encrypt)'),
'glyphicon-lock', 'letsencrypt:index', 20)
'glyphicon-lock', 'letsencrypt:index')
def setup(helper, old_version=None):

View File

@ -51,7 +51,7 @@ description = [
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-th-large', 'minetest:index', 325)
menu.add_urlname(title, 'glyphicon-th-large', 'minetest:index')
global service
service = service_module.Service(

View File

@ -54,7 +54,7 @@ def init():
"""Initialize the monkeysphere module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(_('Monkeysphere'), 'glyphicon-certificate',
'monkeysphere:index', 970)
'monkeysphere:index')
def setup(helper, old_version=None):

View File

@ -50,7 +50,7 @@ description = [
def init():
"""Intialize the Mumble module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-headphones', 'mumble:index', 900)
menu.add_urlname(title, 'glyphicon-headphones', 'mumble:index')
global service
service = service_module.Service(

View File

@ -48,7 +48,7 @@ logger = logging.getLogger(__name__)
def init():
"""Initialize the names module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-tag', 'names:index', 19)
menu.add_urlname(title, 'glyphicon-tag', 'names:index')
domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed)

View File

@ -42,7 +42,7 @@ logger = Logger(__name__)
def init():
"""Initialize the Networks module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-signal', 'networks:index', 18)
menu.add_urlname(title, 'glyphicon-signal', 'networks:index')
def setup(helper, old_version=None):

View File

@ -53,7 +53,7 @@ description = [
def init():
"""Intialize the OpenVPN module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-lock', 'openvpn:index', 850)
menu.add_urlname(title, 'glyphicon-lock', 'openvpn:index')
global service
service = service_module.Service(

View File

@ -60,7 +60,7 @@ def init():
return
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-picture', 'owncloud:index', 700)
menu.add_urlname(title, 'glyphicon-picture', 'owncloud:index')
global service
service = service_module.Service(

View File

@ -34,7 +34,7 @@ title = _('Public Visibility (PageKite)')
def init():
"""Intialize the PageKite module"""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-flag', 'pagekite:index', 800)
menu.add_urlname(title, 'glyphicon-flag', 'pagekite:index')
# Register kite name with Name Services module.
utils.update_names_module(initial_registration=True)

View File

@ -39,4 +39,4 @@ description = [
def init():
"""Initialize the power module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-off', 'power:index', 1000)
menu.add_urlname(title, 'glyphicon-off', 'power:index')

View File

@ -60,7 +60,7 @@ managed_services = ['privoxy']
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-cloud-upload', 'privoxy:index', 1000)
menu.add_urlname(title, 'glyphicon-cloud-upload', 'privoxy:index')
global service
service = service_module.Service(

View File

@ -58,7 +58,7 @@ description = [
def init():
"""Initialize the quassel module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-retweet', 'quassel:index', 730)
menu.add_urlname(title, 'glyphicon-retweet', 'quassel:index')
global service
service = service_module.Service(

View File

@ -53,7 +53,7 @@ description = [
def init():
"""Initialize the radicale module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-calendar', 'radicale:index', 375)
menu.add_urlname(title, 'glyphicon-calendar', 'radicale:index')
global service
service = service_module.Service(

View File

@ -62,7 +62,7 @@ managed_services = ['repro']
def init():
"""Initialize the repro module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-phone-alt', 'repro:index', 825)
menu.add_urlname(title, 'glyphicon-phone-alt', 'repro:index')
global service
service = service_module.Service(

View File

@ -53,7 +53,7 @@ managed_services = ['node-restore']
def init():
"""Initialize the reStore module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-hdd', 'restore:index', 750)
menu.add_urlname(title, 'glyphicon-hdd', 'restore:index')
global service
service = service_module.Service(

View File

@ -61,7 +61,7 @@ service = None
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-envelope', 'roundcube:index', 600)
menu.add_urlname(title, 'glyphicon-envelope', 'roundcube:index')
global service
service = service_module.Service(

View File

@ -48,7 +48,7 @@ service = None
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-bookmark', 'shaarli:index', 350)
menu.add_urlname(title, 'glyphicon-bookmark', 'shaarli:index')
global service
service = service_module.Service(

View File

@ -44,4 +44,4 @@ description = [
def init():
"""Initialize the system module"""
cfg.main_menu.add_urlname(title, 'glyphicon-cog', 'system:index', 100)
cfg.main_menu.add_urlname(title, 'glyphicon-cog', 'system:index')

View File

@ -54,7 +54,7 @@ bridge_service = None
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-eye-close', 'tor:index', 100)
menu.add_urlname(title, 'glyphicon-eye-close', 'tor:index')
global socks_service
socks_service = service_module.Service(

View File

@ -51,7 +51,7 @@ TRANSMISSION_CONFIG = '/etc/transmission-daemon/settings.json'
def init():
"""Intialize the Transmission module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-save', 'transmission:index', 300)
menu.add_urlname(title, 'glyphicon-save', 'transmission:index')
global service
service = service_module.Service(

View File

@ -50,7 +50,7 @@ managed_services = ['tt-rss']
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-envelope', 'ttrss:index', 780)
menu.add_urlname(title, 'glyphicon-envelope', 'ttrss:index')
global service
service = service_module.Service(

View File

@ -46,7 +46,7 @@ service = None
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-refresh', 'upgrades:index', 21)
menu.add_urlname(title, 'glyphicon-refresh', 'upgrades:index')
global service
service = service_module.Service(
'auto-upgrades', title, is_external=False, is_enabled=is_enabled,

View File

@ -38,7 +38,7 @@ title = _('Users and Groups')
def init():
"""Intialize the user module."""
menu = cfg.main_menu.get('system:index')
menu.add_urlname(title, 'glyphicon-user', 'users:index', 15)
menu.add_urlname(title, 'glyphicon-user', 'users:index')
def setup(helper, old_version=None):

View File

@ -58,7 +58,7 @@ managed_services = ['ejabberd']
def init():
"""Initialize the XMPP module"""
menu = cfg.main_menu.get('apps:index')
menu.add_urlname(title, 'glyphicon-comment', 'xmpp:index', 400)
menu.add_urlname(title, 'glyphicon-comment', 'xmpp:index')
global service
service = service_module.Service(

View File

@ -20,17 +20,16 @@
{% load i18n %}
<ul class="nav nav-pills nav-stacked">
{% for item in menu.items %}
{% for item in menu.sorted_items %}
{% if item.url in active_menu_urls %}
<li class="active">
<a href="{{ item.url }}" class="active">
<span class="{{ item.icon }} glyphicon"></span>
{% else %}
<li>
<a href="{{ item.url }}">
<span class="{{ item.icon }} glyphicon"></span>
{% endif %}
<span class="{{ item.icon }} glyphicon"></span>
{{ item.label }}
</a>
</li>

View File

@ -119,13 +119,21 @@ class MenuTestCase(TestCase):
def test_sort_items(self):
"""Verify that menu items are sorted correctly."""
menu = build_menu()
size = 1000
menu = build_menu(size)
for index in range(0, 200):
menu.items[index].order = 100
# Verify that the order of every item is equal to or greater
# than the order of the item preceding it
for index in range(1, 5):
self.assertGreaterEqual(menu.items[index].order,
menu.items[index - 1].order)
# than the order of the item preceding it and if the order is
# the same, the labels are considered.
items = menu.sorted_items()
for index in range(1, size):
self.assertGreaterEqual(items[index].order, items[index - 1].order)
if items[index].order == items[index - 1].order:
self.assertGreaterEqual(items[index].label,
items[index - 1].label)
def test_add_urlname(self):
"""Verify that a named URL can be added to a menu correctly."""