| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 | 
							- import os.path as op
 
- import warnings
 
- from functools import wraps
 
- from flask import Blueprint, current_app, render_template, abort, g, url_for
 
- from flask_admin import babel
 
- from flask_admin._compat import with_metaclass, as_unicode
 
- from flask_admin import helpers as h
 
- # For compatibility reasons import MenuLink
 
- from flask_admin.menu import MenuCategory, MenuView, MenuLink  # noqa: F401
 
- def expose(url='/', methods=('GET',)):
 
-     """
 
-         Use this decorator to expose views in your view classes.
 
-         :param url:
 
-             Relative URL for the view
 
-         :param methods:
 
-             Allowed HTTP methods. By default only GET is allowed.
 
-     """
 
-     def wrap(f):
 
-         if not hasattr(f, '_urls'):
 
-             f._urls = []
 
-         f._urls.append((url, methods))
 
-         return f
 
-     return wrap
 
- def expose_plugview(url='/'):
 
-     """
 
-         Decorator to expose Flask's pluggable view classes
 
-         (``flask.views.View`` or ``flask.views.MethodView``).
 
-         :param url:
 
-             Relative URL for the view
 
-         .. versionadded:: 1.0.4
 
-     """
 
-     def wrap(v):
 
-         handler = expose(url, v.methods)
 
-         if hasattr(v, 'as_view'):
 
-             return handler(v.as_view(v.__name__))
 
-         else:
 
-             return handler(v)
 
-     return wrap
 
- # Base views
 
- def _wrap_view(f):
 
-     # Avoid wrapping view method twice
 
-     if hasattr(f, '_wrapped'):
 
-         return f
 
-     @wraps(f)
 
-     def inner(self, *args, **kwargs):
 
-         # Store current admin view
 
-         h.set_current_view(self)
 
-         # Check if administrative piece is accessible
 
-         abort = self._handle_view(f.__name__, **kwargs)
 
-         if abort is not None:
 
-             return abort
 
-         return self._run_view(f, *args, **kwargs)
 
-     inner._wrapped = True
 
-     return inner
 
- class AdminViewMeta(type):
 
-     """
 
-         View metaclass.
 
-         Does some precalculations (like getting list of view methods from the class) to avoid
 
-         calculating them for each view class instance.
 
-     """
 
-     def __init__(cls, classname, bases, fields):
 
-         type.__init__(cls, classname, bases, fields)
 
-         # Gather exposed views
 
-         cls._urls = []
 
-         cls._default_view = None
 
-         for p in dir(cls):
 
-             attr = getattr(cls, p)
 
-             if hasattr(attr, '_urls'):
 
-                 # Collect methods
 
-                 for url, methods in attr._urls:
 
-                     cls._urls.append((url, p, methods))
 
-                     if url == '/':
 
-                         cls._default_view = p
 
-                 # Wrap views
 
-                 setattr(cls, p, _wrap_view(attr))
 
- class BaseViewClass(object):
 
-     pass
 
- class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
 
-     """
 
-         Base administrative view.
 
-         Derive from this class to implement your administrative interface piece. For example::
 
-             from flask_admin import BaseView, expose
 
-             class MyView(BaseView):
 
-                 @expose('/')
 
-                 def index(self):
 
-                     return 'Hello World!'
 
-         Icons can be added to the menu by using `menu_icon_type` and `menu_icon_value`. For example::
 
-             admin.add_view(MyView(name='My View', menu_icon_type='glyph', menu_icon_value='glyphicon-home'))
 
-     """
 
-     @property
 
-     def _template_args(self):
 
-         """
 
-             Extra template arguments.
 
-             If you need to pass some extra parameters to the template,
 
-             you can override particular view function, contribute
 
-             arguments you want to pass to the template and call parent view.
 
-             These arguments are local for this request and will be discarded
 
-             in the next request.
 
-             Any value passed through ``_template_args`` will override whatever
 
-             parent view function passed to the template.
 
-             For example::
 
-                 class MyAdmin(ModelView):
 
-                     @expose('/')
 
-                     def index(self):
 
-                         self._template_args['name'] = 'foobar'
 
-                         self._template_args['code'] = '12345'
 
-                         super(MyAdmin, self).index()
 
-         """
 
-         args = getattr(g, '_admin_template_args', None)
 
-         if args is None:
 
-             args = g._admin_template_args = dict()
 
-         return args
 
-     def __init__(self, name=None, category=None, endpoint=None, url=None,
 
-                  static_folder=None, static_url_path=None,
 
-                  menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
 
-         """
 
-             Constructor.
 
-             :param name:
 
-                 Name of this view. If not provided, will default to the class name.
 
-             :param category:
 
-                 View category. If not provided, this view will be shown as a top-level menu item. Otherwise, it will
 
-                 be in a submenu.
 
-             :param endpoint:
 
-                 Base endpoint name for the view. For example, if there's a view method called "index" and
 
-                 endpoint is set to "myadmin", you can use `url_for('myadmin.index')` to get the URL to the
 
-                 view method. Defaults to the class name in lower case.
 
-             :param url:
 
-                 Base URL. If provided, affects how URLs are generated. For example, if the url parameter
 
-                 is "test", the resulting URL will look like "/admin/test/". If not provided, will
 
-                 use endpoint as a base url. However, if URL starts with '/', absolute path is assumed
 
-                 and '/admin/' prefix won't be applied.
 
-             :param static_url_path:
 
-                 Static URL Path. If provided, this specifies the path to the static url directory.
 
-             :param menu_class_name:
 
-                 Optional class name for the menu item.
 
-             :param menu_icon_type:
 
-                 Optional icon. Possible icon types:
 
-                  - `flask_admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
 
-                  - `flask_admin.consts.ICON_TYPE_FONT_AWESOME` - Font Awesome icon
 
-                  - `flask_admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
 
-                  - `flask_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.category = category
 
-         self.endpoint = self._get_endpoint(endpoint)
 
-         self.url = url
 
-         self.static_folder = static_folder
 
-         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
 
-         self.admin = None
 
-         self.blueprint = None
 
-         # Default view
 
-         if self._default_view is None:
 
-             raise Exception(u'Attempted to instantiate admin view %s without default view' % self.__class__.__name__)
 
-     def _get_endpoint(self, endpoint):
 
-         """
 
-             Generate Flask endpoint name. By default converts class name to lower case if endpoint is
 
-             not explicitly provided.
 
-         """
 
-         if endpoint:
 
-             return endpoint
 
-         return self.__class__.__name__.lower()
 
-     def _get_view_url(self, admin, url):
 
-         """
 
-             Generate URL for the view. Override to change default behavior.
 
-         """
 
-         if url is None:
 
-             if admin.url != '/':
 
-                 url = '%s/%s' % (admin.url, self.endpoint)
 
-             else:
 
-                 if self == admin.index_view:
 
-                     url = '/'
 
-                 else:
 
-                     url = '/%s' % self.endpoint
 
-         else:
 
-             if not url.startswith('/'):
 
-                 url = '%s/%s' % (admin.url, url)
 
-         return url
 
-     def create_blueprint(self, admin):
 
-         """
 
-             Create Flask blueprint.
 
-         """
 
-         # Store admin instance
 
-         self.admin = admin
 
-         # If the static_url_path is not provided, use the admin's
 
-         if not self.static_url_path:
 
-             self.static_url_path = admin.static_url_path
 
-         # Generate URL
 
-         self.url = self._get_view_url(admin, self.url)
 
-         # If we're working from the root of the site, set prefix to None
 
-         if self.url == '/':
 
-             self.url = None
 
-             # prevent admin static files from conflicting with flask static files
 
-             if not self.static_url_path:
 
-                 self.static_folder = 'static'
 
-                 self.static_url_path = '/static/admin'
 
-         # If name is not povided, use capitalized endpoint name
 
-         if self.name is None:
 
-             self.name = self._prettify_class_name(self.__class__.__name__)
 
-         # Create blueprint and register rules
 
-         self.blueprint = Blueprint(self.endpoint, __name__,
 
-                                    url_prefix=self.url,
 
-                                    subdomain=self.admin.subdomain,
 
-                                    template_folder=op.join('templates', self.admin.template_mode),
 
-                                    static_folder=self.static_folder,
 
-                                    static_url_path=self.static_url_path)
 
-         for url, name, methods in self._urls:
 
-             self.blueprint.add_url_rule(url,
 
-                                         name,
 
-                                         getattr(self, name),
 
-                                         methods=methods)
 
-         return self.blueprint
 
-     def render(self, template, **kwargs):
 
-         """
 
-             Render template
 
-             :param template:
 
-                 Template path to render
 
-             :param kwargs:
 
-                 Template arguments
 
-         """
 
-         # Store self as admin_view
 
-         kwargs['admin_view'] = self
 
-         kwargs['admin_base_template'] = self.admin.base_template
 
-         # Provide i18n support even if flask-babel is not installed
 
-         # or enabled.
 
-         kwargs['_gettext'] = babel.gettext
 
-         kwargs['_ngettext'] = babel.ngettext
 
-         kwargs['h'] = h
 
-         # Expose get_url helper
 
-         kwargs['get_url'] = self.get_url
 
-         # Expose config info
 
-         kwargs['config'] = current_app.config
 
-         # Contribute extra arguments
 
-         kwargs.update(self._template_args)
 
-         return render_template(template, **kwargs)
 
-     def _prettify_class_name(self, name):
 
-         """
 
-             Split words in PascalCase string into separate words.
 
-             :param name:
 
-                 String to prettify
 
-         """
 
-         return h.prettify_class_name(name)
 
-     def is_visible(self):
 
-         """
 
-             Override this method if you want dynamically hide or show administrative views
 
-             from Flask-Admin menu structure
 
-             By default, item is visible in menu.
 
-             Please note that item should be both visible and accessible to be displayed in menu.
 
-         """
 
-         return True
 
-     def is_accessible(self):
 
-         """
 
-             Override this method to add permission checks.
 
-             Flask-Admin does not make any assumptions about the authentication system used in your application, so it is
 
-             up to you to implement it.
 
-             By default, it will allow access for everyone.
 
-         """
 
-         return True
 
-     def _handle_view(self, name, **kwargs):
 
-         """
 
-             This method will be executed before calling any view method.
 
-             It will execute the ``inaccessible_callback`` if the view is not
 
-             accessible.
 
-             :param name:
 
-                 View function name
 
-             :param kwargs:
 
-                 View function arguments
 
-         """
 
-         if not self.is_accessible():
 
-             return self.inaccessible_callback(name, **kwargs)
 
-     def _run_view(self, fn, *args, **kwargs):
 
-         """
 
-             This method will run actual view function.
 
-             While it is similar to _handle_view, can be used to change
 
-             arguments that are passed to the view.
 
-             :param fn:
 
-                 View function
 
-             :param kwargs:
 
-                 Arguments
 
-         """
 
-         return fn(self, *args, **kwargs)
 
-     def inaccessible_callback(self, name, **kwargs):
 
-         """
 
-             Handle the response to inaccessible views.
 
-             By default, it throw HTTP 403 error. Override this method to
 
-             customize the behaviour.
 
-         """
 
-         return abort(403)
 
-     def get_url(self, endpoint, **kwargs):
 
-         """
 
-             Generate URL for the endpoint. If you want to customize URL generation
 
-             logic (persist some query string argument, for example), this is
 
-             right place to do it.
 
-             :param endpoint:
 
-                 Flask endpoint name
 
-             :param kwargs:
 
-                 Arguments for `url_for`
 
-         """
 
-         return url_for(endpoint, **kwargs)
 
-     @property
 
-     def _debug(self):
 
-         if not self.admin or not self.admin.app:
 
-             return False
 
-         return self.admin.app.debug
 
- class AdminIndexView(BaseView):
 
-     """
 
-         Default administrative interface index page when visiting the ``/admin/`` URL.
 
-         It can be overridden by passing your own view class to the ``Admin`` constructor::
 
-             class MyHomeView(AdminIndexView):
 
-                 @expose('/')
 
-                 def index(self):
 
-                     arg1 = 'Hello'
 
-                     return self.render('admin/myhome.html', arg1=arg1)
 
-             admin = Admin(index_view=MyHomeView())
 
-         Also, you can change the root url from /admin to / with the following::
 
-             admin = Admin(
 
-                 app,
 
-                 index_view=AdminIndexView(
 
-                     name='Home',
 
-                     template='admin/myhome.html',
 
-                     url='/'
 
-                 )
 
-             )
 
-         Default values for the index page are:
 
-         * If a name is not provided, 'Home' will be used.
 
-         * If an endpoint is not provided, will default to ``admin``
 
-         * Default URL route is ``/admin``.
 
-         * Automatically associates with static folder.
 
-         * Default template is ``admin/index.html``
 
-     """
 
-     def __init__(self, name=None, category=None,
 
-                  endpoint=None, url=None,
 
-                  template='admin/index.html',
 
-                  menu_class_name=None,
 
-                  menu_icon_type=None,
 
-                  menu_icon_value=None):
 
-         super(AdminIndexView, self).__init__(name or babel.lazy_gettext('Home'),
 
-                                              category,
 
-                                              endpoint or 'admin',
 
-                                              '/admin' if url is None else url,
 
-                                              'static',
 
-                                              menu_class_name=menu_class_name,
 
-                                              menu_icon_type=menu_icon_type,
 
-                                              menu_icon_value=menu_icon_value)
 
-         self._template = template
 
-     @expose()
 
-     def index(self):
 
-         return self.render(self._template)
 
- class Admin(object):
 
-     """
 
-         Collection of the admin views. Also manages menu structure.
 
-     """
 
-     def __init__(self, app=None, name=None,
 
-                  url=None, subdomain=None,
 
-                  index_view=None,
 
-                  translations_path=None,
 
-                  endpoint=None,
 
-                  static_url_path=None,
 
-                  base_template=None,
 
-                  template_mode=None,
 
-                  category_icon_classes=None):
 
-         """
 
-             Constructor.
 
-             :param app:
 
-                 Flask application object
 
-             :param name:
 
-                 Application name. Will be displayed in the main menu and as a page title. Defaults to "Admin"
 
-             :param url:
 
-                 Base URL
 
-             :param subdomain:
 
-                 Subdomain to use
 
-             :param index_view:
 
-                 Home page view to use. Defaults to `AdminIndexView`.
 
-             :param translations_path:
 
-                 Location of the translation message catalogs. By default will use the translations
 
-                 shipped with Flask-Admin.
 
-             :param endpoint:
 
-                 Base endpoint name for index view. If you use multiple instances of the `Admin` class with
 
-                 a single Flask application, you have to set a unique endpoint name for each instance.
 
-             :param static_url_path:
 
-                 Static URL Path. If provided, this specifies the default path to the static url directory for
 
-                 all its views. Can be overridden in view configuration.
 
-             :param base_template:
 
-                 Override base HTML template for all static views. Defaults to `admin/base.html`.
 
-             :param template_mode:
 
-                 Base template path. Defaults to `bootstrap2`. If you want to use
 
-                 Bootstrap 3 integration, change it to `bootstrap3`.
 
-             :param category_icon_classes:
 
-                 A dict of category names as keys and html classes as values to be added to menu category icons.
 
-                 Example: {'Favorites': 'glyphicon glyphicon-star'}
 
-         """
 
-         self.app = app
 
-         self.translations_path = translations_path
 
-         self._views = []
 
-         self._menu = []
 
-         self._menu_categories = dict()
 
-         self._menu_links = []
 
-         if name is None:
 
-             name = 'Admin'
 
-         self.name = name
 
-         self.index_view = index_view or AdminIndexView(endpoint=endpoint, url=url)
 
-         self.endpoint = endpoint or self.index_view.endpoint
 
-         self.url = url or self.index_view.url
 
-         self.static_url_path = static_url_path
 
-         self.subdomain = subdomain
 
-         self.base_template = base_template or 'admin/base.html'
 
-         self.template_mode = template_mode or 'bootstrap2'
 
-         self.category_icon_classes = category_icon_classes or dict()
 
-         # Add index view
 
-         self._set_admin_index_view(index_view=index_view, endpoint=endpoint, url=url)
 
-         # Register with application
 
-         if app is not None:
 
-             self._init_extension()
 
-     def add_view(self, view):
 
-         """
 
-             Add a view to the collection.
 
-             :param view:
 
-                 View to add.
 
-         """
 
-         # Add to views
 
-         self._views.append(view)
 
-         # 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 _set_admin_index_view(self, index_view=None,
 
-                               endpoint=None, url=None):
 
-         """
 
-             Add the admin index view.
 
-           :param index_view:
 
-                Home page view to use. Defaults to `AdminIndexView`.
 
-            :param url:
 
-                Base URL
 
-           :param endpoint:
 
-                Base endpoint name for index view. If you use multiple instances of the `Admin` class with
 
-                a single Flask application, you have to set a unique endpoint name for each instance.
 
-         """
 
-         self.index_view = index_view or AdminIndexView(endpoint=endpoint, url=url)
 
-         self.endpoint = endpoint or self.index_view.endpoint
 
-         self.url = url or self.index_view.url
 
-         # Add predefined index view
 
-         # assume index view is always the first element of views.
 
-         if len(self._views) > 0:
 
-             self._views[0] = self.index_view
 
-         else:
 
-             self.add_view(self.index_view)
 
-     def add_views(self, *args):
 
-         """
 
-             Add one or more views to the collection.
 
-             Examples::
 
-                 admin.add_views(view1)
 
-                 admin.add_views(view1, view2, view3, view4)
 
-                 admin.add_views(*my_list)
 
-             :param args:
 
-                 Argument list including the views to add.
 
-         """
 
-         for view in args:
 
-             self.add_view(view)
 
-     def add_link(self, link):
 
-         """
 
-             Add link to menu links collection.
 
-             :param link:
 
-                 Link to add.
 
-         """
 
-         if link.category:
 
-             self.add_menu_item(link, link.category)
 
-         else:
 
-             self._menu_links.append(link)
 
-     def add_links(self, *args):
 
-         """
 
-             Add one or more links to the menu links collection.
 
-             Examples::
 
-                 admin.add_links(link1)
 
-                 admin.add_links(link1, link2, link3, link4)
 
-                 admin.add_links(*my_list)
 
-             :param args:
 
-                 Argument list including the links to add.
 
-         """
 
-         for link in args:
 
-             self.add_link(link)
 
-     def add_menu_item(self, menu_item, target_category=None):
 
-         """
 
-             Add menu item to menu tree hierarchy.
 
-             :param menu_item:
 
-                 MenuItem class instance
 
-             :param target_category:
 
-                 Target category name
 
-         """
 
-         if target_category:
 
-             cat_text = as_unicode(target_category)
 
-             category = self._menu_categories.get(cat_text)
 
-             # create a new menu category if one does not exist already
 
-             if category is None:
 
-                 category = MenuCategory(target_category)
 
-                 category.class_name = self.category_icon_classes.get(cat_text)
 
-                 self._menu_categories[cat_text] = category
 
-                 self._menu.append(category)
 
-             category.add_child(menu_item)
 
-         else:
 
-             self._menu.append(menu_item)
 
-     def _add_menu_item(self, menu_item, target_category):
 
-         warnings.warn('Admin._add_menu_item is obsolete - use Admin.add_menu_item instead.')
 
-         return self.add_menu_item(menu_item, target_category)
 
-     def _add_view_to_menu(self, view):
 
-         """
 
-             Add a view to the menu tree
 
-             :param view:
 
-                 View to add
 
-         """
 
-         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, index_view=None,
 
-                  endpoint=None, url=None):
 
-         """
 
-             Register all views with the Flask application.
 
-             :param app:
 
-                 Flask application instance
 
-         """
 
-         self.app = app
 
-         self._init_extension()
 
-         # Register Index view
 
-         if index_view is not None:
 
-             self._set_admin_index_view(
 
-                 index_view=index_view,
 
-                 endpoint=endpoint,
 
-                 url=url
 
-             )
 
-         # Register views
 
-         for view in self._views:
 
-             app.register_blueprint(view.create_blueprint(self))
 
-     def _init_extension(self):
 
-         if not hasattr(self.app, 'extensions'):
 
-             self.app.extensions = dict()
 
-         admins = self.app.extensions.get('admin', [])
 
-         for p in admins:
 
-             if p.endpoint == self.endpoint:
 
-                 raise Exception(u'Cannot have two Admin() instances with same'
 
-                                 u' endpoint name.')
 
-             if p.url == self.url and p.subdomain == self.subdomain:
 
-                 raise Exception(u'Cannot assign two Admin() instances with same'
 
-                                 u' URL and subdomain to the same application.')
 
-         admins.append(self)
 
-         self.app.extensions['admin'] = admins
 
-     def menu(self):
 
-         """
 
-             Return the menu hierarchy.
 
-         """
 
-         return self._menu
 
-     def menu_links(self):
 
-         """
 
-             Return menu links.
 
-         """
 
-         return self._menu_links
 
 
  |