Commit 8809d3d8 authored by Serge S. Koval's avatar Serge S. Koval

Fixed #596. Menu items can now have CSS class(es) associated, as well as glyph and image icons

parent 65503372
...@@ -148,7 +148,8 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -148,7 +148,8 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
return args return args
def __init__(self, name=None, category=None, endpoint=None, url=None, def __init__(self, name=None, category=None, endpoint=None, url=None,
static_folder=None, static_url_path=None): static_folder=None, static_url_path=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -168,9 +169,16 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -168,9 +169,16 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
and '/admin/' prefix won't be applied. and '/admin/' prefix won't be applied.
:param static_url_path: :param static_url_path:
Static URL Path. If provided, this specifies the path to the static url directory. Static URL Path. If provided, this specifies the path to the static url directory.
:param debug: :param menu_class_name:
Optional debug flag. If set to `True`, will rethrow exceptions in some cases, so Werkzeug Optional class name for the menu item.
debugger can catch them. :param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self.name = name self.name = name
self.category = category self.category = category
...@@ -178,6 +186,11 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -178,6 +186,11 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
self.url = url self.url = url
self.static_folder = static_folder self.static_folder = static_folder
self.static_url_path = static_url_path self.static_url_path = static_url_path
self.menu = None
self.menu_class_name = menu_class_name
self.menu_icon_type = menu_icon_type
self.menu_icon_value = menu_icon_value
# Initialized from create_blueprint # Initialized from create_blueprint
self.admin = None self.admin = None
...@@ -483,6 +496,9 @@ class Admin(object): ...@@ -483,6 +496,9 @@ class Admin(object):
def _add_view_to_menu(self, view): def _add_view_to_menu(self, view):
self._add_menu_item(MenuView(view.name, view), view.category) self._add_menu_item(MenuView(view.name, view), view.category)
def get_category_menu_item(self, name):
return self._menu_categories.get(name)
def init_app(self, app): def init_app(self, app):
""" """
Register all views with the Flask application. Register all views with the Flask application.
......
# bootstrap glyph icon
ICON_TYPE_GLYPH = 'glyph'
# image relative to Flask static folder
ICON_TYPE_IMAGE = 'image'
# external image
ICON_TYPE_IMAGE_URL = 'image-url'
...@@ -199,7 +199,8 @@ class ModelView(BaseModelView): ...@@ -199,7 +199,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, name=None, def __init__(self, model, name=None,
category=None, endpoint=None, url=None): category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor Constructor
...@@ -213,10 +214,24 @@ class ModelView(BaseModelView): ...@@ -213,10 +214,24 @@ class ModelView(BaseModelView):
Endpoint Endpoint
:param url: :param url:
Custom URL Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self._search_fields = [] self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
......
...@@ -132,10 +132,14 @@ class ModelView(BaseModelView): ...@@ -132,10 +132,14 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, name=None, def __init__(self, model, name=None,
category=None, endpoint=None, url=None): category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
self._search_fields = [] self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
......
...@@ -39,7 +39,8 @@ class ModelView(BaseModelView): ...@@ -39,7 +39,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, coll, def __init__(self, coll,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor Constructor
...@@ -53,6 +54,16 @@ class ModelView(BaseModelView): ...@@ -53,6 +54,16 @@ class ModelView(BaseModelView):
Endpoint Endpoint
:param url: :param url:
Custom URL Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self._search_fields = [] self._search_fields = []
...@@ -62,7 +73,10 @@ class ModelView(BaseModelView): ...@@ -62,7 +73,10 @@ class ModelView(BaseModelView):
if endpoint is None: if endpoint is None:
endpoint = ('%sview' % coll.name).lower() endpoint = ('%sview' % coll.name).lower()
super(ModelView, self).__init__(None, name, category, endpoint, url) super(ModelView, self).__init__(None, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.coll = coll self.coll = coll
......
...@@ -242,7 +242,8 @@ class ModelView(BaseModelView): ...@@ -242,7 +242,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, session, def __init__(self, model, session,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -258,6 +259,16 @@ class ModelView(BaseModelView): ...@@ -258,6 +259,16 @@ class ModelView(BaseModelView):
Endpoint name. If not set, defaults to the model name Endpoint name. If not set, defaults to the model name
:param url: :param url:
Base URL. If not set, defaults to '/admin/' + endpoint Base URL. If not set, defaults to '/admin/' + endpoint
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self.session = session self.session = session
...@@ -271,7 +282,10 @@ class ModelView(BaseModelView): ...@@ -271,7 +282,10 @@ class ModelView(BaseModelView):
if self.form_choices is None: if self.form_choices is None:
self.form_choices = {} self.form_choices = {}
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
# Primary key # Primary key
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
......
...@@ -5,11 +5,18 @@ class BaseMenu(object): ...@@ -5,11 +5,18 @@ class BaseMenu(object):
""" """
Base menu item Base menu item
""" """
def __init__(self, name): def __init__(self, name, class_name=None, icon_type=None, icon_value=None):
self.name = name self.name = name
self.class_name = class_name
self.icon_type = icon_type
self.icon_value = icon_value
self.parent = None
self._children = [] self._children = []
def add_child(self, menu): def add_child(self, menu):
# TODO: Check if menu item is already assigned to some parent
menu.parent = self
self._children.append(menu) self._children.append(menu)
def get_url(self): def get_url(self):
...@@ -25,6 +32,15 @@ class BaseMenu(object): ...@@ -25,6 +32,15 @@ class BaseMenu(object):
return False return False
def get_class_name(self):
return self.class_name
def get_icon_type(self):
return self.icon_type
def get_icon_value(self):
return self.icon_value
def is_visible(self): def is_visible(self):
return True return True
...@@ -65,11 +81,16 @@ class MenuView(BaseMenu): ...@@ -65,11 +81,16 @@ class MenuView(BaseMenu):
Admin view menu item Admin view menu item
""" """
def __init__(self, name, view=None): def __init__(self, name, view=None):
super(MenuView, self).__init__(name) super(MenuView, self).__init__(name,
class_name=view.menu_class_name,
icon_type=view.menu_icon_type,
icon_value=view.menu_icon_value)
self._view = view self._view = view
self._cached_url = None self._cached_url = None
view.menu = self
def get_url(self): def get_url(self):
if self._view is None: if self._view is None:
return None return None
......
...@@ -484,7 +484,8 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -484,7 +484,8 @@ class BaseModelView(BaseView, ActionsMixin):
""" """
def __init__(self, model, def __init__(self, model,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -500,9 +501,16 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -500,9 +501,16 @@ class BaseModelView(BaseView, ActionsMixin):
'userview' 'userview'
:param url: :param url:
Base URL. If not provided, will use endpoint as a URL. Base URL. If not provided, will use endpoint as a URL.
:param debug: :param menu_class_name:
Enable debugging mode. Won't catch exceptions on model Optional class name for the menu item.
save failures. :param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
# If name not provided, it is model name # If name not provided, it is model name
...@@ -513,7 +521,10 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -513,7 +521,10 @@ class BaseModelView(BaseView, ActionsMixin):
if endpoint is None: if endpoint is None:
endpoint = model.__name__.lower() endpoint = model.__name__.lower()
super(BaseModelView, self).__init__(name, category, endpoint, url) super(BaseModelView, self).__init__(name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.model = model self.model = model
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="{{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %} {% macro menu() %}
{% for item in admin_view.admin.menu() %} {% for item in admin_view.admin.menu() %}
{% if item.is_category() %} {% if item.is_category() %}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{% if children %} {% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %} {% set class_name = item.get_class_name() %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a> {% if item.is_active(admin_view) %}
<li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b>
</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% for child in children %} {% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = child.get_class_name() %}
<a href="{{ child.get_url() }}">{{ child.name }}</a> {% if child.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">{{ menu_icon(item) }}{{ child.name }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
...@@ -16,8 +42,13 @@ ...@@ -16,8 +42,13 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = item.get_class_name() %}
<a href="{{ item.get_url() }}">{{ item.name }}</a> {% if item.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="glyphicon {{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %} {% macro menu() %}
{% for item in admin_view.admin.menu() %} {% for item in admin_view.admin.menu() %}
{% if item.is_category() %} {% if item.is_category() %}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{% if children %} {% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %} {% if item.is_active(admin_view) %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a> <li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">aaas{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% for child in children %} {% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = child.get_class_name() %}
<a href="{{ child.get_url() }}">{{ child.name }}</a> {% if child.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">asasdasd{{ menu_icon(item) }}{{ child.name }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
...@@ -16,8 +39,13 @@ ...@@ -16,8 +39,13 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = item.get_class_name() %}
<a href="{{ item.get_url() }}">{{ item.name }}</a> {% if item.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
......
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