Commit ac8392b8 authored by Serge S. Koval's avatar Serge S. Koval

Fixed #416. More flexible menu system

parent 4466d365
......@@ -84,6 +84,10 @@ if __name__ == '__main__':
admin.add_link(NotAuthenticatedMenuLink(name='Login',
endpoint='login_view'))
# Add links with categories
admin.add_link(MenuLink(name='Google', category='Links', url='http://www.google.com/'))
admin.add_link(MenuLink(name='Mozilla', category='Links', url='http://mozilla.org/'))
# Add logout link by endpoint
admin.add_link(AuthenticatedMenuLink(name='Logout',
endpoint='logout_view'))
......
from functools import wraps
from flask import Blueprint, render_template, url_for, abort, g
from flask import Blueprint, render_template, abort, g
from flask.ext.admin import babel
from flask.ext.admin._compat import with_metaclass
from flask.ext.admin import helpers as h
# For compatibility reasons import MenuLink
from flask.ext.admin.menu import MenuCategory, MenuView, MenuLink
def expose(url='/', methods=('GET',)):
"""
......@@ -343,79 +346,6 @@ class AdminIndexView(BaseView):
return self.render(self._template)
class MenuItem(object):
"""
Simple menu tree hierarchy.
"""
def __init__(self, name, view=None):
self.name = name
self._view = view
self._children = []
self._children_urls = set()
self._cached_url = None
self.url = None
if view is not None:
self.url = view.url
def add_child(self, view):
self._children.append(view)
self._children_urls.add(view.url)
def get_url(self):
if self._view is None:
return None
if self._cached_url:
return self._cached_url
self._cached_url = url_for('%s.%s' % (self._view.endpoint, self._view._default_view))
return self._cached_url
def is_active(self, view):
if view == self._view:
return True
return view.url in self._children_urls
def is_visible(self):
if self._view is None:
return False
return self._view.is_visible()
def is_accessible(self):
if self._view is None:
return False
return self._view.is_accessible()
def is_category(self):
return self._view is None
def get_children(self):
return [c for c in self._children if c.is_accessible() and c.is_visible()]
class MenuLink(object):
"""
Additional menu links.
"""
def __init__(self, name, url=None, endpoint=None):
self.name = name
self.url = url
self.endpoint = endpoint
def get_url(self):
return self.url or url_for(self.endpoint)
def is_visible(self):
return True
def is_accessible(self):
return True
class Admin(object):
"""
Collection of the admin views. Also manages menu structure.
......@@ -492,6 +422,7 @@ class Admin(object):
# If app was provided in constructor, register view with Flask app
if self.app is not None:
self.app.register_blueprint(view.create_blueprint(self))
self._add_view_to_menu(view)
def add_link(self, link):
......@@ -501,26 +432,33 @@ class Admin(object):
:param link:
Link to add.
"""
if link.category:
self._add_menu_item(link, link.category)
else:
self._menu_links.append(link)
def _add_view_to_menu(self, view):
def _add_menu_item(self, menu_item, target_category):
"""
Add a view to the menu tree
:param view:
View to add
"""
if view.category:
category = self._menu_categories.get(view.category)
if target_category:
category = self._menu_categories.get(target_category)
if category is None:
category = MenuItem(view.category)
self._menu_categories[view.category] = category
category = MenuCategory(target_category)
self._menu_categories[target_category] = category
self._menu.append(category)
category.add_child(MenuItem(view.name, view))
category.add_child(menu_item)
else:
self._menu.append(MenuItem(view.name, view))
self._menu.append(menu_item)
def _add_view_to_menu(self, view):
self._add_menu_item(MenuView(view.name, view), view.category)
def init_app(self, app):
"""
......@@ -536,7 +474,6 @@ class Admin(object):
# Register views
for view in self._views:
app.register_blueprint(view.create_blueprint(self))
self._add_view_to_menu(view)
def _init_extension(self):
if not hasattr(self.app, 'extensions'):
......
from flask import url_for
class BaseMenu(object):
"""
Base menu item
"""
def __init__(self, name):
self.name = name
self._children = []
def add_child(self, menu):
self._children.append(menu)
def get_url(self):
raise NotImplemented()
def is_category(self):
return False
def is_active(self, view):
for c in self._children:
if c.is_active(view):
return True
return False
def is_visible(self):
return True
def is_accessible(self):
return True
def get_children(self):
return [c for c in self._children if c.is_accessible() and c.is_visible()]
class MenuCategory(BaseMenu):
"""
Menu category item.
"""
def get_url(self):
return None
def is_category(self):
return True
def is_visible(self):
for c in self._children:
if c.is_visible():
return True
return False
def is_accessible(self):
for c in self._children:
if c.is_accessible():
return True
return False
class MenuView(BaseMenu):
"""
Admin view menu item
"""
def __init__(self, name, view=None):
super(MenuView, self).__init__(name)
self._view = view
self._cached_url = None
def get_url(self):
if self._view is None:
return None
if self._cached_url:
return self._cached_url
self._cached_url = url_for('%s.%s' % (self._view.endpoint, self._view._default_view))
return self._cached_url
def is_active(self, view):
if view == self._view:
return True
return super(MenuView, self).is_active(view)
def is_visible(self):
if self._view is None:
return False
return self._view.is_visible()
def is_accessible(self):
if self._view is None:
return False
return self._view.is_accessible()
class MenuLink(BaseMenu):
"""
Link item
"""
def __init__(self, name, url=None, endpoint=None, category=None):
super(MenuLink, self).__init__(name)
self.category = category
self.url = url
self.endpoint = endpoint
def get_url(self):
return self.url or url_for(self.endpoint)
......@@ -262,11 +262,16 @@ def test_submenu():
eq_(admin._menu[1].name, 'Test')
eq_(len(admin._menu[1]._children), 2)
# Categories don't have URLs and they're not accessible
# Categories don't have URLs
eq_(admin._menu[1].get_url(), None)
eq_(admin._menu[1].is_accessible(), False)
eq_(len(admin._menu[1].get_children()), 1)
# Categories are only accessible if there is at least one accessible child
eq_(admin._menu[1].is_accessible(), True)
children = admin._menu[1].get_children()
eq_(len(children), 1)
ok_(children[0].is_accessible())
def test_delayed_init():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment