diff --git a/lib/hawat/app.py b/lib/hawat/app.py index f527dbe1534eb21eee24bb2f01772ef4e2e93885..dff20bd4ae012dff1957b44e52dc305e21a20c8c 100644 --- a/lib/hawat/app.py +++ b/lib/hawat/app.py @@ -9,7 +9,12 @@ """ -This module contains core application features for Hawat. +This module contains core application features for Hawat, the official user web +interface for the Mentat system. + +The most important feture of this module is the :py:func:`hawat.app.create_app` +factory method, that is responsible for bootstrapping the whole application (see +its documentation for more details). """ @@ -47,6 +52,7 @@ from hawat.models.user import GuiUserModel RE_COUNTRY_CODE = re.compile('^[a-zA-Z]{2,3}$') +"""Regular expression for validating language/country codes.""" #------------------------------------------------------------------------------- @@ -127,36 +133,37 @@ def _setup_app_core(app): """Flask error handler to be called to service HTTP 500 error.""" return flask.render_template('errors/e500.html', error_obj = err), 500 + @app.before_request + def before_request(): # pylint: disable=locally-disabled,unused-variable + """ + Use Flask`s :py:func:`flask.Flask.before_request` hook for performing + various usefull tasks before each request. + """ + flask.g.requeststart = datetime.datetime.utcnow() + @app.context_processor - def inject_version(): # pylint: disable=locally-disabled,unused-variable + def jinja_inject_variables(): # pylint: disable=locally-disabled,unused-variable """ - Inject additional variables into Jinja2 templates. + Inject additional variables into Jinja2 global template namespace. """ return dict( + hawat_version = mentat.__version__, + hawat_bversion = mentat._buildmeta.__bversion__, # pylint: disable=locally-disabled,protected-access + hawat_bversion_full = mentat._buildmeta.__bversion_full__, # pylint: disable=locally-disabled,protected-access hawat_current_menu_main = flask.current_app.menu_main, hawat_current_menu_auth = flask.current_app.menu_auth, hawat_current_menu_anon = flask.current_app.menu_anon, - hawat_version = mentat.__version__, - hawat_bversion = mentat._buildmeta.__bversion__, # pylint: disable=locally-disabled,protected-access - hawat_bversion_full = mentat._buildmeta.__bversion_full__ # pylint: disable=locally-disabled,protected-access + hawat_current_view = app.get_endpoint_class(flask.request.endpoint, True) ) - @app.before_request - def before_request(): # pylint: disable=locally-disabled,unused-variable - """ - Use Flask`s :py:func:`flask.Flask.before_request` hook for performing - various usefull tasks before each request. - """ - flask.g.requeststart = datetime.datetime.utcnow() - @app.context_processor - def utility_processor(): # pylint: disable=locally-disabled,unused-variable + def jinja2_inject_functions(): # pylint: disable=locally-disabled,unused-variable """ - Register additional helpers into Jinja global namespace. This function - will install following helpers: + Register additional helpers into Jinja2 global template namespace. This + function will install following helpers: - get_fa_icon - Reference for :py:func:`hawat.app.get_fa_icon` + get_icon + Reference for :py:func:`hawat.app.get_icon` get_datetime_utc Reference for :py:func:`hawat.app.get_datetime_utc` @@ -177,7 +184,7 @@ def _setup_app_core(app): :param str endpoint: Name of the view endpoint. """ - return app.view_classes[endpoint] + return app.get_endpoint_class(endpoint) def check_endpoint_exists(endpoint): """ @@ -190,7 +197,7 @@ def _setup_app_core(app): """ return endpoint in app.view_classes - def get_fa_icon(icon_name, default_icon = 'missing-icon'): + def get_icon(icon_name, default_icon = 'missing-icon'): """ Get HTML icon markup for given icon. The icon will be looked up in the :py:const:`hawat.const.FA_ICONS` lookup table. @@ -240,7 +247,7 @@ def _setup_app_core(app): :rtype: flask.Markup """ if not RE_COUNTRY_CODE.match(country): - return get_fa_icon('flag') + return get_icon('flag') return flask.Markup( '<img src="{}">'.format( @@ -301,6 +308,18 @@ def _setup_app_core(app): """ return os.path.isfile(filename) + def in_query_params(haystack, needles, on_true = True, on_false = False, on_empty = False): + """ + Utility method for checking that any needle from given list of needles is + present in given haystack. + """ + if not haystack: + return on_empty + for needle in needles: + if needle in haystack: + return on_true + return on_false + def generate_query_params(baseparams, updates): """ Generate query parameters for GET method form. @@ -319,7 +338,7 @@ def _setup_app_core(app): get_endpoint_class = get_endpoint_class, check_endpoint_exists = check_endpoint_exists, - get_fa_icon = get_fa_icon, + get_icon = get_icon, get_module_icon = get_module_icon, get_endpoint_icon = get_endpoint_icon, get_country_flag = get_country_flag, @@ -332,6 +351,7 @@ def _setup_app_core(app): check_file_exists = check_file_exists, + in_query_params = in_query_params, generate_query_params = generate_query_params, current_datetime_utc = datetime.datetime.utcnow() diff --git a/lib/hawat/base.py b/lib/hawat/base.py index 3595be6c4cabfe7442ca35abb688bae01efa4c24..dba87584bd747e239d5001bd637980d152ba8325 100644 --- a/lib/hawat/base.py +++ b/lib/hawat/base.py @@ -22,6 +22,8 @@ Module contents * :py:class:`HawatApp` * :py:class:`HawatBlueprint` +* :py:class:`HTMLViewMixin` +* :py:class:`AJAXViewMixin` * :py:class:`HawatBaseView` * :py:class:`HawatFileNameView` @@ -50,14 +52,17 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" +import re import sys import collections +import urllib.parse import weakref import traceback # # Flask related modules. # +import werkzeug.routing import werkzeug.utils import flask import flask.views @@ -70,7 +75,7 @@ from flask_babel import gettext import hawat.const import hawat.menu import hawat.db -from hawat.forms import ItemActionConfirmForm +from hawat.forms import get_redirect_target, ItemActionConfirmForm from mentat.datatype.sqldb import ItemChangeLogModel @@ -170,7 +175,7 @@ class HawatApp(flask.Flask): """ self.logger.error('%s%s', label, ''.join(tbexc.format())) # pylint: disable=locally-disabled,no-member - def has_view_class(self, endpoint): + def has_endpoint(self, endpoint): """ Check if given routing endpoint is available. @@ -180,7 +185,7 @@ class HawatApp(flask.Flask): """ return endpoint in self.view_classes - def get_view_class(self, endpoint): + def get_endpoint_class(self, endpoint, quiet = False): """ Get reference to view class registered to given routing endpoint. @@ -189,6 +194,8 @@ class HawatApp(flask.Flask): :rtype: class """ if not endpoint in self.view_classes: + if quiet: + return None raise HawatAppException("Unknown endpoint name '{}'.".format(endpoint)) return self.view_classes[endpoint] @@ -202,7 +209,7 @@ class HawatApp(flask.Flask): :rtype: bool """ try: - view_class = self.get_view_class(endpoint) + view_class = self.get_endpoint_class(endpoint) # Reject unauthenticated users in case view requires authentication. if view_class.authentication: @@ -226,9 +233,20 @@ class HawatApp(flask.Flask): return False def get_resource(self, name): + """ + Return reference to given registered resource. + + :param str name: Name of the resource. + """ return self.resources[name]() def set_resource(self, name, resource): + """ + Store reference to given resource. + + :param str name: Name of the resource. + :param resource: Resource to be registered. + """ self.resources[name] = weakref.ref(resource) @@ -307,6 +325,228 @@ class HawatBlueprint(flask.Blueprint): self.add_url_rule(route_spec, view_func = view_func) +#------------------------------------------------------------------------------- + + +class HTMLViewMixin: + """ + Mixin class enabling rendering responses as HTML. Use it in your custom view + classess based on :py:class:`hawat.base.HawatRenderableView` to provide the + ability to render Jinja2 template files into HTML responses. + """ + + @staticmethod + def abort(status_code, message = None): # pylint: disable=locally-disabled,unused-argument + """ + Abort request processing with ``flask.abort`` function and custom status + code. Return response as HTML error page. + """ + flask.abort(status_code) + + def flash(self, message, level = 'info'): # pylint: disable=locally-disabled,no-self-use + """ + Display a one time message to the user. This implementation uses the + :py:func:`flask.flash` method. + + :param str message: Message text. + :param str level: Level of the flash message. + """ + flask.flash(message, level) + + def redirect(self, target_url = None, default_url = None, exclude_url = None): # pylint: disable=locally-disabled,no-self-use + """ + Redirect user to different page. This implementation uses the + :py:func:`flask.redirect` method to return valid HTTP redirection response. + + :param str target_url: Explicit redirection target, if possible. + :param str default_url: Default redirection URL to use in case it cannot be autodetected from the response. + :param str exclude_url: URL to which to never redirect (for example never redirect back to the item detail after the item deletion). + """ + return flask.redirect( + get_redirect_target(target_url, default_url, exclude_url) + ) + + def generate_response(self, view_template = None): + """ + Generate the response appropriate for this view class, in this case HTML + page. + + :param str view_template: Override internally preconfigured page template. + """ + return flask.render_template( + view_template or self.get_view_template(), + **self.response_context + ) + + +class AJAXViewMixin: + """ + Mixin class enabling rendering responses as JSON documents. Use it in your + custom view classess based on based on :py:class:`hawat.base.HawatRenderableView` + to provide the ability to generate JSON responses. + """ + + @staticmethod + def process_response_context(response_context): + """ + Perform additional mangling with the response context before generating + the response. This method can be useful to delete some context keys, that + should not leave the server. + + :param dict response_context: Response context. + :return: Possibly updated response context. + :rtype: dict + """ + return response_context + + @staticmethod + def abort(status_code, message = None): + """ + Abort request processing with ``flask.abort`` function and custom status + code. Return response as JSON document. + """ + response = flask.jsonify({ + 'status': status_code, + 'message': message, + }) + response.status_code = status_code + flask.abort(response) + + def flash(self, message, level = 'info'): # pylint: disable=locally-disabled,no-self-use + """ + Display a one time message to the user. This implementation uses the + ``flash_messages`` subkey in returned JSON document to store the messages. + + :param str message: Message text. + :param str level: Level of the flash message. + """ + self.response_context.\ + setdefault('flash_messages', {}).\ + setdefault(level, []).\ + append(message) + + def redirect(self, target_url = None, default_url = None, exclude_url = None): # pylint: disable=locally-disabled,no-self-use + """ + Redirect user to different page. This implementation stores the redirection + target to the JSON response. + + :param str target_url: Explicit redirection target, if possible. + :param str default_url: Default redirection URL to use in case it cannot be autodetected from the response. + :param str exclude_url: URL to which to never redirect (for example never redirect back to the item detail after the item deletion). + """ + self.response_context.update( + redirect = get_redirect_target(target_url, default_url, exclude_url) + ) + return flask.jsonify( + self.process_response_context(self.response_context) + ) + + def generate_response(self, view_template = None): # pylint: disable=locally-disabled,unused-argument + """ + Generate the response appropriate for this view class, in this case JSON + document. + + :param str view_template: Override internally preconfigured page template. + """ + return flask.jsonify( + self.process_response_context(self.response_context) + ) + + +class SQLAlchemyViewMixin: + """ + Mixin class providing generic interface for interacting with SQL database + backend through SQLAlchemy library. + """ + + @property + def dbmodel(self): + """ + This property must be implemented in each subclass to + return reference to appropriate model class based on *SQLAlchemy* declarative + base. + """ + raise NotImplementedError() + + @property + def search_by(self): + """ + Return model`s attribute (column) according to which to search for the item. + """ + return self.dbmodel.id + + @property + def dbsession(self): + """ + This property contains the reference to current *SQLAlchemy* database session. + """ + return hawat.db.db_get().session + + def dbquery(self, dbmodel = None): + """ + This property contains the reference to *SQLAlchemy* query object appropriate + for particular ``dbmodel`` property. + """ + return self.dbsession.query(dbmodel or self.dbmodel) + + @staticmethod + def build_query(query, model, form_args): # pylint: disable=locally-disabled,unused-argument + """ + *Hook method*. Modify given query according to the given arguments. + """ + return query + + def fetch(self, item_id): + """ + Fetch item with given primary identifier from the database. + """ + return self.dbquery().filter(self.search_by == item_id).first() + + def search(self, form_args): + """ + Perform actual search with given query. + """ + query = self.build_query(self.dbquery(), self.dbmodel, form_args) + + # Adjust the query according to the paging parameters. + if 'limit' in form_args and form_args['limit']: + query = query.limit(int(form_args['limit'])) + if 'page' in form_args and form_args['page'] and int(form_args['page']) > 1: + query = query.offset((int(form_args['page']) - 1) * int(form_args['limit'])) + + return query.all() + + +class PsycopgViewMixin: + """ + Mixin class providing generic interface for interacting with SQL database + backend through SQLAlchemy library. + """ + + def fetch(self, item_id): # pylint: disable=locally-disabled,no-self-use + """ + Fetch item with given primary identifier from the database. + """ + return hawat.events.db_get().fetch_event(item_id) + + def search(self, form_args): + """ + Perform actual search of IDEA events using provided query arguments. + + :param dict form_args: Search query arguments. + :return: Tuple containing number of items as integer and list of searched items. + :rtype: tuple + """ + items_count_total, items = hawat.events.db_get().search_events(form_args) + self.response_context.update( + sqlquery = hawat.events.db_get().cursor.lastquery + ) + return items + + +#------------------------------------------------------------------------------- + + class HawatBaseView(flask.views.View): """ Base class for all custom Hawat application views. @@ -358,6 +598,9 @@ class HawatBaseView(flask.views.View): parent blueprint/module and should contain only characters ``[a-z0-9]``. It will be used for generating endpoint name for the view. + *This method does not have any default implementation and must be overridden + by a subclass.* + :return: Name for the view. :rtype: str """ @@ -366,45 +609,75 @@ class HawatBaseView(flask.views.View): @classmethod def get_view_endpoint(cls): """ - Return name of the routing endpoint for the view. + Return name of the routing endpoint for the view within the whole application. + + Default implementation generates the endpoint name by concatenating the + module name and view name. - :return: Routing endpoint for the view. + :return: Routing endpoint for the view within the whole application. :rtype: str """ return '{}.{}'.format(cls.module_name, cls.get_view_name()) @classmethod - def get_menu_title(cls): + def get_view_title(cls, item = None): # pylint: disable=locally-disabled,unused-argument """ - Return menu item title for the view. + Return title for the view, that will be displayed in the ``title`` tag of + HTML ``head`` element and also as the content of page header in ``h2`` tag. + + Default implementation returns the return value of :py:func:`hawat.base.HawatBaseView.get_menu_title` + method by default. - :return: Menu item title for the view. + :param item: Optional item. + :return: Title for the view. :rtype: str """ - raise NotImplementedError() + return cls.get_menu_title(item) @classmethod - def get_menu_legend(cls, item = None): # pylint: disable=locally-disabled,unused-argument + def get_menu_icon(cls): """ - Return menu item legend for the view (hover tooltip). + Return menu entry icon name for the view. Given name will be used as index + to built-in icon registry. - :param item: Optional item. - :return: Menu item legend for the view. + Default implementation generates the icon name by concatenating the prefix + ``module-`` with module name. + + :return: Menu entry icon for the view. :rtype: str """ - return cls.get_menu_title() + return 'module-{}'.format(cls.module_name) @classmethod - def get_menu_icon(cls): + def get_menu_title(cls, item = None): # pylint: disable=locally-disabled,unused-argument """ - Return menu item icon name for the view. Given name will be used as index to - built-in icon registry. + Return menu entry title for the view. + + *This method does not have any default implementation and must be overridden + by a subclass.* - :return: Menu item icon for the view. + :param item: Optional item. + :return: Menu entry title for the view. :rtype: str """ raise NotImplementedError() + @classmethod + def get_menu_legend(cls, item = None): # pylint: disable=locally-disabled,unused-argument + """ + Return menu entry legend for the view (menu entry hover tooltip). + + Default implementation returns the return value of :py:func:`hawat.base.HawatBaseView.get_menu_title` + method by default. + + :param item: Optional item. + :return: Menu entry legend for the view. + :rtype: str + """ + return cls.get_menu_title(item) + + #--------------------------------------------------------------------------- + @staticmethod def can_access_endpoint(endpoint, item = None): """ @@ -414,23 +687,24 @@ class HawatBaseView(flask.views.View): :param str endpoint: Application routing endpoint. :param item: Optional item. - :return: ``True`` in case user can access the endpoint, ``False`` otherwise. + :return: ``True`` in case current user can access the endpoint, ``False`` otherwise. :rtype: bool """ return flask.current_app.can_access_endpoint(endpoint, item) - def has_view_class(self, endpoint): + @staticmethod + def has_endpoint(endpoint): """ - Check if given routing endpoint is available. + Check if given routing endpoint is available within the application. :param str endpoint: Application routing endpoint. :return: ``True`` in case endpoint exists, ``False`` otherwise. :rtype: bool """ - return flask.current_app.has_view_class(endpoint) + return flask.current_app.has_endpoint(endpoint) @staticmethod - def get_view_class(endpoint): + def get_endpoint_class(endpoint): """ Get reference to view class registered to given routing endpoint. @@ -438,7 +712,16 @@ class HawatBaseView(flask.views.View): :return: Reference to view class. :rtype: class """ - return flask.current_app.get_view_class(endpoint) + return flask.current_app.get_endpoint_class(endpoint) + + #--------------------------------------------------------------------------- + + @property + def logger(self): + """ + Return current application`s logger object. + """ + return flask.current_app.logger class HawatFileNameView(HawatBaseView): @@ -452,8 +735,11 @@ class HawatFileNameView(HawatBaseView): @classmethod def get_directory_path(cls): """ - *Hook method*. Must return absolute path to the directory, that will be - used as a base path for serving files. + Return absolute path to the directory, that will be used as a base path + for serving files. + + *This method does not have any default implementation and must be overridden + by a subclass.* :return: Absolute path to the directory for serving files. :rtype: str @@ -463,8 +749,9 @@ class HawatFileNameView(HawatBaseView): @classmethod def validate_filename(cls, filename): """ - *Hook method*. Validate given file name to prevent user from accessing - restricted files. + Validate given file name to prevent user from accessing restricted files. + + In default implementation all files pass the validation. :param str filename: Name of the file to be validated/filtered. :return: ``True`` in case file name is allowed, ``False`` otherwise. @@ -475,12 +762,12 @@ class HawatFileNameView(HawatBaseView): def dispatch_request(self, filename): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. """ if not self.validate_filename(filename): flask.abort(400) - flask.current_app.logger.info( + self.logger.info( "Serving file '{}' from directory '{}'.".format( filename, self.get_directory_path() @@ -508,11 +795,14 @@ class HawatFileIdView(HawatBaseView): @classmethod def get_directory_path(cls, fileid): """ - *Hook method*. Must return absolute path to the directory, that will be + This method must return absolute path to the directory, that will be used as a base path for serving files. Parameter ``fileid`` may be used internally to further customize the base directory, for example when serving some files places into subdirectories based on some part of the - file name (to reduce total number of files in base directory). + file name (for example to reduce total number of files in base directory). + + *This method does not have any default implementation and must be overridden + by a subclass.* :param str fileid: Identifier of the requested file. :return: Absolute path to the directory for serving files. @@ -523,9 +813,12 @@ class HawatFileIdView(HawatBaseView): @classmethod def get_filename(cls, fileid, filetype): """ - *Hook method*. Must return actual name of the file based on given identifier + This method must return actual name of the file based on given identifier and type. + *This method does not have any default implementation and must be overridden + by a subclass.* + :param str fileid: Identifier of the requested file. :param str filetype: Type of the requested file. :return: Translated name of the file. @@ -536,12 +829,14 @@ class HawatFileIdView(HawatBaseView): def dispatch_request(self, fileid, filetype): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. """ basedirpath = self.get_directory_path(fileid) filename = self.get_filename(fileid, filetype) + if not basedirpath or not filename: + flask.abort(400) - flask.current_app.logger.info( + self.logger.info( "Serving file '{}' from directory '{}'.".format( filename, basedirpath @@ -556,28 +851,12 @@ class HawatFileIdView(HawatBaseView): class HawatRenderableView(HawatBaseView): # pylint: disable=locally-disabled,abstract-method """ - Base class for all views, that are rendering content based on Jinja2 templates. + Base class for all views, that are rendering content based on Jinja2 templates + or returning JSON/XML data. """ - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(cls.module_name) - - @classmethod - def get_view_title(cls): - """ - Return title for the view, that will be displayed in the ``title`` tag of - HTML ``head`` element and also as the content of page header in ``h2`` tag. - Default implementation returns the content of ``get_menu_title`` method - by default. - - :return: Title for the view. - :rtype: str - """ - return cls.get_menu_title() + def __init__(self): + self.response_context = {} @classmethod def get_view_template(cls): @@ -587,89 +866,71 @@ class HawatRenderableView(HawatBaseView): # pylint: disable=locally-disabled,ab was properly registered into the parent blueprint/module with :py:func:`hawat.base.HawatBlueprint.register_view_class` method. - :return: Title for the view. + :return: Jinja2 template file to use to render the view. :rtype: str """ if cls.module_name: return '{}/{}.html'.format(cls.module_name, cls.get_view_name()) raise RuntimeError("Unable to guess default view template, because module name was not yet set.") - def get_template_context(self): + def do_before_response(self, **kwargs): # pylint: disable=locally-disabled,no-self-use,unused-argument """ - Initialize and return default context to be passed down to the rendered - template. This method is perfect place for injecting common ``global`` - variables into the template. + This method will be called just before generating the response. By providing + some meaningfull implementation you can use it for some simple item and + response context mangling tasks. - :return: Jinja2 template context. - :rtype: dict + :param kwargs: Custom additional arguments. """ - return { - 'hawat_this_view_title': self.get_view_title(), - 'hawat_this_view_endpoint': self.get_view_endpoint() - } - + pass -class HawatSimpleView(HawatRenderableView): # pylint: disable=locally-disabled,abstract-method - """ - Base class for simple views. These are the most, well, simple views, that are - rendering single template file. - - In most use cases, it should be enough to just enhance the default implementation :py:func:`hawat.base.HawatBaseView.get_template_context` to inject some additional - variables into the template. - """ - def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ + def generate_response(self): """ - Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. - """ - context = self.get_template_context() - return flask.render_template(self.get_view_template(), **context) - + Generate the appropriate response from given response context. -class HawatDbmodelView(HawatRenderableView): - """ - Base class for all CREATE,READ,UPDATE,DELETE,LIST views. This base class provides - generic interface for interacting with SQL database backend through SQLAlchemy - library. - """ + :param dict response_context: Response context as a dictionary + """ + raise NotImplementedError() - @property - def dbmodel(self): + @staticmethod + def abort(status_code, message = None): """ - *Hook property*. This property must be implemented in each subclass to - return reference to appropriate model class based on *SQLAlchemy* declarative - base. + Abort request processing with HTTP status code. """ raise NotImplementedError() - @property - def dbsession(self): + def flash(self, message, level = 'info'): """ - This property contains the reference to current *SQLAlchemy* database session. + Flash information to the user. """ - return hawat.db.db_get().session + raise NotImplementedError() - @property - def dbquery(self): + def redirect(self, default_url = None, exclude_url = None): """ - This property contains the reference to *SQLAlchemy* query object appropriate - for particular ``dbmodel`` property. + Redirect user to different location. """ - return self.dbsession.query(self.dbmodel) + raise NotImplementedError() - def do_before_render(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument - """ - *Hook method*. Will be called before rendering the template. By providing - some meaningfull implementation you can use it for simple item and context - mangling tasks. - :param item: Item of unspecified type, depends on the ``dbmodel`` property. - :param dict context: Jinja2 template context. +class HawatSimpleView(HawatRenderableView): # pylint: disable=locally-disabled,abstract-method + """ + Base class for simple views. These are the most, well, simple views, that are + rendering single template file or directly returning some JSON/XML data without + any user parameters. + + In most use cases, it should be enough to just enhance the default implementation + of :py:func:`hawat.base.HawatRenderableView.get_response_context` to inject + some additional variables into the template. + """ + def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ """ - return None + Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. + Will be called by the **Flask** framework to service the request. + """ + self.do_before_response() + return self.generate_response() -class HawatSearchView(HawatDbmodelView): +class HawatSearchView(HawatRenderableView): """ Base class for search views. """ @@ -680,20 +941,6 @@ class HawatSearchView(HawatDbmodelView): """ return 'search' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(cls.module_name) - - @classmethod - def get_view_template(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. - """ - return '{}/search.html'.format(cls.module_name) - #--------------------------------------------------------------------------- @staticmethod @@ -728,31 +975,16 @@ class HawatSearchView(HawatDbmodelView): params[arg] = request_args[arg] return params - @staticmethod - def build_query(query, model, args, context): # pylint: disable=locally-disabled,unused-argument - """ - *Hook method*. Modify given query according to the given arguments. - """ - return query - - def search(self, form_args, context): + def search(self, form_args): """ Perform actual search with given query. """ - query = self.build_query(self.dbquery, self.dbmodel, form_args, context) - - # Adjust the query according to the paging parameters. - if 'limit' in form_args and form_args['limit']: - query = query.limit(int(form_args['limit'])) - if 'page' in form_args and form_args['page'] and int(form_args['page']) > 1: - query = query.offset((int(form_args['page']) - 1) * int(form_args['limit'])) - - return query.all() + raise NotImplementedError() #--------------------------------------------------------------------------- @classmethod - def get_breadcrumbs_menu(cls): + def get_breadcrumbs_menu(cls, item = None): """ Get breadcrumbs menu. """ @@ -771,14 +1003,14 @@ class HawatSearchView(HawatDbmodelView): return breadcrumbs_menu @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ Get action menu for all items. """ return None @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ @@ -793,47 +1025,75 @@ class HawatSearchView(HawatDbmodelView): #--------------------------------------------------------------------------- + def do_before_search(self, form_data): # pylint: disable=locally-disabled,no-self-use,unused-argument + """ + This hook method will be called before search attempt. + """ + pass + + def do_after_search(self, items): # pylint: disable=locally-disabled,no-self-use,unused-argument + """ + This hook method will be called after successfull search. + """ + pass + def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. """ - context = self.get_template_context() - form = self.get_search_form(flask.request.args) + flask.g.search_form = form if hawat.const.HAWAT_FORM_ACTION_SUBMIT in flask.request.args: if form.validate(): form_data = form.data - items = self.search(form_data, context) - context.update( + self.do_before_search(form_data) + + try: + items = self.search(form_data) + except Exception as err: # pylint: disable=locally-disabled,broad-except + match = re.match('invalid IP4R value: "([^"]+)"', str(err)) + if match: + self.flash( + flask.Markup( + gettext( + 'Invalid address value <strong>%(address)s</strong> in event search form.', + address = flask.escape(str(match.group(1))) + ) + ), + hawat.const.HAWAT_FLASH_FAILURE + ) + else: + raise + + self.response_context.update( searched = True, items = items, items_count = len(items), - form_data = form_data, - action_menu = self.get_action_menu(), - context_action_menu = self.get_context_action_menu() + form_data = form_data ) - # Not all search forms support result paging + + # Not all search forms support result paging. if 'page' in form_data: - context.update( + self.response_context.update( pager_index_low = ((form_data['page'] - 1) * form_data['limit']) + 1, pager_index_high = ((form_data['page'] - 1) * form_data['limit']) + len(items), pager_index_limit = ((form_data['page'] - 1) * form_data['limit']) + form_data['limit'] ) - self.do_before_render(items, context) - context.update( - search_form = form, - request_args = flask.request.args, + self.do_after_search(items) + + self.response_context.update( query_params = self.get_query_parameters(form, flask.request.args), - breadcrumbs_menu = self.get_breadcrumbs_menu() + search_widget_item_limit = 3 ) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() -class HawatItemListView(HawatDbmodelView): # pylint: disable=locally-disabled,abstract-method +class HawatItemListView(HawatRenderableView): # pylint: disable=locally-disabled,abstract-method """ Base class for item *list* views. These views provide quick and simple access to lists of all objects. @@ -847,7 +1107,7 @@ class HawatItemListView(HawatDbmodelView): # pylint: disable=locally-disabled,a return 'list' @classmethod - def get_breadcrumbs_menu(cls): + def get_breadcrumbs_menu(cls, item = None): """ Get breadcrumbs menu. """ @@ -868,43 +1128,44 @@ class HawatItemListView(HawatDbmodelView): # pylint: disable=locally-disabled,a return action_menu @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ Get action menu for all items. """ return None @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ Get context action menu for particular item. """ return None + def search(self, form_args): + """ + Perform actual search with given query. + """ + raise NotImplementedError() + def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. List of all items will be retrieved from database and injected into template to be displayed to the user. """ - items = self.dbquery.all() + items = self.search({}) - context = self.get_template_context() - context.update( - items = items, - breadcrumbs_menu = self.get_breadcrumbs_menu(), - action_menu = self.get_action_menu(), - context_action_menu = self.get_context_action_menu() + self.response_context.update( + items = items ) - self.do_before_render(items, context) + self.do_before_response() + return self.generate_response() - return flask.render_template(self.get_view_template(), **context) - -class HawatItemShowView(HawatDbmodelView): # pylint: disable=locally-disabled,abstract-method +class HawatItemShowView(HawatRenderableView): # pylint: disable=locally-disabled,abstract-method """ Base class for item *show* views. These views expect unique item identifier as parameter and are supposed to display specific information about single @@ -939,20 +1200,12 @@ class HawatItemShowView(HawatDbmodelView): # pylint: disable=locally-disabled,a """ return None - @property - def search_by(self): - """ - Return model`s attribute (column) according to which to search for the item. - """ - return self.dbmodel.id - @classmethod def get_breadcrumbs_menu(cls, item): # pylint: disable=locally-disabled,unused-argument """ Get breadcrumbs menu. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'link', 'index', @@ -970,40 +1223,58 @@ class HawatItemShowView(HawatDbmodelView): # pylint: disable=locally-disabled,a endpoint = '{}.show'.format(cls.module_name), item = item ) - return action_menu + def fetch(self, item_id): + """ + Perform actual search with given query. + """ + raise NotImplementedError() + def dispatch_request(self, item_id): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. Single item with given unique identifier will be retrieved from database and injected into template to be displayed to the user. """ - item = self.dbquery.filter(self.search_by == item_id).first_or_404() + item = self.fetch(item_id) + if not item: + self.abort(404) if not self.authorize_item_action(item): - flask.abort(403) + self.abort(403) - context = self.get_template_context() - context.update( + self.response_context.update( item_id = item_id, item = item, - breadcrumbs_menu = self.get_breadcrumbs_menu(item), - action_menu = self.get_action_menu(item) + search_widget_item_limit = 100 ) - self.do_before_render(item, context) - - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() -class HawatItemActionView(HawatDbmodelView): # pylint: disable=locally-disabled,abstract-method +class HawatItemActionView(HawatRenderableView): # pylint: disable=locally-disabled,abstract-method """ Base class for item action views. These views perform various actions (create/update/delete) with given item class. """ + @classmethod + def get_menu_icon(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. + """ + return 'action-{}'.format(cls.get_view_name()) + + @classmethod + def get_view_template(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. + """ + return 'form_{}.html'.format(cls.get_view_name()).replace('-', '_') + @staticmethod def get_message_success(**kwargs): """ @@ -1039,7 +1310,7 @@ class HawatItemActionView(HawatDbmodelView): # pylint: disable=locally-disabled """ try: return flask.url_for('{}.{}'.format(self.module_name, 'list')) - except: + except werkzeug.routing.BuildError: return flask.url_for('index') def check_action_cancel(self, form, **kwargs): @@ -1047,23 +1318,23 @@ class HawatItemActionView(HawatDbmodelView): # pylint: disable=locally-disabled Check the form for *cancel* button press and cancel the action. """ if getattr(form, hawat.const.HAWAT_FORM_ACTION_CANCEL).data: - flask.flash( + self.flash( flask.Markup(self.get_message_cancel(**kwargs)), hawat.const.HAWAT_FLASH_INFO ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) return None - def do_before_action(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_action(self, item): # pylint: disable=locally-disabled,no-self-use,unused-argument """ *Hook method*. Will be called before any action handling tasks. """ return - def do_after_action(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_after_action(self, item): # pylint: disable=locally-disabled,no-self-use,unused-argument """ - *Hook method*. Will be called after successfull action. + *Hook method*. Will be called after successfull action handling tasks. """ return @@ -1074,6 +1345,28 @@ class HawatItemActionView(HawatDbmodelView): # pylint: disable=locally-disabled """ return True + @property + def dbsession(self): + """ + This property contains the reference to current *SQLAlchemy* database session. + """ + raise NotImplementedError() + + @property + def dbmodel(self): + """ + This property must be implemented in each subclass to + return reference to appropriate model class based on *SQLAlchemy* declarative + base. + """ + raise NotImplementedError() + + def fetch(self, item_id): + """ + Perform actual search with given query. + """ + raise NotImplementedError() + def changelog_log(self, item, json_state_before = '', json_state_after = ''): """ Log item action into changelog. One of the method arguments is permitted @@ -1114,13 +1407,6 @@ class HawatItemCreateView(HawatItemActionView): # pylint: disable=locally-disab """ return 'create' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'action-create' - @classmethod def get_view_template(cls): """ @@ -1147,19 +1433,17 @@ class HawatItemCreateView(HawatItemActionView): # pylint: disable=locally-disab def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. This method will attempt to validate the submitted form and create new instance of appropriate item from form data and finally store the item into the database. """ if not self.authorize_item_action(): - flask.abort(403) + self.abort(403) item = self.dbmodel() - context = self.get_template_context() - form = self.get_item_form() cancel_response = self.check_action_cancel(form) @@ -1170,26 +1454,26 @@ class HawatItemCreateView(HawatItemActionView): # pylint: disable=locally-disab form_data = form.data form.populate_obj(item) - self.do_before_action(item, context) + self.do_before_action(item) if form_data[hawat.const.HAWAT_FORM_ACTION_SUBMIT]: try: self.dbsession.add(item) self.dbsession.commit() - self.do_after_action(item, context) + self.do_after_action(item) # Log the item creation into changelog. self.changelog_log(item, '', item.to_json()) - flask.flash( + self.flash( flask.Markup(self.get_message_success(item = item)), hawat.const.HAWAT_FLASH_SUCCESS ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) except Exception: # pylint: disable=locally-disabled,broad-except self.dbsession.rollback() - flask.flash( + self.flash( flask.Markup(self.get_message_failure()), hawat.const.HAWAT_FLASH_FAILURE ) @@ -1197,17 +1481,17 @@ class HawatItemCreateView(HawatItemActionView): # pylint: disable=locally-disab traceback.TracebackException(*sys.exc_info()), self.get_message_failure() ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) - context.update( + self.response_context.update( action_name = gettext('Create'), form_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name())), form = form, item_action = hawat.const.HAWAT_ITEM_ACTION_CREATE ) - self.do_before_render(item, context) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() class HawatItemCreateForView(HawatItemActionView): # pylint: disable=locally-disabled,abstract-method @@ -1285,7 +1569,7 @@ class HawatItemCreateForView(HawatItemActionView): # pylint: disable=locally-di def dispatch_request(self, parent_id): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. This method will attempt to validate the submitted form and create new instance of appropriate item from form data and finally store the item @@ -1294,10 +1578,9 @@ class HawatItemCreateForView(HawatItemActionView): # pylint: disable=locally-di parent = self.dbquery_par.filter(self.dbmodel_par.id == parent_id).first_or_404() if not self.authorize_item_action(parent): - flask.abort(403) + self.abort(403) - context = self.get_template_context() - context.update( + self.response_context.update( parent_id = parent_id, parent = parent ) @@ -1313,27 +1596,27 @@ class HawatItemCreateForView(HawatItemActionView): # pylint: disable=locally-di form_data = form.data form.populate_obj(item) - self.do_before_action(item, context) + self.do_before_action(item) self.add_parent_to_item(item, parent) if form_data[hawat.const.HAWAT_FORM_ACTION_SUBMIT]: try: self.dbsession.add(item) self.dbsession.commit() - self.do_after_action(item, context) + self.do_after_action(item) # Log the item creation into changelog. self.changelog_log(item, '', item.to_json()) - flask.flash( + self.flash( flask.Markup(self.get_message_success(item = item, parent = parent)), hawat.const.HAWAT_FLASH_SUCCESS ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) except Exception: # pylint: disable=locally-disabled,broad-except self.dbsession.rollback() - flask.flash( + self.flash( flask.Markup(self.get_message_failure(parent = parent)), hawat.const.HAWAT_FLASH_FAILURE ) @@ -1341,17 +1624,17 @@ class HawatItemCreateForView(HawatItemActionView): # pylint: disable=locally-di traceback.TracebackException(*sys.exc_info()), self.get_message_failure(parent = parent) ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) - context.update( + self.response_context.update( action_name = gettext('Create'), form_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name()), parent_id = parent_id), form = form, item_action = hawat.const.HAWAT_ITEM_ACTION_CREATEFOR ) - self.do_before_render(item, context) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disabled,abstract-method @@ -1367,13 +1650,6 @@ class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disab """ return 'update' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'action-update' - @classmethod def get_view_template(cls): """ @@ -1400,18 +1676,18 @@ class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disab def dispatch_request(self, item_id): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. This method will attempt to validate the submitted form and update the instance of appropriate item from form data and finally store the item back into the database. """ - item = self.dbquery.filter(self.dbmodel.id == item_id).first_or_404() + item = self.fetch(item_id) + if not item: + self.abort(404) if not self.authorize_item_action(item): - flask.abort(403) - - context = self.get_template_context() + self.abort(403) self.dbsession.add(item) @@ -1427,32 +1703,32 @@ class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disab form_data = form.data form.populate_obj(item) - self.do_before_action(item, context) + self.do_before_action(item) if form_data[hawat.const.HAWAT_FORM_ACTION_SUBMIT]: try: if item not in self.dbsession.dirty: - flask.flash( + self.flash( gettext('No changes detected, no update needed.'), hawat.const.HAWAT_FLASH_INFO ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) self.dbsession.commit() - self.do_after_action(item, context) + self.do_after_action(item) # Log the item update into changelog. self.changelog_log(item, item_json_before, item.to_json()) - flask.flash( + self.flash( flask.Markup(self.get_message_success(item = item)), hawat.const.HAWAT_FLASH_SUCCESS ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) except Exception: # pylint: disable=locally-disabled,broad-except self.dbsession.rollback() - flask.flash( + self.flash( flask.Markup(self.get_message_failure(item = item)), hawat.const.HAWAT_FLASH_FAILURE ) @@ -1460,9 +1736,9 @@ class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disab traceback.TracebackException(*sys.exc_info()), self.get_message_failure(item = item) ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) - context.update( + self.response_context.update( action_name = gettext('Update'), form_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name()), item_id = item_id), form = form, @@ -1471,8 +1747,8 @@ class HawatItemUpdateView(HawatItemActionView): # pylint: disable=locally-disab item = item ) - self.do_before_render(item, context) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() class HawatItemDeleteView(HawatItemActionView): # pylint: disable=locally-disabled,abstract-method @@ -1488,35 +1764,21 @@ class HawatItemDeleteView(HawatItemActionView): # pylint: disable=locally-disab """ return 'delete' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'action-delete' - - @classmethod - def get_view_template(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. - """ - return 'form_delete.html' - def dispatch_request(self, item_id): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. This method will attempt to validate the submitted form and delete the instance of appropriate item from database in case user agreed to the item removal action. """ - item = self.dbquery.filter(self.dbmodel.id == item_id).first_or_404() + item = self.fetch(item_id) + if not item: + self.abort(404) if not self.authorize_item_action(item): - flask.abort(403) - - context = self.get_template_context() + self.abort(403) form = ItemActionConfirmForm() @@ -1529,29 +1791,29 @@ class HawatItemDeleteView(HawatItemActionView): # pylint: disable=locally-disab if form.validate_on_submit(): form_data = form.data - self.do_before_action(item, context) + self.do_before_action(item) if form_data[hawat.const.HAWAT_FORM_ACTION_SUBMIT]: try: self.dbsession.delete(item) self.dbsession.commit() - self.do_after_action(item, context) + self.do_after_action(item) # Log the item deletion into changelog. self.changelog_log(item, item_json_before, '') - flask.flash( + self.flash( flask.Markup(self.get_message_success(item = item)), hawat.const.HAWAT_FLASH_SUCCESS ) - return form.flask_redirect( + return self.redirect( default_url = self.get_url_next(), exclude_url = flask.url_for('{}.{}'.format(self.module_name, 'show'), item_id = item.id) ) except Exception: # pylint: disable=locally-disabled,broad-except self.dbsession.rollback() - flask.flash( + self.flash( flask.Markup(self.get_message_failure(item = item)), hawat.const.HAWAT_FLASH_FAILURE ) @@ -1559,9 +1821,9 @@ class HawatItemDeleteView(HawatItemActionView): # pylint: disable=locally-disab traceback.TracebackException(*sys.exc_info()), self.get_message_failure(item = item) ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) - context.update( + self.response_context.update( confirm_form = form, confirm_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name()), item_id = item_id), item_name = str(item), @@ -1569,8 +1831,8 @@ class HawatItemDeleteView(HawatItemActionView): # pylint: disable=locally-disab item = item ) - self.do_before_render(item, context) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() class HawatItemChangeView(HawatItemActionView): # pylint: disable=locally-disabled,abstract-method @@ -1598,21 +1860,21 @@ class HawatItemChangeView(HawatItemActionView): # pylint: disable=locally-disab def dispatch_request(self, item_id): # pylint: disable=locally-disabled,arguments-differ """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + Will be called by the **Flask** framework to service the request. This method will attempt to validate the submitted form, then perform arbitrary mangling action with the item and submit the changes to the database. """ - item = self.dbquery.filter(self.dbmodel.id == item_id).first_or_404() + item = self.fetch(item_id) + if not item: + self.abort(404) if not self.authorize_item_action(item): - flask.abort(403) + self.abort(403) if not self.validate_item_change(item): - flask.abort(400) - - context = self.get_template_context() + self.abort(400) form = ItemActionConfirmForm() @@ -1625,40 +1887,40 @@ class HawatItemChangeView(HawatItemActionView): # pylint: disable=locally-disab if form.validate_on_submit(): form_data = form.data - self.do_before_action(item, context) + self.do_before_action(item) if form_data[hawat.const.HAWAT_FORM_ACTION_SUBMIT]: try: self.change_item(item) if item not in self.dbsession.dirty: - flask.flash( + self.flash( gettext('No changes detected, no update needed.'), hawat.const.HAWAT_FLASH_INFO ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) self.dbsession.commit() - self.do_after_action(item, context) + self.do_after_action(item) # Log the item change into changelog. self.changelog_log(item, item_json_before, item.to_json()) - flask.flash( + self.flash( flask.Markup(self.get_message_success(item = item)), hawat.const.HAWAT_FLASH_SUCCESS ) try: exclude_url = flask.url_for('{}.{}'.format(self.module_name, 'show'), item_id = item.id) - except: + except werkzeug.routing.BuildError: exclude_url = None - return form.flask_redirect( + return self.redirect( default_url = self.get_url_next(), exclude_url = exclude_url ) except Exception: # pylint: disable=locally-disabled,broad-except self.dbsession.rollback() - flask.flash( + self.flash( flask.Markup(self.get_message_failure(item = item)), hawat.const.HAWAT_FLASH_FAILURE ) @@ -1666,9 +1928,9 @@ class HawatItemChangeView(HawatItemActionView): # pylint: disable=locally-disab traceback.TracebackException(*sys.exc_info()), self.get_message_failure(item = item) ) - return form.flask_redirect(self.get_url_next()) + return self.redirect(default_url = self.get_url_next()) - context.update( + self.response_context.update( confirm_form = form, confirm_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name()), item_id = item_id), item_name = str(item), @@ -1676,8 +1938,8 @@ class HawatItemChangeView(HawatItemActionView): # pylint: disable=locally-disab item = item ) - self.do_before_render(item, context) - return flask.render_template(self.get_view_template(), **context) + self.do_before_response() + return self.generate_response() class HawatItemDisableView(HawatItemChangeView): # pylint: disable=locally-disabled,abstract-method @@ -1692,20 +1954,6 @@ class HawatItemDisableView(HawatItemChangeView): # pylint: disable=locally-disa """ return 'disable' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'action-disable' - - @classmethod - def get_view_template(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. - """ - return 'form_disable.html' - @classmethod def validate_item_change(cls, item): # pylint: disable=locally-disabled,unused-argument """ @@ -1736,20 +1984,6 @@ class HawatItemEnableView(HawatItemChangeView): # pylint: disable=locally-disab """ return 'enable' - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'action-enable' - - @classmethod - def get_view_template(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. - """ - return 'form_enable.html' - @classmethod def validate_item_change(cls, item): # pylint: disable=locally-disabled,unused-argument """ diff --git a/lib/hawat/blueprints/auth_api/__init__.py b/lib/hawat/blueprints/auth_api/__init__.py index 27c5579f8e92e1240bf0ab040ed679093bf58d59..59ad6390d28182bd96ef1c8c3ea9bb4a97bacf34 100644 --- a/lib/hawat/blueprints/auth_api/__init__.py +++ b/lib/hawat/blueprints/auth_api/__init__.py @@ -32,19 +32,17 @@ from flask_babel import gettext, lazy_gettext # Custom modules. # import hawat.const -import hawat.base import hawat.db import hawat.forms +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemChangeView, HawatBlueprint from hawat.models.user import GuiUserModel -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'auth_api' +"""Name of the blueprint as module global constant.""" -class GenerateKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locally-disabled,too-many-ancestors +class GenerateKeyView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemChangeView): # pylint: disable=locally-disabled,too-many-ancestors """ View for generating API keys for user accounts. """ @@ -69,18 +67,25 @@ class GenerateKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locall return 'action-genkey' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Generate API key') + @classmethod + def get_view_template(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. + """ + return 'auth_api/key-generate.html' + #--------------------------------------------------------------------------- @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -89,21 +94,21 @@ class GenerateKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locall @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('API key for user account <strong>%(item_id)s</strong> was successfully generated.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to generate API key for user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled generating API key for user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -119,7 +124,7 @@ class GenerateKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locall item.apikey = serializer.dumps(item.id) -class DeleteKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locally-disabled,too-many-ancestors +class DeleteKeyView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemChangeView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -144,18 +149,25 @@ class DeleteKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locally- return 'action-delete' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Delete API key') + @classmethod + def get_view_template(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_template`. + """ + return 'auth_api/key-delete.html' + #--------------------------------------------------------------------------- @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -164,21 +176,21 @@ class DeleteKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locally- @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('API key for user account <strong>%(item_id)s</strong> was successfully deleted.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to delete API key for user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting API key for user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -193,7 +205,7 @@ class DeleteKeyView(hawat.base.HawatItemChangeView): # pylint: disable=locally- #------------------------------------------------------------------------------- -class AuthBlueprint(hawat.base.HawatBlueprint): +class AuthBlueprint(HawatBlueprint): """ Hawat pluggable module - environment based authentication. """ diff --git a/lib/hawat/blueprints/auth_api/templates/auth_api/key-delete.html b/lib/hawat/blueprints/auth_api/templates/auth_api/key-delete.html index 1572f39a6e0cedd2a50020adec604c7f393edb0e..ac2f2e8673f48874b2177443d208d07d5b193e8f 100644 --- a/lib/hawat/blueprints/auth_api/templates/auth_api/key-delete.html +++ b/lib/hawat/blueprints/auth_api/templates/auth_api/key-delete.html @@ -10,7 +10,7 @@ <div class="modal-content"> <div class="modal-header"> - <h4 class="modal-title">{{ get_fa_icon('modal-question') }} {{ hawat_this_view_title }}</h4> + <h4 class="modal-title">{{ get_icon('modal-question') }} {{ hawat_current_view.get_view_title() }}</h4> </div> <div class="modal-body"> diff --git a/lib/hawat/blueprints/auth_api/templates/auth_api/key-generate.html b/lib/hawat/blueprints/auth_api/templates/auth_api/key-generate.html index a4768ebb6b8209e243afac3a78b84705fc3f741c..84c5272456959ff7082d293694987b5804865f10 100644 --- a/lib/hawat/blueprints/auth_api/templates/auth_api/key-generate.html +++ b/lib/hawat/blueprints/auth_api/templates/auth_api/key-generate.html @@ -10,7 +10,7 @@ <div class="modal-content"> <div class="modal-header"> - <h4 class="modal-title">{{ get_fa_icon('modal-question') }} {{ hawat_this_view_title }}</h4> + <h4 class="modal-title">{{ get_icon('modal-question') }} {{ hawat_current_view.get_view_title() }}</h4> </div> <div class="modal-body"> diff --git a/lib/hawat/blueprints/auth_dev/__init__.py b/lib/hawat/blueprints/auth_dev/__init__.py index dcaa49b46817a05e5bbae63e16e618afbb7f6510..37493fff18c767d8e29e241580e02095404d015d 100644 --- a/lib/hawat/blueprints/auth_dev/__init__.py +++ b/lib/hawat/blueprints/auth_dev/__init__.py @@ -61,19 +61,17 @@ from flask_babel import gettext, lazy_gettext # Custom modules. # import hawat.const -import hawat.base import hawat.forms +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatSimpleView, HawatBlueprint from hawat.models.user import GuiUserModel from hawat.blueprints.auth_dev.forms import LoginForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'auth_dev' +"""Name of the blueprint as module global constant.""" -class LoginView(hawat.base.HawatSimpleView): +class LoginView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSimpleView): """ View enabling special developer login. """ @@ -94,19 +92,24 @@ class LoginView(hawat.base.HawatSimpleView): return 'login' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Developer login') + @property + def dbmodel(self): + """ + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. + """ + return GuiUserModel + def dispatch_request(self): """ Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. Will be called by the *Flask* framework to service the request. """ - context = self.get_template_context() - form = LoginForm() if form.validate_on_submit(): @@ -115,13 +118,13 @@ class LoginView(hawat.base.HawatSimpleView): user = dbsess.query(GuiUserModel).filter(GuiUserModel.login == form.login.data).one() if not user.enabled: - flask.flash( + self.flash( flask.Markup( gettext('The account for user <strong>%(login)s (%(name)s)</strong> is currently disabled, you may not log in as that user.', login = user.login, name = user.fullname) ), hawat.const.HAWAT_FLASH_FAILURE ) - return flask.abort(403) + self.abort(403) flask_login.login_user(user) @@ -133,30 +136,30 @@ class LoginView(hawat.base.HawatSimpleView): identity = flask_principal.Identity(user.get_id()) ) - flask.flash( + self.flash( flask.Markup(gettext( 'You have been successfully logged in as <strong>%(user)s</strong>.', user = str(user) )), hawat.const.HAWAT_FLASH_SUCCESS ) - flask.current_app.logger.info("User '{}' successfully logged in.".format(user.login)) + self.logger.info("User '{}' successfully logged in.".format(user.login)) # Redirect user back to original page. return hawat.forms.flask_redirect(flask.url_for('index')) except sqlalchemy.orm.exc.MultipleResultsFound: - flask.current_app.logger.error("Multiple results found for user login '{}'.".format(form.login.data)) - return flask.abort(500) + self.logger.error("Multiple results found for user login '{}'.".format(form.login.data)) + self.abort(500) except sqlalchemy.orm.exc.NoResultFound: - flask.flash( + self.flash( gettext('You have entered wrong login credentials.'), hawat.const.HAWAT_FLASH_FAILURE ) except Exception: # pylint: disable=locally-disabled,broad-except - flask.flash( + self.flash( gettext("Unable to perform developer login as '{}'.".format(form.login.data)), hawat.const.HAWAT_FLASH_FAILURE ) @@ -167,17 +170,17 @@ class LoginView(hawat.base.HawatSimpleView): - context.update( + self.response_context.update( form = form, next = hawat.forms.get_redirect_target() ) - return flask.render_template(self.get_view_template(), **context) + return self.generate_response() #------------------------------------------------------------------------------- -class DevAuthBlueprint(hawat.base.HawatBlueprint): +class DevAuthBlueprint(HawatBlueprint): """ Hawat pluggable module - special developer authentication (*auth_dev*). """ diff --git a/lib/hawat/blueprints/auth_dev/templates/auth_dev/login.html b/lib/hawat/blueprints/auth_dev/templates/auth_dev/login.html index eb7000310d05e36998d08a71d0f8663c519e1e88..3ba404e7214f34059348a6554b2a4e2f2d152d0a 100644 --- a/lib/hawat/blueprints/auth_dev/templates/auth_dev/login.html +++ b/lib/hawat/blueprints/auth_dev/templates/auth_dev/login.html @@ -8,7 +8,7 @@ <form method="POST" action="{{ url_for('auth_dev.login', next = next) }}"> <fieldset> - <legend>{{ get_fa_icon('login') }} {{ hawat_this_view_title }}</legend> + <legend>{{ get_icon('login') }} {{ hawat_current_view.get_view_title() }}</legend> {{ macros_site.render_form_item_select(form.login) }} diff --git a/lib/hawat/blueprints/auth_env/__init__.py b/lib/hawat/blueprints/auth_env/__init__.py index e58e17b82acef1a465089398f9f47e270758b4ff..aafc92cfcdd2177ccfa8aa8583b062f29761b3df 100644 --- a/lib/hawat/blueprints/auth_env/__init__.py +++ b/lib/hawat/blueprints/auth_env/__init__.py @@ -97,17 +97,15 @@ from flask_babel import gettext, lazy_gettext, force_locale from mentat.datatype.sqldb import ItemChangeLogModel import hawat.const -import hawat.base import hawat.db import hawat.forms +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatRenderableView, HawatBlueprint from hawat.models.user import GuiUserModel from hawat.blueprints.auth_env.forms import RegisterUserAccountForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'auth_env' +"""Name of the blueprint as module global constant.""" class RegistrationException(Exception): @@ -131,7 +129,7 @@ def get_login_from_environment(): flask.request.environ.get('REMOTE_USER', None) ) -class LoginView(hawat.base.HawatBaseView): +class LoginView(HTMLViewMixin, HawatRenderableView): """ View responsible for user login via application environment. """ @@ -152,7 +150,7 @@ class LoginView(hawat.base.HawatBaseView): return 'login' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -165,29 +163,29 @@ class LoginView(hawat.base.HawatBaseView): """ user_login = get_login_from_environment() if not user_login: - flask.flash( + self.flash( gettext('User login was not received, unable to perform login process.'), hawat.const.HAWAT_FLASH_FAILURE ) - flask.abort(403) + self.abort(403) dbsess = hawat.db.db_get().session try: user = dbsess.query(GuiUserModel).filter(GuiUserModel.login == user_login).one() except sqlalchemy.orm.exc.MultipleResultsFound: - flask.current_app.logger.error("Multiple results found for user login '{}'.".format(user_login)) - return flask.abort(500) + self.logger.error("Multiple results found for user login '{}'.".format(user_login)) + flask.abort(500) except sqlalchemy.orm.exc.NoResultFound: - flask.flash( + self.flash( gettext('You have entered wrong login credentials.'), hawat.const.HAWAT_FLASH_FAILURE ) - return flask.abort(403) + self.abort(403) if not user.enabled: - flask.flash( + self.flash( flask.Markup( gettext( 'Your user account <strong>%(login)s (%(name)s)</strong> is currently disabled, you are not permitted to log in.', @@ -197,7 +195,7 @@ class LoginView(hawat.base.HawatBaseView): ), hawat.const.HAWAT_FLASH_FAILURE ) - return flask.abort(403) + self.abort(403) flask_login.login_user(user) @@ -213,7 +211,7 @@ class LoginView(hawat.base.HawatBaseView): user.logintime = datetime.datetime.utcnow() dbsess.commit() - flask.flash( + self.flash( flask.Markup( gettext( 'You have been successfully logged in as <strong>%(login)s (%(name)s)</strong>.', @@ -223,13 +221,13 @@ class LoginView(hawat.base.HawatBaseView): ), hawat.const.HAWAT_FLASH_SUCCESS ) - flask.current_app.logger.info("User '{}' successfully logged in.".format(user.login)) + self.logger.info("User '{}' successfully logged in.".format(user.login)) # Redirect user back to original page. return hawat.forms.flask_redirect(flask.url_for('index')) -class RegisterView(hawat.base.HawatDbmodelView): +class RegisterView(HTMLViewMixin, SQLAlchemyViewMixin, HawatRenderableView): """ View responsible for registering new user account into application. """ @@ -250,16 +248,16 @@ class RegisterView(hawat.base.HawatDbmodelView): return 'register' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Register') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('User account registration') @@ -270,7 +268,7 @@ class RegisterView(hawat.base.HawatDbmodelView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -362,22 +360,21 @@ class RegisterView(hawat.base.HawatDbmodelView): Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. Will be called by the *Flask* framework to service the request. """ - context = self.get_template_context() - context.update(apacheenv = flask.request.environ) + self.response_context.update(apacheenv = flask.request.environ) item = None # Attempt to create user object from server environment variables. try: item = self.get_user_from_env() - context.update(item = item) + self.response_context.update(item = item) except RegistrationException as exc: - context.update(failure_reason = exc) - return flask.render_template('auth_env/registration_unavailable.html', **context) + self.response_context.update(failure_reason = exc) + return flask.render_template('auth_env/registration_unavailable.html', **self.response_context) # Perhaps the account already exists. - user = self.dbquery.filter(self.dbmodel.login == item.login).first() + user = self.dbquery().filter(self.dbmodel.login == item.login).first() if user: - flask.flash( + self.flash( flask.Markup( gettext( 'User account <strong>%(item_id)s</strong> already exists.', @@ -386,8 +383,8 @@ class RegisterView(hawat.base.HawatDbmodelView): ), hawat.const.HAWAT_FLASH_WARNING ) - context.update(item = user) - return flask.render_template('auth_env/registration_show.html', **context) + self.response_context.update(item = user) + return flask.render_template('auth_env/registration_show.html', **self.response_context) form = self.get_item_form(item) @@ -395,7 +392,7 @@ class RegisterView(hawat.base.HawatDbmodelView): form_data = form.data if form_data[hawat.const.HAWAT_FORM_ACTION_CANCEL]: - flask.flash( + self.flash( gettext('Account registration canceled.'), hawat.const.HAWAT_FLASH_INFO ) @@ -453,7 +450,7 @@ class RegisterView(hawat.base.HawatDbmodelView): ) flask.current_app.mailer.send(msg) - flask.flash( + self.flash( flask.Markup( gettext( 'User account <strong>%(login)s (%(name)s)</strong> was successfully registered.', @@ -463,10 +460,10 @@ class RegisterView(hawat.base.HawatDbmodelView): ), hawat.const.HAWAT_FLASH_SUCCESS ) - return flask.render_template('auth_env/registration_show.html', **context) + return flask.render_template('auth_env/registration_show.html', **self.response_context) except Exception: # pylint: disable=locally-disabled,broad-except - flask.flash( + self.flash( gettext('Unable to register new user account.'), hawat.const.HAWAT_FLASH_FAILURE ) @@ -475,17 +472,17 @@ class RegisterView(hawat.base.HawatDbmodelView): gettext('Unable to register new user account.'), ) - context.update( + self.response_context.update( form_url = flask.url_for('{}.{}'.format(self.module_name, self.get_view_name())), form = form ) - return flask.render_template(self.get_view_template(), **context) + return flask.render_template(self.get_view_template(), **self.response_context) #------------------------------------------------------------------------------- -class AuthBlueprint(hawat.base.HawatBlueprint): +class AuthBlueprint(HawatBlueprint): """ Hawat pluggable module - environment based authentication. """ diff --git a/lib/hawat/blueprints/auth_env/templates/auth_env/registration.html b/lib/hawat/blueprints/auth_env/templates/auth_env/registration.html index 276b9d3e00c36900cfd44333cb6531e80919f494..66d81ffb816ec5957acb243c65ae45e7143523c2 100644 --- a/lib/hawat/blueprints/auth_env/templates/auth_env/registration.html +++ b/lib/hawat/blueprints/auth_env/templates/auth_env/registration.html @@ -13,7 +13,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {{ macros_site.render_form_item_static(gettext('Login:'), item.login) }} diff --git a/lib/hawat/blueprints/auth_env/templates/auth_env/registration_unavailable.html b/lib/hawat/blueprints/auth_env/templates/auth_env/registration_unavailable.html index 679ee92fbe4c82edab6b88ed07c1a8755c723e0b..e9c6e2e340b05c9b715c1208af2004fc559bd32c 100644 --- a/lib/hawat/blueprints/auth_env/templates/auth_env/registration_unavailable.html +++ b/lib/hawat/blueprints/auth_env/templates/auth_env/registration_unavailable.html @@ -5,7 +5,7 @@ <div class="row"> <div class="col-lg-12"> <div class="well col-md-6 col-md-offset-3"> - <h1>{{ get_fa_icon('alert-danger') }} {{ gettext('Registration unavailable') }}</h1> + <h1>{{ get_icon('alert-danger') }} {{ gettext('Registration unavailable') }}</h1> <p class="lead"> {{ gettext('We are sorry, but the automated registration is not available for your identity provider.') }} {{ gettext('Registration process failed for the following reason:') }} diff --git a/lib/hawat/blueprints/changelogs/__init__.py b/lib/hawat/blueprints/changelogs/__init__.py index 1df94968b4fd48cf0963aad9c48ed62f6ec6fd80..ebecfd57cf35a4cbfea7b88182c7513db9c32fce 100644 --- a/lib/hawat/blueprints/changelogs/__init__.py +++ b/lib/hawat/blueprints/changelogs/__init__.py @@ -31,18 +31,17 @@ from flask_babel import gettext, lazy_gettext # from mentat.datatype.sqldb import ItemChangeLogModel -import hawat.base -import hawat.db +import hawat.acl +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView,\ + HawatItemShowView, HawatBlueprint from hawat.blueprints.changelogs.forms import ItemChangeLogSearchForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'changelogs' +"""Name of the blueprint as module global constant.""" -class SearchView(hawat.base.HawatSearchView): +class SearchView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView): """ General item changelog record listing. """ @@ -53,16 +52,16 @@ class SearchView(hawat.base.HawatSearchView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Changelogs') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Search item changelogs') @@ -71,7 +70,7 @@ class SearchView(hawat.base.HawatSearchView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return ItemChangeLogModel @@ -83,35 +82,35 @@ class SearchView(hawat.base.HawatSearchView): return ItemChangeLogSearchForm(request_args, meta = {'csrf': False}) @staticmethod - def build_query(query, model, args, context): + def build_query(query, model, form_args): """ *Interface implementation* of :py:func:`hawat.base.HawatSearchView.build_query`. """ # Adjust query based on lower time boudary selection. - if 'dt_from' in args and args['dt_from']: - query = query.filter(model.createtime >= args['dt_from']) + if 'dt_from' in form_args and form_args['dt_from']: + query = query.filter(model.createtime >= form_args['dt_from']) # Adjust query based on upper time boudary selection. - if 'dt_to' in args and args['dt_to']: - query = query.filter(model.createtime <= args['dt_to']) + if 'dt_to' in form_args and form_args['dt_to']: + query = query.filter(model.createtime <= form_args['dt_to']) # Adjust query based on changelog author selection. - if 'authors' in args and args['authors']: - query = query.filter(model.author_id.in_([x.id for x in args['authors']])) + if 'authors' in form_args and form_args['authors']: + query = query.filter(model.author_id.in_([x.id for x in form_args['authors']])) # Adjust query based on changelog operation selection. - if 'operations' in args and args['operations']: - query = query.filter(model.operation.in_(args['operations'])) + if 'operations' in form_args and form_args['operations']: + query = query.filter(model.operation.in_(form_args['operations'])) # Adjust query based on changelog model selection. - if 'imodel' in args and args['imodel']: + if 'imodel' in form_args and form_args['imodel']: # Adjust query based on changelog model ID selection. - if 'imodel_id' in args and args['imodel_id']: - query = query.filter(model.model == args['imodel'], model.imodel_id == args['imodel_id']) + if 'imodel_id' in form_args and form_args['imodel_id']: + query = query.filter(model.model == form_args['imodel'], model.imodel_id == form_args['imodel_id']) else: - query = query.filter(model.model == args['imodel']) + query = query.filter(model.model == form_args['imodel']) # Return the result sorted by creation time in descending order. return query.order_by(model.createtime.desc()) @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ @@ -152,7 +151,7 @@ class SearchView(hawat.base.HawatSearchView): return context_action_menu -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed network record view. """ @@ -163,7 +162,7 @@ class ShowView(hawat.base.HawatItemShowView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -177,9 +176,9 @@ class ShowView(hawat.base.HawatItemShowView): return lazy_gettext('View details of item changelog record "%(item)s"', item = str(item)) @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show item changelog record details') @@ -188,7 +187,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return ItemChangeLogModel @@ -223,7 +222,7 @@ class ShowView(hawat.base.HawatItemShowView): #------------------------------------------------------------------------------- -class ItemChangeLogsBlueprint(hawat.base.HawatBlueprint): +class ItemChangeLogsBlueprint(HawatBlueprint): """ Hawat pluggable module - changelogs. """ diff --git a/lib/hawat/blueprints/changelogs/templates/changelogs/search.html b/lib/hawat/blueprints/changelogs/templates/changelogs/search.html index 7e173aee18b9e565f6a3d2bb59931d450a5fd880..6021c3d3098832633ee193320cda9184762b0099 100644 --- a/lib/hawat/blueprints/changelogs/templates/changelogs/search.html +++ b/lib/hawat/blueprints/changelogs/templates/changelogs/search.html @@ -4,26 +4,26 @@ <div class="row"> <div class="col-sm-6"> - {{ macros_site.render_form_item_datetime(search_form.dt_from, 'datetimepicker-hm-from') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_from, 'datetimepicker-hm-from') }} </div> <div class="col-sm-6"> - {{ macros_site.render_form_item_datetime(search_form.dt_to, 'datetimepicker-hm-to') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_to, 'datetimepicker-hm-to') }} </div> </div> <div class="row"> <div class="col-sm-6"> - {{ macros_site.render_form_item_select(search_form.authors) }} + {{ macros_site.render_form_item_select(g.search_form.authors) }} </div> <div class="col-sm-6"> - {{ macros_site.render_form_item_select(search_form.operations) }} + {{ macros_site.render_form_item_select(g.search_form.operations) }} </div> </div> <div class="row"> <div class="col-sm-6"> - {{ macros_site.render_form_item_select(search_form.imodel) }} + {{ macros_site.render_form_item_select(g.search_form.imodel) }} </div> <div class="col-sm-6"> - {{ macros_site.render_form_item_select(search_form.imodel_id) }} + {{ macros_site.render_form_item_select(g.search_form.imodel_id) }} </div> </div> @@ -31,6 +31,6 @@ {%- block contentsearchresult %} - {{ macros_site.render_changelog_records(items, context_action_menu) }} + {{ macros_site.render_changelog_records(items, hawat_current_view.get_context_action_menu()) }} {%- endblock contentsearchresult %} diff --git a/lib/hawat/blueprints/changelogs/templates/changelogs/show.html b/lib/hawat/blueprints/changelogs/templates/changelogs/show.html index 4f17c03d614b9adabe964e9a560f61878240392b..06a5ece793b3ab423504074cab190dce051fdc60 100644 --- a/lib/hawat/blueprints/changelogs/templates/changelogs/show.html +++ b/lib/hawat/blueprints/changelogs/templates/changelogs/show.html @@ -4,8 +4,9 @@ <div class="row"> <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ item.__str__() }}</h3> <br> diff --git a/lib/hawat/blueprints/dbstatus/__init__.py b/lib/hawat/blueprints/dbstatus/__init__.py index 38d9d63d5f929404fd035e2162b911751bae9ff3..4128a146fa24f67f1fb4571b0173013d511ec928 100644 --- a/lib/hawat/blueprints/dbstatus/__init__.py +++ b/lib/hawat/blueprints/dbstatus/__init__.py @@ -57,27 +57,31 @@ __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea import datetime +# +# Flask related modules. +# from flask_babel import lazy_gettext from sqlalchemy import or_ +# +# Custom modules. +# import mentat.const import mentat.system from mentat.datatype.sqldb import GroupModel, FilterModel, SettingsReportingModel -import hawat.base import hawat.menu import hawat.acl import hawat.events +from hawat.base import HawatSimpleView, SQLAlchemyViewMixin, HTMLViewMixin, HawatBlueprint from hawat.models.user import GuiUserModel -# -# Name of the blueprint as global constant. -# BLUEPRINT_NAME = 'dbstatus' +"""Name of the blueprint as module global constant.""" -class ViewView(hawat.base.HawatSimpleView): +class ViewView(HTMLViewMixin, HawatSimpleView): """ Application view providing access event database status information. """ @@ -100,16 +104,16 @@ class ViewView(hawat.base.HawatSimpleView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Database status') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Database status') @@ -127,40 +131,35 @@ class ViewView(hawat.base.HawatSimpleView): stats['list_{}'.format(key)] = list(sorted(stats[key].keys())) return stats - def get_template_context(self): + def do_before_response(self, **kwargs): """ - Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ - context = super().get_template_context() - - context.update( + self.response_context.update( database_status_events = hawat.events.db_get().database_status(), sw_versions = mentat.system.analyze_versions() ) dbstatistics_events = { 'total_bytes': { - x: y['total_bytes'] for x, y in context['database_status_events']['tables'].items() + x: y['total_bytes'] for x, y in self.response_context['database_status_events']['tables'].items() }, 'table_bytes': { - x: y['table_bytes'] for x, y in context['database_status_events']['tables'].items() + x: y['table_bytes'] for x, y in self.response_context['database_status_events']['tables'].items() }, 'index_bytes': { - x: y['index_bytes'] for x, y in context['database_status_events']['tables'].items() + x: y['index_bytes'] for x, y in self.response_context['database_status_events']['tables'].items() }, 'row_estimate': { - x: y['row_estimate'] for x, y in context['database_status_events']['tables'].items() + x: y['row_estimate'] for x, y in self.response_context['database_status_events']['tables'].items() } } - context.update( + self.response_context.update( database_statistics_events = self.calculate_secondary_stats(dbstatistics_events) ) - return context - -class DashboardView(hawat.base.HawatSimpleView): +class DashboardView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSimpleView): # pylint: disable=locally-disabled,abstract-method """ View responsible for presenting database dashboard. """ @@ -176,16 +175,16 @@ class DashboardView(hawat.base.HawatSimpleView): return 'dashboard' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Object management') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Object management dashboards') @@ -198,39 +197,24 @@ class DashboardView(hawat.base.HawatSimpleView): #--------------------------------------------------------------------------- - @property - def dbsession(self): - """ - This property contains the reference to current *SQLAlchemy* database session. - """ - return hawat.db.db_get().session - def dbquery(self, dbmodel): + def do_before_response(self, **kwargs): """ - This property contains the reference to *SQLAlchemy* query object appropriate - for particular ``dbmodel`` property. - """ - return self.dbsession.query(dbmodel) - - def get_template_context(self): + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ - *Hook method*. Will be called before rendering the template. - """ - context = super().get_template_context() - - context['users_disabled'] = self.dbquery(GuiUserModel).\ + self.response_context['users_disabled'] = self.dbquery(GuiUserModel).\ filter(GuiUserModel.enabled == False).\ order_by(GuiUserModel.createtime.desc()).\ all() - context['users_nomemberships'] = self.dbquery(GuiUserModel).\ + self.response_context['users_nomemberships'] = self.dbquery(GuiUserModel).\ filter(~GuiUserModel.memberships.any()).\ order_by(GuiUserModel.createtime.desc()).\ all() - context['users_noroles'] = self.dbquery(GuiUserModel).\ + self.response_context['users_noroles'] = self.dbquery(GuiUserModel).\ filter(GuiUserModel.roles == []).\ order_by(GuiUserModel.createtime.desc()).\ all() - context['users_nologin'] = self.dbquery(GuiUserModel).\ + self.response_context['users_nologin'] = self.dbquery(GuiUserModel).\ filter( or_( GuiUserModel.logintime.is_(None), @@ -240,61 +224,61 @@ class DashboardView(hawat.base.HawatSimpleView): order_by(GuiUserModel.createtime.desc()).\ all() - context['groups_disabled'] = self.dbquery(GroupModel).\ + self.response_context['groups_disabled'] = self.dbquery(GroupModel).\ filter(GroupModel.enabled == False).\ order_by(GroupModel.createtime.desc()).\ all() - context['groups_nomembers'] = self.dbquery(GroupModel).\ + self.response_context['groups_nomembers'] = self.dbquery(GroupModel).\ filter(~GroupModel.members.any()).\ order_by(GroupModel.createtime.desc()).\ all() - context['groups_nomanagers'] = self.dbquery(GroupModel).\ + self.response_context['groups_nomanagers'] = self.dbquery(GroupModel).\ filter(~GroupModel.members.any()).\ order_by(GroupModel.createtime.desc()).\ all() - context['groups_nonetworks'] = self.dbquery(GroupModel).\ + self.response_context['groups_nonetworks'] = self.dbquery(GroupModel).\ filter(~GroupModel.networks.any()).\ order_by(GroupModel.createtime.desc()).\ all() - context['filters_disabled'] = self.dbquery(FilterModel).\ + self.response_context['filters_disabled'] = self.dbquery(FilterModel).\ filter(FilterModel.enabled == False).\ order_by(FilterModel.createtime.desc()).\ all() - context['filters_nohits'] = self.dbquery(FilterModel).\ + self.response_context['filters_nohits'] = self.dbquery(FilterModel).\ filter(FilterModel.hits == 0).\ order_by(FilterModel.createtime.desc()).\ all() - context['filters_future'] = self.dbquery(FilterModel).\ + self.response_context['filters_future'] = self.dbquery(FilterModel).\ filter(FilterModel.valid_from > (datetime.datetime.utcnow() + datetime.timedelta(days = 14))).\ order_by(FilterModel.createtime.desc()).\ all() - context['filters_expired'] = self.dbquery(FilterModel).\ + self.response_context['filters_expired'] = self.dbquery(FilterModel).\ filter(FilterModel.valid_to < datetime.datetime.utcnow()).\ order_by(FilterModel.createtime.desc()).\ all() - context['settingsrep_muted'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_muted'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.mute == True).\ order_by(SettingsReportingModel.createtime.desc()).\ all() - context['settingsrep_redirected'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_redirected'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.redirect == True).\ order_by(SettingsReportingModel.createtime.desc()).\ all() - context['settingsrep_modenone'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_modenone'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.mode == mentat.const.REPORTING_MODE_NONE).\ order_by(SettingsReportingModel.createtime.desc()).\ all() - context['settingsrep_attachcsv'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_attachcsv'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.attachments.in_([mentat.const.REPORTING_ATTACH_CSV, mentat.const.REPORTING_ATTACH_ALL])).\ order_by(SettingsReportingModel.createtime.desc()).\ all() - context['settingsrep_emailscust'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_emailscust'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.emails != []).\ order_by(SettingsReportingModel.createtime.desc()).\ all() - context['settingsrep_timingcust'] = self.dbquery(SettingsReportingModel).\ + self.response_context['settingsrep_timingcust'] = self.dbquery(SettingsReportingModel).\ filter(SettingsReportingModel.timing == mentat.const.REPORTING_TIMING_CUSTOM).\ order_by(SettingsReportingModel.createtime.desc()).\ all() @@ -339,7 +323,7 @@ class DashboardView(hawat.base.HawatSimpleView): icon = 'action-delete-user', legend = lambda x: lazy_gettext('Delete user account "%(item)s"', item = x.login) ) - context['context_action_menu_user'] = action_menu + self.response_context['context_action_menu_user'] = action_menu action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -378,7 +362,7 @@ class DashboardView(hawat.base.HawatSimpleView): endpoint = 'groups.delete', legend = lambda x: lazy_gettext('Delete group "%(item)s"', item = str(x)) ) - context['context_action_menu_group'] = action_menu + self.response_context['context_action_menu_group'] = action_menu action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -417,15 +401,13 @@ class DashboardView(hawat.base.HawatSimpleView): endpoint = 'filters.delete', legend = lambda x: lazy_gettext('Delete reporting filter "%(item)s"', item = x.name) ) - context['context_action_menu_filter'] = action_menu - - return context + self.response_context['context_action_menu_filter'] = action_menu #------------------------------------------------------------------------------- -class DatabaseStatusBlueprint(hawat.base.HawatBlueprint): +class DatabaseStatusBlueprint(HawatBlueprint): """ Hawat pluggable module - database status. """ diff --git a/lib/hawat/blueprints/dbstatus/templates/dbstatus/dashboard.html b/lib/hawat/blueprints/dbstatus/templates/dbstatus/dashboard.html index fc1ee6728c6d36afab3414b73866589698eee619..96ac09d917be3765a53692de8efa26e719b34c7c 100644 --- a/lib/hawat/blueprints/dbstatus/templates/dbstatus/dashboard.html +++ b/lib/hawat/blueprints/dbstatus/templates/dbstatus/dashboard.html @@ -8,22 +8,22 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-users" aria-controls="tab-users" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-users') }} {{ gettext('Users') }}</strong> + <strong>{{ get_icon('module-users') }} {{ gettext('Users') }}</strong> </a> </li> <li role="presentation"> <a href="#tab-groups" aria-controls="tab-groups" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-groups') }} {{ gettext('Groups') }}</strong> + <strong>{{ get_icon('module-groups') }} {{ gettext('Groups') }}</strong> </a> </li> <li role="presentation"> <a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-filters') }} {{ gettext('Filters') }}</strong> + <strong>{{ get_icon('module-filters') }} {{ gettext('Filters') }}</strong> </a> </li> <li role="presentation"> <a href="#tab-settingsrep" aria-controls="tab-settingsrep" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-settings-reporting') }} {{ gettext('Reporting settings') }}</strong> + <strong>{{ get_icon('module-settings-reporting') }} {{ gettext('Reporting settings') }}</strong> </a> </li> </ul> diff --git a/lib/hawat/blueprints/dbstatus/templates/dbstatus/view.html b/lib/hawat/blueprints/dbstatus/templates/dbstatus/view.html index b2d4d607d673a45f477435f97af51eaf417c09e7..ed6cfd340ec5eaec9360564002681923a4c6c85b 100644 --- a/lib/hawat/blueprints/dbstatus/templates/dbstatus/view.html +++ b/lib/hawat/blueprints/dbstatus/templates/dbstatus/view.html @@ -8,7 +8,7 @@ <li><a href="{{ url_for('index') }}">{{ gettext('Home') }}</a></li> <li class="active">{{ gettext('Database status') }}</li> </ol> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <!-- Nav tabs --> @@ -209,7 +209,7 @@ </td> <td> {{ database_status_events['pg_settings'][setting].category }} - <span data-toggle="tooltip" title="{{ database_status_events['pg_settings'][setting].short_desc }}">{{ get_fa_icon('help')}}</span> + <span data-toggle="tooltip" title="{{ database_status_events['pg_settings'][setting].short_desc }}">{{ get_icon('help')}}</span> </td> </tr> {%- endfor %} diff --git a/lib/hawat/blueprints/design/templates/_layout.html b/lib/hawat/blueprints/design/templates/_layout.html index d267d5f85cbad6cabc986c8bb2e02bc32cc9263e..ffa0e9b230ea39df7ded4605596af38a988e8ec7 100644 --- a/lib/hawat/blueprints/design/templates/_layout.html +++ b/lib/hawat/blueprints/design/templates/_layout.html @@ -1,4 +1,5 @@ {%- import '_macros_site.html' as macros_site with context -%} +{%- import '_macros_page.html' as macros_page with context -%} <!DOCTYPE html> <html lang="en"> <head> @@ -31,7 +32,7 @@ <!-- Favicon --> <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> - <title>{% block title %}{{ hawat_this_view_title }}{% endblock %} - Mentat</title> + <title>{% block title %}{{ hawat_current_view.get_view_title() }}{% endblock %} - Mentat</title> {%- endblock head %} <script> @@ -56,7 +57,7 @@ <div id="ajax-loader-overlay"> <div id="ajax-loader"> - {{ get_fa_icon('ajax-loader') }} + {{ get_icon('ajax-loader') }} <hr> <h3><strong>{{ gettext('... LOADING ...') }}</strong></h3> </div> @@ -83,7 +84,7 @@ {{ macros_site.render_submenu_auth() }} {{ macros_site.render_locale_switcher() }} <li{% if request.endpoint == "help.view" %} class="active"{% endif %}> - <a href="{{ url_for('help.view') }}" data-toggle="tooltip" data-placement="bottom" title="{{ gettext('Browse help') }}">{{ get_fa_icon('help') }}<span class="hidden-sm hidden-md"> {{ gettext('Help') }}</span></a> + <a href="{{ url_for('help.view') }}" data-toggle="tooltip" data-placement="bottom" title="{{ gettext('Browse help') }}">{{ get_icon('help') }}<span class="hidden-sm hidden-md"> {{ gettext('Help') }}</span></a> </li> </ul> </div> <!-- #navbar --> @@ -118,8 +119,8 @@ <!-- Footer container - BEGIN -----------------------------------------> <div class="container-fluid" id="footer"> <hr> - <span data-toggle="tooltip" title="{{ gettext('Page generated at: ') }}">{{ get_fa_icon('clock') }}</span><span class="hidden-sm"> <em>{{ gettext('Page generated at: ') }}</em></span> {{ babel_format_datetime(current_datetime_utc) }} | - <span data-toggle="tooltip" title="{{ gettext('Page generated in: ') }}">{{ get_fa_icon('stopwatch') }}</span><span class="hidden-sm"> <em>{{ gettext('Page generated in: ') }}</em></span> {{ get_timedelta(g.requeststart) }} + <span data-toggle="tooltip" title="{{ gettext('Page generated at: ') }}">{{ get_icon('clock') }}</span><span class="hidden-sm"> <em>{{ gettext('Page generated at: ') }}</em></span> {{ babel_format_datetime(current_datetime_utc) }} | + <span data-toggle="tooltip" title="{{ gettext('Page generated in: ') }}">{{ get_icon('stopwatch') }}</span><span class="hidden-sm"> <em>{{ gettext('Page generated in: ') }}</em></span> {{ get_timedelta(g.requeststart) }} <br> <span data-toggle="tooltip" title="{{ hawat_bversion_full }}"> Hawat {{ hawat_bversion }} diff --git a/lib/hawat/blueprints/design/templates/_layout_list.html b/lib/hawat/blueprints/design/templates/_layout_list.html index ef4d235e53ff7e7eae4372ee547fb6683df28009..c3dd2a275e48f29a2d7084bff0a9dd6f7c3381e5 100644 --- a/lib/hawat/blueprints/design/templates/_layout_list.html +++ b/lib/hawat/blueprints/design/templates/_layout_list.html @@ -35,15 +35,13 @@ <div class="row"> <div class="col-lg-12"> - {%- if breadcrumbs_menu %} - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - {%- endif %} - <h2>{{ hawat_this_view_title }}</h2> - {%- if action_menu %} + + {{ macros_page.render_breadcrumbs() }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions() }} </div> - {%- endif %} <br> <hr> diff --git a/lib/hawat/blueprints/design/templates/_layout_search.html b/lib/hawat/blueprints/design/templates/_layout_search.html index 93df7beef2e1d95fe87c5edb2c4eddc718fed403..3f1458207abefa1d56fda3c1329252a665230fda 100644 --- a/lib/hawat/blueprints/design/templates/_layout_search.html +++ b/lib/hawat/blueprints/design/templates/_layout_search.html @@ -4,22 +4,22 @@ <div class="row"> <div class="col-lg-12"> - {%- if breadcrumbs_menu %} - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - {%- endif %} + + {{ macros_page.render_breadcrumbs() }} + <!-- Search form - BEGIN ----------------------------------> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> - <form method="GET" class="form" action="{{ url_for(hawat_this_view_endpoint) }}"> + <form method="GET" class="form" action="{{ url_for(request.endpoint) }}"> {% block contentsearchform %}{% endblock contentsearchform %} <hr> <div class="btn-toolbar" role="toolbar" aria-label="{{ gettext('Form submission buttons') }}"> <div class="btn-group" role="group"> - {{ search_form.submit(class_='btn btn-primary') }} - <a role="button" class="btn btn-default" href="{{ url_for(hawat_this_view_endpoint) }}">{{ gettext('Clear') }}</a> + {{ g.search_form.submit(class_='btn btn-primary') }} + <a role="button" class="btn btn-default" href="{{ url_for(request.endpoint) }}">{{ gettext('Clear') }}</a> </div> </div> </form> @@ -32,8 +32,9 @@ {%- if searched %} {%- if items %} - {{ macros_site.render_pager(hawat_this_view_endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} + {{ macros_site.render_pager(request.endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} + <!-- Search results - BEGIN ---------------------------------------> <div class="row"> <div class="col-lg-12"> @@ -41,8 +42,9 @@ </div> <!-- /.col-lg-12 --> </div> <!-- /.row --> + <!-- Search results - END -----------------------------------------> - {{ macros_site.render_pager(hawat_this_view_endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} + {{ macros_site.render_pager(request.endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} {%- if permission_can('developer') %} <hr> @@ -61,7 +63,7 @@ {%- if permission_can('developer') %} <hr> - {{ macros_site.render_raw_var('request_args', request_args) }} + {{ macros_site.render_raw_var('request_args', request.args) }} {{ macros_site.render_raw_var('query_params', query_params) }} {%- endif %} diff --git a/lib/hawat/blueprints/design/templates/_macros_page.html b/lib/hawat/blueprints/design/templates/_macros_page.html new file mode 100644 index 0000000000000000000000000000000000000000..bc67b5b48956d03edef559a490da08a1cc83a6ca --- /dev/null +++ b/lib/hawat/blueprints/design/templates/_macros_page.html @@ -0,0 +1,114 @@ +{#- ---------------------------------------------------------------------------- + + Macro for rendering the breadcrumb menu for currently active view. + +----------------------------------------------------------------------------- #} + +{%- macro render_breadcrumbs(context_item = None) %} + {%- set breadcrumbs_menu = hawat_current_view.get_breadcrumbs_menu(context_item) %} + {%- if breadcrumbs_menu %} + <!-- Breadcrumbs menu widget - BEGIN ----------------------> + <ol class="breadcrumb"> + {%- for menu_item in breadcrumbs_menu.get_menu(context_item) %} + <li{% if menu_item.is_active(request) %} class="active"{% endif %}> + {%- if menu_item.is_active(request) %} + {{ menu_item.get_title(context_item) }} + {%- else %} + <a href="{{ menu_item.get_url(context_item) }}"> + {{ menu_item.get_title(context_item) }} + </a> + {%- endif %} + </li> + {%- endfor %} + </ol> + <!-- Breadcrumbs menu widget - END ------------------------> + {%- else %} + <!-- Breadcrumbs menu widget is empty on this page --------> + {%- endif %} +{%- endmacro %} + +{#- ---------------------------------------------------------------------------- + + Macro for rendering the action menu for currently active view. + +----------------------------------------------------------------------------- #} + +{%- macro render_menu_actions(context_item = None) %} + {%- set action_menu = hawat_current_view.get_action_menu(context_item) %} + {%- if action_menu %} + {%- set menu_item_list = action_menu.get_menu(context_item) %} + {%- if menu_item_list %} + <!-- Action menu widget - BEGIN -----------------------> + <div class="btn-toolbar" role="toolbar" aria-label="{{ gettext('Action toolbar') }}"> + <div class="btn-group btn-group-sm" role="group" aria-label="{{ gettext('Action buttons') }}"> + {%- for menu_item in menu_item_list %} + {%- if menu_item.type == 'submenu' %} + <a data-toggle="dropdown" role="button" aria-haspopup="true" href="#" class="btn btn-default btn-sm dropdown-toggle" aria-expanded="false"> + <span{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> + </span> + </a> + <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> + {%- for submenu_item in menu_item.get_menu() %} + <li> + <a href="{{ submenu_item.get_url() }}"{% if submenu_item.get_legend() %} data-toggle="tooltip" title="{{ submenu_item.get_legend() }}"{% endif %}> + {% if submenu_item.get_icon() %}{{ get_icon(submenu_item.get_icon()) }}{% endif %}{% if submenu_item.get_title() %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title() }}</span>{% else %} {{ submenu_item.get_title() }}{% endif %}{% endif %} + </a> + </li> + {%- endfor %} + </ul> + {%- else %} + <a role="button" class="btn btn-default btn-sm" href="{{ menu_item.get_url() }}"{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} + </a> + {%- endif %} + {%- endfor %} + </div> + </div> + <!-- Action menu widget - END -------------------------> + {%- endif %} + {%- endif %} +{%- endmacro %} + +{#- ---------------------------------------------------------------------------- + + Macro for rendering the context item action menus. + +----------------------------------------------------------------------------- #} + +{%- macro render_menu_context_actions(context_item, cssclass = '') %} + {%- set action_menu = hawat_current_view.get_context_action_menu() %} + {%- if action_menu %} + {%- set menu_item_list = action_menu.get_menu(context_item) %} + {%- if menu_item_list %} + <!-- Item context action menu widget - BEGIN ------------------> + <div class="btn-toolbar{% if cssclass %} {{ cssclass }}{% endif %}" role="toolbar" aria-label="{{ gettext('Item context action toolbar') }}"> + <div class="btn-group btn-group-sm" role="group" aria-label="{{ gettext('Item context action buttons') }}"> + {%- for menu_item in menu_item_list %} + {%- if menu_item.type == 'submenu' %} + <a data-toggle="dropdown" role="button" aria-haspopup="true" href="#" class="btn btn-default btn-sm dropdown-toggle" aria-expanded="false"> + <span{% if menu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ menu_item.get_legend(context_item) }}"{% endif %}> + {% if menu_item.get_icon(context_item) %}{{ get_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} <span class="caret"></span> + </span> + </a> + <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> + {%- for submenu_item in menu_item.get_menu(context_item) %} + <li> + <a href="{{ submenu_item.get_url(context_item) }}"{% if submenu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ submenu_item.get_legend(context_item) }}"{% endif %}> + {% if submenu_item.get_icon(context_item) %}{{ get_icon(submenu_item.get_icon(context_item)) }}{% endif %}{% if submenu_item.get_title(context_item) %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title(context_item) }}</span>{% else %} {{ submenu_item.get_title(context_item) }}{% endif %}{% endif %} + </a> + </li> + {%- endfor %} + </ul> + {%- else %} + <a role="button" class="btn btn-default btn-sm" href="{{ menu_item.get_url(context_item) }}"{% if menu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ menu_item.get_legend(context_item) }}"{% endif %}> + {% if menu_item.get_icon(context_item) %}{{ get_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} + </a> + {%- endif %} + {%- endfor %} + </div> + </div> + <!-- Item context action menu widget - END --------------------> + {%- endif %} + {%- endif %} +{%- endmacro %} diff --git a/lib/hawat/blueprints/design/templates/_macros_site.html b/lib/hawat/blueprints/design/templates/_macros_site.html index df6c5b90ab56d94bdda25112fe4f5660ef44e0b3..ae994dc252a0c479f3e0d06891855b2a4ecc4260 100644 --- a/lib/hawat/blueprints/design/templates/_macros_site.html +++ b/lib/hawat/blueprints/design/templates/_macros_site.html @@ -20,7 +20,7 @@ <li{% if menu_item_submenu %} class="dropdown dropdown-submenu"{% elif menu_item.is_active(request) %} class="active"{% endif %}> {%- if menu_item.type == 'submenu' %} <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> {%- if menu_item_submenu %} <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> {{ loop(menu_item_submenu) }} @@ -29,7 +29,7 @@ </a> {%- else %} <a href="{% if menu_item.is_active(request) %}#{% else %}{{ menu_item.get_url() }}{% endif %}"{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} </a> {%- endif %} </li> @@ -51,7 +51,7 @@ <!-- Authenticated menu widget - BEGIN ----------------> <li class="dropdown dropdown-submenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> - {%- if current_user.has_role('admin') %}{{ get_fa_icon('role-admin') }}{% elif current_user.has_role('maintainer') %}{{ get_fa_icon('role-maintainer') }}{% elif current_user.has_role('developer') %}{{ get_fa_icon('role-developer') }}{% else %}{{ get_fa_icon('role-user') }}{% endif %}<span class="hidden-sm hidden-md"> {{ current_user.login }}</span> <span class="caret"></span> + {%- if current_user.has_role('admin') %}{{ get_icon('role-admin') }}{% elif current_user.has_role('maintainer') %}{{ get_icon('role-maintainer') }}{% elif current_user.has_role('developer') %}{{ get_icon('role-developer') }}{% else %}{{ get_icon('role-user') }}{% endif %}<span class="hidden-sm hidden-md"> {{ current_user.login }}</span> <span class="caret"></span> </a> <ul class="dropdown-menu"> {%- for menu_item in hawat_current_menu_auth.get_menu() recursive %} @@ -63,7 +63,7 @@ <li{% if menu_item_submenu %} class="dropdown dropdown-submenu"{% elif menu_item.is_active(request) %} class="active"{% endif %}> {%- if menu_item.type == 'submenu' %} <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> {%- if menu_item_submenu %} <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> {{ loop(menu_item_submenu) }} @@ -72,14 +72,14 @@ </a> {%- else %} <a href="{% if menu_item.is_active(request) %}#{% else %}{{ menu_item.get_url() }}{% endif %}"{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} </a> {%- endif %} </li> {%- endfor %} <li role="separator" class="divider"></li> <li> - <a href="{{ url_for('logout') }}" title="{{ gettext('Logout') }}">{{ get_fa_icon('logout') }} {{ gettext('Logout') }}</a> + <a href="{{ url_for('logout') }}" title="{{ gettext('Logout') }}">{{ get_icon('logout') }} {{ gettext('Logout') }}</a> </li> </ul> <!-- /.dropdown-menu --> </li> <!-- /.dropdown --> @@ -88,7 +88,7 @@ <!-- Anonymous menu widget - BEGIN --------------------> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="{{ gettext('Anonymous') }}"> - {{ get_fa_icon('role-anonymous') }}<span class="hidden-sm hidden-md"> {{ gettext('Anonymous') }}</span> <span class="caret"></span> + {{ get_icon('role-anonymous') }}<span class="hidden-sm hidden-md"> {{ gettext('Anonymous') }}</span> <span class="caret"></span> </a> <ul class="dropdown-menu"> {%- for menu_item in hawat_current_menu_anon.get_menu() recursive %} @@ -100,7 +100,7 @@ <li{% if menu_item_submenu %} class="dropdown dropdown-submenu"{% elif menu_item.is_active(request) %} class="active"{% endif %}> {%- if menu_item.type == 'submenu' %} <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> {%- if menu_item_submenu %} <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> {{ loop(menu_item_submenu) }} @@ -109,7 +109,7 @@ </a> {%- else %} <a href="{% if menu_item.is_active(request) %}#{% else %}{{ menu_item.get_url() }}{% endif %}"{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} </a> {%- endif %} </li> @@ -137,21 +137,21 @@ {%- if menu_item.type == 'submenu' %} <a data-toggle="dropdown" role="button" aria-haspopup="true" href="#" class="btn btn-default btn-sm dropdown-toggle" aria-expanded="false"> <span{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} <span class="caret"></span> </span> </a> <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> {%- for submenu_item in menu_item.get_menu() %} <li> <a href="{{ submenu_item.get_url() }}"{% if submenu_item.get_legend() %} data-toggle="tooltip" title="{{ submenu_item.get_legend() }}"{% endif %}> - {% if submenu_item.get_icon() %}{{ get_fa_icon(submenu_item.get_icon()) }}{% endif %}{% if submenu_item.get_title() %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title() }}</span>{% else %} {{ submenu_item.get_title() }}{% endif %}{% endif %} + {% if submenu_item.get_icon() %}{{ get_icon(submenu_item.get_icon()) }}{% endif %}{% if submenu_item.get_title() %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title() }}</span>{% else %} {{ submenu_item.get_title() }}{% endif %}{% endif %} </a> </li> {%- endfor %} </ul> {%- else %} <a role="button" class="btn btn-default btn-sm" href="{{ menu_item.get_url() }}"{% if menu_item.get_legend() %} data-toggle="tooltip" title="{{ menu_item.get_legend() }}"{% endif %}> - {% if menu_item.get_icon() %}{{ get_fa_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} + {% if menu_item.get_icon() %}{{ get_icon(menu_item.get_icon()) }}{% endif %}{% if menu_item.get_title() %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title() }}</span>{% else %} {{ menu_item.get_title() }}{% endif %}{% endif %} </a> {%- endif %} {%- endfor %} @@ -178,21 +178,21 @@ {%- if menu_item.type == 'submenu' %} <a data-toggle="dropdown" role="button" aria-haspopup="true" href="#" class="btn btn-default btn-sm dropdown-toggle" aria-expanded="false"> <span{% if menu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ menu_item.get_legend(context_item) }}"{% endif %}> - {% if menu_item.get_icon(context_item) %}{{ get_fa_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} <span class="caret"></span> + {% if menu_item.get_icon(context_item) %}{{ get_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} <span class="caret"></span> </span> </a> <ul class="dropdown-menu{% if menu_item.align_right %} dropdown-menu-right{% endif %}"> {%- for submenu_item in menu_item.get_menu(context_item) %} <li> <a href="{{ submenu_item.get_url(context_item) }}"{% if submenu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ submenu_item.get_legend(context_item) }}"{% endif %}> - {% if submenu_item.get_icon(context_item) %}{{ get_fa_icon(submenu_item.get_icon(context_item)) }}{% endif %}{% if submenu_item.get_title(context_item) %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title(context_item) }}</span>{% else %} {{ submenu_item.get_title(context_item) }}{% endif %}{% endif %} + {% if submenu_item.get_icon(context_item) %}{{ get_icon(submenu_item.get_icon(context_item)) }}{% endif %}{% if submenu_item.get_title(context_item) %}{% if submenu_item.resptitle %}<span class="hidden-sm"> {{ submenu_item.get_title(context_item) }}</span>{% else %} {{ submenu_item.get_title(context_item) }}{% endif %}{% endif %} </a> </li> {%- endfor %} </ul> {%- else %} <a role="button" class="btn btn-default btn-sm" href="{{ menu_item.get_url(context_item) }}"{% if menu_item.get_legend(context_item) %} data-toggle="tooltip" title="{{ menu_item.get_legend(context_item) }}"{% endif %}> - {% if menu_item.get_icon(context_item) %}{{ get_fa_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} + {% if menu_item.get_icon(context_item) %}{{ get_icon(menu_item.get_icon(context_item)) }}{% endif %}{% if menu_item.get_title(context_item) %}{% if menu_item.resptitle %}<span class="hidden-sm"> {{ menu_item.get_title(context_item) }}</span>{% else %} {{ menu_item.get_title(context_item) }}{% endif %}{% endif %} </a> {%- endif %} {%- endfor %} @@ -214,7 +214,7 @@ <div class="btn-group btn-group-{{ btn_size }}" role="group"> {%- for endpoint_name in endpoint_names %} <a role="button" class="btn btn-default btn-{{ btn_size }}" href="{{ url_for(get_endpoint_class(endpoint_name).get_view_endpoint()) }}" data-toggle="tooltip" title="{{ get_endpoint_class(endpoint_name).get_menu_legend() }}"> - {{ get_fa_icon(get_endpoint_class(endpoint_name).get_menu_icon()) }}{% if with_title %} {{ get_endpoint_class(endpoint_name).get_menu_title()}}{% endif %} + {{ get_icon(get_endpoint_class(endpoint_name).get_menu_icon()) }}{% if with_title %} {{ get_endpoint_class(endpoint_name).get_menu_title()}}{% endif %} </a> {%- endfor %} </div> @@ -234,7 +234,7 @@ <!-- Locale switcher widget - BEGIN ---------------------------> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> - <span data-toggle="tooltip" data-placement="bottom" title="{{ gettext('Switch site locale') }}">{{ get_fa_icon('language') }} <span class="hidden-sm hidden-md">{{ config['SUPPORTED_LOCALES'][g.locale] | capitalize }}</span></span><span class="caret"></span> + <span data-toggle="tooltip" data-placement="bottom" title="{{ gettext('Switch site locale') }}">{{ get_icon('language') }} <span class="hidden-sm hidden-md">{{ config['SUPPORTED_LOCALES'][g.locale] | capitalize }}</span></span><span class="caret"></span> </a> <ul class="dropdown-menu locales" role="menu"> {%- for locale in config['SUPPORTED_LOCALES'] %} @@ -294,16 +294,16 @@ </button> {%- endif %} {%- if forceicon %} - {{ get_fa_icon(forceicon) }} + {{ get_icon(forceicon) }} {%- else %} - {{ get_fa_icon('alert-' + category) }} + {{ get_icon('alert-' + category) }} {%- endif %} {{ caller() }} </div> {%- endmacro %} {%- macro render_help_popover(title, helptext, btn_size = 'xs', placement = 'left') %} -<a tabindex="0" class="btn btn-{{ btn_size }} btn-info" role="button" data-toggle="popover" data-placement="{{ placement }}" data-trigger="focus" title="{{ title }}" data-content="{{ helptext }}">{{ get_fa_icon('help') }}</a> +<a tabindex="0" class="btn btn-{{ btn_size }} btn-info" role="button" data-toggle="popover" data-placement="{{ placement }}" data-trigger="focus" title="{{ title }}" data-content="{{ helptext }}">{{ get_icon('help') }}</a> {%- endmacro %} {%- macro render_pager(endpoint, qparams, ii_low, ii_high, ii_limit) %} @@ -363,7 +363,7 @@ {{ form_item(class_='form-control') }} {%- if with_errors %} {%- for err in form_item.errors %} - <span class="help-block form-error">{{ get_fa_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} + <span class="help-block form-error">{{ get_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} {%- endfor %} {%- endif %} </div> @@ -382,7 +382,7 @@ {{ form_item(class_='form-control selectpicker',**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3', 'data-none-selected-text': gettext('Nothing selected') }) }} {%- if with_errors %} {%- for err in form_item.errors %} - <span class="help-block form-error">{{ get_fa_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} + <span class="help-block form-error">{{ get_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} {%- endfor %} {%- endif %} </div> @@ -401,12 +401,12 @@ <div id="{{ ident }}" class="input-group date"> {{ form_item(class_='form-control') }} <span class="input-group-addon"> - {{ get_fa_icon('calendar') }} + {{ get_icon('calendar') }} </span> </div> {%- if with_errors %} {%- for err in form_item.errors %} - <span class="help-block form-error">{{ get_fa_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} + <span class="help-block form-error">{{ get_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} {%- endfor %} {%- endif %} </div> @@ -448,7 +448,7 @@ </div> {%- if with_errors %} {%- for err in form_item.errors %} - <span class="help-block form-error">{{ get_fa_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} + <span class="help-block form-error">{{ get_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} {%- endfor %} {%- endif %} </div> @@ -463,13 +463,13 @@ {%- macro form_errors(errors) %} {%- for err in errors %} -<span class="help-block form-error">{{ get_fa_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} +<span class="help-block form-error">{{ get_icon('form-error') }} {{ err }}</span>{%- if not loop.last %}<br>{%- endif %} {%- endfor %} {%- endmacro %} {%- macro form_errors_labeled(item) %} {%- for err in item.errors %} -<div><span class="help-block form-error">{{ get_fa_icon('form-error') }} <strong>{{ item.label.text }}</strong> {{ err }}</span></div> +<div><span class="help-block form-error">{{ get_icon('form-error') }} <strong>{{ item.label.text }}</strong> {{ err }}</span></div> {%- endfor %} {%- endmacro %} @@ -482,7 +482,7 @@ {%- macro render_raw_item_view(item) %} <div class="well col-md-6 col-md-offset-3"> - <h3 class="panel-title">{{ get_fa_icon('debug') }} {{ gettext('Raw item') }}</h3> + <h3 class="panel-title">{{ get_icon('debug') }} {{ gettext('Raw item') }}</h3> <hr> <pre> {{ item | pprint }} @@ -492,14 +492,14 @@ {%- macro render_raw_var(label, var) %} <div class="well"> - <h4>{{ get_fa_icon('debug') }} {{ gettext('Variable dump:') }} <strong>{{ label }}</strong></h4> + <h4>{{ get_icon('debug') }} {{ gettext('Variable dump:') }} <strong>{{ label }}</strong></h4> <hr> <div class="panel-group" id="vardump-{{ label }}" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="vardump-{{ label }}-pprint-h"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#vardump-{{ label }}" href="#vardump-{{ label }}-pprint" aria-expanded="true" aria-controls="vardump-{{ label }}-pprint"> - <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('PPRINT') }} {{ get_fa_icon('caret-down') }}</span> + <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('PPRINT') }} {{ get_icon('caret-down') }}</span> </a> </h4> </div> @@ -513,7 +513,7 @@ <div class="panel-heading" role="tab" id="vardump-{{ label }}-repr-h"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#vardump-{{ label }}" href="#vardump-{{ label }}-repr" aria-expanded="true" aria-controls="vardump-{{ label }}-repr"> - <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('REPR') }} {{ get_fa_icon('caret-down') }}</span> + <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('REPR') }} {{ get_icon('caret-down') }}</span> </a> </h4> </div> @@ -527,7 +527,7 @@ <div class="panel-heading" role="tab" id="vardump-{{ label }}-str-h"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#vardump-{{ label }}" href="#vardump-{{ label }}-str" aria-expanded="true" aria-controls="vardump-{{ label }}-str"> - <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('STR') }} {{ get_fa_icon('caret-down') }}</span> + <span data-toggle="tooltip" title="{{ gettext('Expand/Collapse') }}">{{ gettext('STR') }} {{ get_icon('caret-down') }}</span> </a> </h4> </div> @@ -641,7 +641,7 @@ <table class="table table-bordered table-striped table-condensed"> <tr> <th> - <span data-toggle="tooltip" title="{{ gettext('Sum of all values') }}">{{ get_fa_icon('sum') }} Sum</span> + <span data-toggle="tooltip" title="{{ gettext('Sum of all values') }}">{{ get_icon('sum') }} Sum</span> </th> <td class="col-value"> {{ babel_format_decimal(chstats.get('sum_' + chstatkey, 0)) }} @@ -652,18 +652,18 @@ </tr> <tr> <th> - <span data-toggle="tooltip" title="{{ gettext('Count of all values') }}">{{ get_fa_icon('cnt') }} Cnt</span> + <span data-toggle="tooltip" title="{{ gettext('Count of all values') }}">{{ get_icon('cnt') }} Cnt</span> </th> <td class="col-value"> {{ babel_format_decimal(chstats.get('cnt_' + chstatkey, 0)) }} </td> <td class="col-value"> - {{ get_fa_icon('na') }} + {{ get_icon('na') }} </td> </tr> <tr> <th> - <span data-toggle="tooltip" title="{{ gettext('Minimal value') }}">{{ get_fa_icon('min') }} Min</span> + <span data-toggle="tooltip" title="{{ gettext('Minimal value') }}">{{ get_icon('min') }} Min</span> </th> <td class="col-value"> {{ babel_format_decimal(chstats.get('min_' + chstatkey, 0)) }} @@ -674,7 +674,7 @@ </tr> <tr> <th> - <span data-toggle="tooltip" title="{{ gettext('Maximal value') }}">{{ get_fa_icon('max') }} Max</span> + <span data-toggle="tooltip" title="{{ gettext('Maximal value') }}">{{ get_icon('max') }} Max</span> </th> <td class="col-value"> {{ babel_format_decimal(chstats.get('max_' + chstatkey, 0)) }} @@ -685,7 +685,7 @@ </tr> <tr> <th> - <span data-toggle="tooltip" title="{{ gettext('Average value') }}">{{ get_fa_icon('avg') }} Avg</span> + <span data-toggle="tooltip" title="{{ gettext('Average value') }}">{{ get_icon('avg') }} Avg</span> </th> <td class="col-value"> {{ babel_format_decimal(chstats.get('avg_' + chstatkey, 0)) }} @@ -711,33 +711,33 @@ {%- macro render_report_label_type(report, with_label = False) %} {%- if report.type == 'summary' %} -<span class="label label-default" title="{{ gettext('Summary report') }}" data-toggle="tooltip">{{ get_fa_icon('r-t-summary') }}{% if with_label %} {{ gettext('summary') | upper }}{% endif %}</span> +<span class="label label-default" title="{{ gettext('Summary report') }}" data-toggle="tooltip">{{ get_icon('r-t-summary') }}{% if with_label %} {{ gettext('summary') | upper }}{% endif %}</span> {%- elif report.type == 'extra' %} -<span class="label label-default" title="{{ gettext('Extra report') }}" data-toggle="tooltip">{{ get_fa_icon('r-t-extra') }}{% if with_label %} {{ gettext('extra') | upper }}{% endif %}</span> +<span class="label label-default" title="{{ gettext('Extra report') }}" data-toggle="tooltip">{{ get_icon('r-t-extra') }}{% if with_label %} {{ gettext('extra') | upper }}{% endif %}</span> {%- endif %} {%- endmacro %} {%- macro render_report_label_severity(report, with_label = False) %} {%- if report.severity == 'low' %} -<span class="label label-info" title="{{ gettext('Low severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-low') }}{% if with_label %} {{ gettext('low') | upper }}{% endif %}</span> +<span class="label label-info" title="{{ gettext('Low severity') }}" data-toggle="tooltip">{{ get_icon('r-s-low') }}{% if with_label %} {{ gettext('low') | upper }}{% endif %}</span> {%- elif report.severity == 'medium' %} -<span class="label label-primary" title="{{ gettext('Medium severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-medium') }}{% if with_label %} {{ gettext('medium') | upper }}{% endif %}</span> +<span class="label label-primary" title="{{ gettext('Medium severity') }}" data-toggle="tooltip">{{ get_icon('r-s-medium') }}{% if with_label %} {{ gettext('medium') | upper }}{% endif %}</span> {%- elif report.severity == 'high' %} -<span class="label label-warning" title="{{ gettext('High severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-high') }}{% if with_label %} {{ gettext('high') | upper }}{% endif %}</span> +<span class="label label-warning" title="{{ gettext('High severity') }}" data-toggle="tooltip">{{ get_icon('r-s-high') }}{% if with_label %} {{ gettext('high') | upper }}{% endif %}</span> {%- elif report.severity == 'critical' %} -<span class="label label-danger" title="{{ gettext('Critical severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-critical') }}{% if with_label %} {{ gettext('critical') | upper }}{% endif %}</span> +<span class="label label-danger" title="{{ gettext('Critical severity') }}" data-toggle="tooltip">{{ get_icon('r-s-critical') }}{% if with_label %} {{ gettext('critical') | upper }}{% endif %}</span> {%- endif %} {%- endmacro %} {%- macro render_report_label_weight(report) %} {%- if report.evcount_rep < 10 %} -<span class="label label-info" title="{{ gettext('Low event count') }}" data-toggle="tooltip">{{ get_fa_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> +<span class="label label-info" title="{{ gettext('Low event count') }}" data-toggle="tooltip">{{ get_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> {%- elif report.evcount_rep < 100 %} -<span class="label label-primary" title="{{ gettext('Medium event count') }}" data-toggle="tooltip">{{ get_fa_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> +<span class="label label-primary" title="{{ gettext('Medium event count') }}" data-toggle="tooltip">{{ get_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> {%- elif report.evcount_rep < 1000 %} -<span class="label label-warning" title="{{ gettext('High event count') }}" data-toggle="tooltip">{{ get_fa_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> +<span class="label label-warning" title="{{ gettext('High event count') }}" data-toggle="tooltip">{{ get_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> {%- else %} -<span class="label label-danger" title="{{ gettext('Very high event count') }}" data-toggle="tooltip">{{ get_fa_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> +<span class="label label-danger" title="{{ gettext('Very high event count') }}" data-toggle="tooltip">{{ get_icon('weight') }} <span class="badge">{{ report.evcount_rep}}</span></span> {%- endif %} {%- endmacro %} @@ -750,15 +750,15 @@ {%- macro render_event_label_severity(event, with_label = False) %} {%- set tmpval = event.get_severity() %} {%- if not tmpval or tmpval == 'unknown' %} -<span class="label label-default" title="{{ gettext('Unknown event severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-unknown') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> +<span class="label label-default" title="{{ gettext('Unknown event severity') }}" data-toggle="tooltip">{{ get_icon('r-s-unknown') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> {%- elif tmpval == 'low' %} -<span class="label label-info" title="{{ gettext('Low event severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-low') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> +<span class="label label-info" title="{{ gettext('Low event severity') }}" data-toggle="tooltip">{{ get_icon('r-s-low') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> {%- elif tmpval == 'medium' %} -<span class="label label-primary" title="{{ gettext('Medium event severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-medium') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> +<span class="label label-primary" title="{{ gettext('Medium event severity') }}" data-toggle="tooltip">{{ get_icon('r-s-medium') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> {%- elif tmpval == 'high' %} -<span class="label label-warning" title="{{ gettext('High event severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-high') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> +<span class="label label-warning" title="{{ gettext('High event severity') }}" data-toggle="tooltip">{{ get_icon('r-s-high') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> {%- elif tmpval == 'critical' %} -<span class="label label-danger" title="{{ gettext('Critical event severity') }}" data-toggle="tooltip">{{ get_fa_icon('r-s-critical') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> +<span class="label label-danger" title="{{ gettext('Critical event severity') }}" data-toggle="tooltip">{{ get_icon('r-s-critical') }}{% if with_label %} {{ gettext(tmpval) | upper }}{% endif %}</span> {%- endif %} {%- endmacro %} @@ -822,7 +822,7 @@ {%- if separate_dropdown and not without_label %} <span class="caret"></span> {%- elif not without_label %} - {{ get_fa_icon(tmpicon) }} {% if marklist and subitem in marklist %}<mark>{{ gettext(subitem) | upper }}</mark>{%- else %}{{ gettext(subitem) | upper }}{%- endif %} <span class="caret"></span> + {{ get_icon(tmpicon) }} {% if marklist and subitem in marklist %}<mark>{{ gettext(subitem) | upper }}</mark>{%- else %}{{ gettext(subitem) | upper }}{%- endif %} <span class="caret"></span> {%- else %} <span class="caret"></span> {%- endif %} @@ -1198,9 +1198,9 @@ {%- macro render_label_item_state(state, with_label = False) %} {%- if state %} -<span class="label label-success" title="{{ gettext('Enabled') }}" data-toggle="tooltip">{{ get_fa_icon('item-enabled') }}{% if with_label %} {{ gettext('Enabled') }}{% endif %}</span> +<span class="label label-success" title="{{ gettext('Enabled') }}" data-toggle="tooltip">{{ get_icon('item-enabled') }}{% if with_label %} {{ gettext('Enabled') }}{% endif %}</span> {%- else %} -<span class="label label-default" title="{{ gettext('Disabled') }}" data-toggle="tooltip">{{ get_fa_icon('item-disabled') }}{% if with_label %} {{ gettext('Disabled') }}{% endif %}</span> +<span class="label label-default" title="{{ gettext('Disabled') }}" data-toggle="tooltip">{{ get_icon('item-disabled') }}{% if with_label %} {{ gettext('Disabled') }}{% endif %}</span> {%- endif %} {%- endmacro %} @@ -1208,9 +1208,9 @@ {%- macro render_labels_role_list(role_list, item, with_label = False) -%} {%- for role_name in role_list -%} {%- if item.has_role(role_name) -%} -<span class="label label-success">{{ get_fa_icon('item-enabled') }} {{ role_name }} {{ get_fa_icon('role-{}'.format(role_name)) }}</span> +<span class="label label-success">{{ get_icon('item-enabled') }} {{ role_name }} {{ get_icon('role-{}'.format(role_name)) }}</span> {%- else -%} -<span class="label label-default">{{ get_fa_icon('item-disabled') }} {{ role_name }} {{ get_fa_icon('role-{}'.format(role_name)) }}</span> +<span class="label label-default">{{ get_icon('item-disabled') }} {{ role_name }} {{ get_icon('role-{}'.format(role_name)) }}</span> {%- endif -%} {%- if not loop.last -%} {%- endif -%} {%- endfor -%} diff --git a/lib/hawat/blueprints/design/templates/form_delete.html b/lib/hawat/blueprints/design/templates/form_delete.html index 605f80abcafd5e374543f0ab279abe22019eeb9e..a84a17ae7ec378b719f159b91f174724be9a0263 100644 --- a/lib/hawat/blueprints/design/templates/form_delete.html +++ b/lib/hawat/blueprints/design/templates/form_delete.html @@ -10,7 +10,7 @@ <div class="modal-content"> <div class="modal-header"> - <h4 class="modal-title">{{ get_fa_icon('modal-question') }} {{ hawat_this_view_title }}</h4> + <h4 class="modal-title">{{ get_icon('modal-question') }} {{ hawat_current_view.get_view_title() }}</h4> </div> <div class="modal-body"> diff --git a/lib/hawat/blueprints/design/templates/form_disable.html b/lib/hawat/blueprints/design/templates/form_disable.html index 337e214f4caafe4b9ba91c6c10c22e3551d0d277..f0b050d5da5bfbab702d82dc7a6bff6c64ef16cf 100644 --- a/lib/hawat/blueprints/design/templates/form_disable.html +++ b/lib/hawat/blueprints/design/templates/form_disable.html @@ -10,7 +10,7 @@ <div class="modal-content"> <div class="modal-header"> - <h4 class="modal-title">{{ get_fa_icon('modal-question') }} {{ hawat_this_view_title }}</h4> + <h4 class="modal-title">{{ get_icon('modal-question') }} {{ hawat_current_view.get_view_title() }}</h4> </div> <div class="modal-body"> diff --git a/lib/hawat/blueprints/design/templates/form_enable.html b/lib/hawat/blueprints/design/templates/form_enable.html index 2714a3b91edf3e636419840ff17ed79299ddceb1..52322ed58d4da2323c3237cae7ea0f15faf9d7ab 100644 --- a/lib/hawat/blueprints/design/templates/form_enable.html +++ b/lib/hawat/blueprints/design/templates/form_enable.html @@ -10,7 +10,7 @@ <div class="modal-content"> <div class="modal-header"> - <h4 class="modal-title">{{ get_fa_icon('modal-question') }} {{ hawat_this_view_title }}</h4> + <h4 class="modal-title">{{ get_icon('modal-question') }} {{ hawat_current_view.get_view_title() }}</h4> </div> <div class="modal-body"> diff --git a/lib/hawat/blueprints/devtools/__init__.py b/lib/hawat/blueprints/devtools/__init__.py index 4e3c30aef50dab93ef538df5b54289ff4d25a5c9..d9470d06be4590d6c8a2e8b8b91ecd8bcb9f2c90 100644 --- a/lib/hawat/blueprints/devtools/__init__.py +++ b/lib/hawat/blueprints/devtools/__init__.py @@ -21,21 +21,25 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" +# +# Flask related modules. +# import flask import flask_debugtoolbar from flask_babel import lazy_gettext -import hawat.base +# +# Custom modules. +# import hawat.acl +from hawat.base import HTMLViewMixin, HawatSimpleView, HawatBlueprint -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'devtools' +"""Name of the blueprint as module global constant.""" -class ConfigView(hawat.base.HawatSimpleView): +class ConfigView(HTMLViewMixin, HawatSimpleView): """ View for displaying current Hawat configuration and environment. """ @@ -59,16 +63,16 @@ class ConfigView(hawat.base.HawatSimpleView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('System configuration') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('View system configuration') @@ -76,7 +80,7 @@ class ConfigView(hawat.base.HawatSimpleView): #------------------------------------------------------------------------------- -class DevtoolsBlueprint(hawat.base.HawatBlueprint): +class DevtoolsBlueprint(HawatBlueprint): """ Hawat pluggable module - development tools. """ diff --git a/lib/hawat/blueprints/devtools/templates/devtools/config.html b/lib/hawat/blueprints/devtools/templates/devtools/config.html index 185c6e1e2345e7495df16c9c58691bed8ce2e549..46fe264b8a595e79117bb07e84b2d1dbcc9d8207 100644 --- a/lib/hawat/blueprints/devtools/templates/devtools/config.html +++ b/lib/hawat/blueprints/devtools/templates/devtools/config.html @@ -4,15 +4,15 @@ <div class="row"> <div class="col-lg-12"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <h3>Permissions</h3> <table class="table"> {%- for key, val in get_endpoints_dict()|dictsort%} <tr> <td> - {{ get_fa_icon('module-{}'.format(val.module_name)) }} - {{ get_fa_icon(val.get_menu_icon()) }} + {{ get_icon('module-{}'.format(val.module_name)) }} + {{ get_icon(val.get_menu_icon()) }} </td> <td> {{ key }} diff --git a/lib/hawat/blueprints/events/__init__.py b/lib/hawat/blueprints/events/__init__.py index bc09ee6f3c766ac2e38ef4ea935a8b47512e5ab0..df2dbefe16214f145d72a71af344681074f9741f 100644 --- a/lib/hawat/blueprints/events/__init__.py +++ b/lib/hawat/blueprints/events/__init__.py @@ -12,8 +12,30 @@ Description ----------- -This pluggable module provides access to IDEA message database. +This pluggable module provides access to `IDEA <https://idea.cesnet.cz/en/index>`__ +message database. It enables users to execute custom queries via provided search +form, display the details of the messages or download them locally to their own +computers. Additionally the overview dashboard panel is provided. + +Provided endpoints +------------------ + +``/events/search`` + Endpoint providing search form for querying the `IDEA <https://idea.cesnet.cz/en/index>`__ + message database. + + *Authentication:* login required + *Authorization:* any role + *Methods:* ``GET`` + +``/api/events/search`` + Endpoint providing API search form for querying the `IDEA <https://idea.cesnet.cz/en/index>`__ + message database. + + *Authentication:* login required + *Authorization:* any role + *Methods:* ``POST`` """ @@ -21,150 +43,58 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" -import re - +# +# Flask related modules. +# import flask from flask_babel import gettext, lazy_gettext +# +# Custom modules. +# import mentat.stats.idea import mentat.services.eventstorage from mentat.datatype.sqldb import EventStatisticsModel import hawat.const -import hawat.base import hawat.events import hawat.acl +from hawat.base import HTMLViewMixin, PsycopgViewMixin, AJAXViewMixin, SQLAlchemyViewMixin,\ + HawatBaseView, HawatSearchView, HawatItemShowView, HawatBlueprint from hawat.blueprints.events.forms import SimpleEventSearchForm, EventDashboardForm -# -# Name of the blueprint as module global constant. -# + BLUEPRINT_NAME = 'events' +"""Name of the blueprint as module global constant.""" -def in_query_params(haystack, needles, on_true = True, on_false = False, on_empty = False): - """ - Utility method for checking that any needle from given list of needles is - present in given haystack. +class AbstractSearchView(PsycopgViewMixin, HawatSearchView): # pylint: disable=locally-disabled,abstract-method """ - if not haystack: - return on_empty - for needle in needles: - if needle in haystack: - return on_true - return on_false - -class SearchView(hawat.base.HawatRenderableView): + Base class for view responsible for searching `IDEA <https://idea.cesnet.cz/en/index>`__ + event database and presenting the results. """ - View responsible for searching IDEA event database and presenting the results. - """ - methods = ['GET'] - authentication = True authorization = [hawat.acl.PERMISSION_ANY] @classmethod - def get_view_name(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_name`. - """ - return 'search' - - @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Events') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Search IDEA event database') - @classmethod - def get_query_parameters(cls, form, request_args): - """ - This method can be used to get the dictionary containing actual parameters - that were used to generate and send the request. This dictionary can be - then used to generate for example paginator links. - - :param wtforms.Form form: Form that was parsed from raw request. - :param dict request_args: Raw request arguments. - :return: Dictionary containing intersect of both form and request_args - :rtype: dict - """ - params = {} - for arg in request_args: - if getattr(form, arg, None) and request_args.get(arg, None): - # Handle multivalue request arguments separately - # Resources: - # http://flask.pocoo.org/docs/1.0/api/#flask.Request.args - # http://werkzeug.pocoo.org/docs/0.14/datastructures/#werkzeug.datastructures.MultiDict - try: - if form.is_multivalue(arg): - params[arg] = request_args.getlist(arg) - else: - params[arg] = request_args[arg] - except AttributeError: - params[arg] = request_args[arg] - return params - - @classmethod - def search(cls, args, context): - """ - Perform actual search of IDEA events using provided query arguments. - - :param dict args: Search query arguments. - :param dict context: Jinja2 template context, that will be passed to template during rendering. - :return: Tuple containing number of items as integer and list of searched items. - :rtype: tuple - """ - items_count_total, items = hawat.events.db_get().search_events(args) - context.update(sqlquery = hawat.events.db_get().cursor.lastquery) - return items_count_total, items - - @classmethod - def get_context_action_menu(cls): - """ - Get context action menu for particular item. - """ - action_menu = hawat.menu.HawatMenu() - - action_menu.add_entry( - 'endpoint', - 'show', - endpoint = 'events.show', - hidetitle = True, - legend = lambda x: lazy_gettext('View details of event "%(item)s"', item = x.get_id()), - item_kw = 'get_id' - ) - action_menu.add_entry( - 'endpoint', - 'download', - endpoint = 'events.download', - icon = 'action-download', - hidetitle = True, - legend = lambda x: lazy_gettext('Download event "%(item)s"', item = x.get_id()), - item_kw = 'get_id' - ) - - return action_menu - - def dispatch_request(self): # pylint: disable=locally-disabled,too-many-locals + @staticmethod + def get_search_form(request_args): """ - Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.get_search_form`. """ # Get lists of available options for various event search form select fields. sourcetypes = hawat.events.get_event_source_types() @@ -178,8 +108,8 @@ class SearchView(hawat.base.HawatRenderableView): protocols = hawat.events.get_event_protocols() inspection_errs = hawat.events.get_event_inspection_errs() - form = SimpleEventSearchForm( - flask.request.args, + return SimpleEventSearchForm( + request_args, meta = {'csrf': False}, choices_source_types = list(zip(sourcetypes,sourcetypes)), choices_target_types = list(zip(targettypes,targettypes)), @@ -193,137 +123,118 @@ class SearchView(hawat.base.HawatRenderableView): choices_inspection_errs = list(zip(inspection_errs,inspection_errs)) ) - context = self.get_template_context() - - if hawat.const.HAWAT_FORM_ACTION_SUBMIT in flask.request.args: - if form.validate(): - form_data = form.data - form_data['groups'] = [item.name for item in form_data['groups']] - try: - items_count_total, items = self.search(form_data, context) - - context.update( - searched = True, - items = items, - items_count = len(items), - items_count_total = items_count_total, - pager_index_low = ((form_data['page'] - 1) * form_data['limit']) + 1, - pager_index_high = ((form_data['page'] - 1) * form_data['limit']) + len(items), - pager_index_limit = ((form_data['page'] - 1) * form_data['limit']) + form_data['limit'], - form_data = form_data, - context_action_menu = self.get_context_action_menu() - ) - - except mentat.services.eventstorage.DataError as err: - match = re.match('invalid IP4R value: "([^"]+)"', str(err)) - if match: - flask.flash( - flask.Markup( - gettext( - 'Invalid address value <strong>%(address)s</strong> in event search form.', - address = flask.escape(str(match.group(1))) - ) - ), - hawat.const.HAWAT_FLASH_FAILURE - ) - else: - raise - - context.update( - search_form = form, - request_args = flask.request.args, - query_params = self.get_query_parameters(form, flask.request.args), - in_query_params = in_query_params, - search_widget_item_limit = 3 - ) - return flask.render_template(self.get_view_template(), **context) + def do_before_search(self, form_data): # pylint: disable=locally-disabled,no-self-use,unused-argument + """ + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.do_before_search`. + """ + form_data['groups'] = [item.name for item in form_data['groups']] -class ShowView(hawat.base.HawatRenderableView): +class SearchView(HTMLViewMixin, AbstractSearchView): # pylint: disable=locally-disabled,too-many-ancestors """ - Detailed event view. + View responsible for querying `IDEA <https://idea.cesnet.cz/en/index>`__ + event database and presenting the results in the form of HTML page. """ methods = ['GET'] - authentication = True - - authorization = [hawat.acl.PERMISSION_ANY] - @classmethod - def get_view_name(cls): + def get_breadcrumbs_menu(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_name`. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.get_breadcrumbs_menu`. """ - return 'show' + breadcrumbs_menu = hawat.menu.HawatMenu() + breadcrumbs_menu.add_entry( + 'link', + 'index', + endpoint = 'index', + title = gettext('Home') + ) + breadcrumbs_menu.add_entry( + 'endpoint', + 'search', + endpoint = '{}.search'.format(cls.module_name) + ) + return breadcrumbs_menu @classmethod - def get_menu_icon(cls): + def get_context_action_menu(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.get_context_action_menu`. """ - return 'action-show' + action_menu = hawat.menu.HawatMenu() + action_menu.add_entry( + 'endpoint', + 'show', + endpoint = 'events.show', + hidetitle = True, + legend = lambda x: lazy_gettext('View details of event "%(item)s"', item = x.get_id()), + item_kw = 'get_id' + ) + action_menu.add_entry( + 'endpoint', + 'download', + endpoint = 'events.download', + icon = 'action-download', + hidetitle = True, + legend = lambda x: lazy_gettext('Download event "%(item)s"', item = x.get_id()), + item_kw = 'get_id' + ) + return action_menu + + +class APISearchView(AJAXViewMixin, AbstractSearchView): # pylint: disable=locally-disabled,too-many-ancestors + """ + View responsible for querying `IDEA <https://idea.cesnet.cz/en/index>`__ + event database and presenting the results in the form of JSON document. + """ + methods = ['GET','POST'] @classmethod - def get_menu_title(cls): + def get_view_name(cls): """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_name`. """ - return lazy_gettext('Show IDEA event') + return 'apisearch' + + +class ShowView(HTMLViewMixin, PsycopgViewMixin, HawatItemShowView): + """ + Detailed `IDEA <https://idea.cesnet.cz/en/index>`__ event view. + """ + methods = ['GET'] + + authentication = True + + authorization = [hawat.acl.PERMISSION_ANY] @classmethod - def get_view_title(cls): + def get_menu_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Show IDEA event') @classmethod - def get_action_menu(cls, item_id, item): # pylint: disable=locally-disabled,unused-argument + def get_action_menu(cls, item): # pylint: disable=locally-disabled,unused-argument """ - Get action menu for particular item. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'download', endpoint = 'events.download', title = lazy_gettext('Download'), icon = 'action-download', - legend = lazy_gettext('Download event "%(item)s"', item = item_id), - link = flask.url_for('events.download', item_id = item_id) + legend = lazy_gettext('Download event "%(item)s"', item = item['ID']), + link = flask.url_for('events.download', item_id = item['ID']) ) - return action_menu - #--------------------------------------------------------------------------- - - def dispatch_request(self, item_id): # pylint: disable=locally-disabled,arguments-differ - """ - Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. - Single item with given unique identifier will be retrieved from database - and injected into template to be displayed to the user. - """ - item = hawat.events.db_get().fetch_event(item_id) - if not item: - flask.abort(404) - - context = self.get_template_context() - context.update( - item_id = item_id, - item = item, - action_menu = self.get_action_menu(item_id, item), - search_widget_item_limit = 100 - ) - - return flask.render_template(self.get_view_template(), **context) - - -class DownloadView(hawat.base.HawatBaseView): +class DownloadView(PsycopgViewMixin, HawatBaseView): """ - Download event view. + Download `IDEA <https://idea.cesnet.cz/en/index>`__ event view. """ methods = ['GET'] @@ -346,7 +257,7 @@ class DownloadView(hawat.base.HawatBaseView): return 'action-download' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -362,10 +273,15 @@ class DownloadView(hawat.base.HawatBaseView): Single item with given unique identifier will be retrieved from database and injected into template to be displayed to the user. """ - item = hawat.events.db_get().fetch_event(item_id) + item = self.fetch(item_id) if not item: flask.abort(404) + self.logger.debug( + "IDEA event %s is being downloaded as a standalone file.", + item['ID'] + ) + response = flask.make_response( item.to_json(indent = 4, sort_keys = True) ) @@ -374,9 +290,10 @@ class DownloadView(hawat.base.HawatBaseView): return response -class DashboardView(hawat.base.HawatSearchView): +class DashboardView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView): """ - View responsible for presenting overall system performance dashboard. + View responsible for presenting overall `IDEA <https://idea.cesnet.cz/en/index>`__ + event statistics dashboard. """ authentication = True @@ -397,16 +314,16 @@ class DashboardView(hawat.base.HawatSearchView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Events') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Overall event dashboards') @@ -422,7 +339,7 @@ class DashboardView(hawat.base.HawatSearchView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return EventStatisticsModel @@ -434,27 +351,31 @@ class DashboardView(hawat.base.HawatSearchView): return EventDashboardForm(request_args, meta = {'csrf': False}) @staticmethod - def build_query(query, model, args, context): + def build_query(query, model, form_args): """ - *Interface implementation* of :py:func:`hawat.base.HawatSearchView.build_query`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.build_query`. """ # Adjust query based on lower time boudary selection. - if 'dt_from' in args and args['dt_from']: - query = query.filter(model.dt_from >= args['dt_from']) + if 'dt_from' in form_args and form_args['dt_from']: + query = query.filter(model.dt_from >= form_args['dt_from']) # Adjust query based on upper time boudary selection. - if 'dt_to' in args and args['dt_to']: - query = query.filter(model.dt_to <= args['dt_to']) + if 'dt_to' in form_args and form_args['dt_to']: + query = query.filter(model.dt_to <= form_args['dt_to']) # Return the result sorted by interval. return query.order_by(model.interval) - def do_before_render(self, item, context): + def do_after_search(self, items): """ - *Hook method*. Will be called before rendering the template. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.do_after_search`. """ - context.update( + self.logger.debug( + "Calculating IDEA event dashboard overview from %d records.", + len(items) + ) + self.response_context.update( statistics = mentat.stats.idea.truncate_evaluations( - mentat.stats.idea.aggregate_stat_groups(item) + mentat.stats.idea.aggregate_stat_groups(items) ) ) @@ -462,7 +383,7 @@ class DashboardView(hawat.base.HawatSearchView): #------------------------------------------------------------------------------- -class EventsBlueprint(hawat.base.HawatBlueprint): +class EventsBlueprint(HawatBlueprint): """ Hawat pluggable module - IDEA events. """ @@ -486,7 +407,7 @@ class EventsBlueprint(hawat.base.HawatBlueprint): """ app.menu_main.add_entry( 'view', - 'dashboards.events', + 'dashboards.{}'.format(BLUEPRINT_NAME), position = 10, view = DashboardView ) @@ -511,13 +432,13 @@ def get_blueprint(): hbp = EventsBlueprint( BLUEPRINT_NAME, __name__, - template_folder = 'templates', - url_prefix = '/{}'.format(BLUEPRINT_NAME) + template_folder = 'templates' ) - hbp.register_view_class(SearchView, '/search') - hbp.register_view_class(ShowView, '/show/<item_id>') - hbp.register_view_class(DownloadView, '/download/<item_id>') - hbp.register_view_class(DashboardView, '/dashboard') + hbp.register_view_class(SearchView, '/{}/search'.format(BLUEPRINT_NAME)) + hbp.register_view_class(APISearchView, '/api/{}/search'.format(BLUEPRINT_NAME)) + hbp.register_view_class(ShowView, '/{}/<item_id>/show'.format(BLUEPRINT_NAME)) + hbp.register_view_class(DownloadView, '/{}/<item_id>/download'.format(BLUEPRINT_NAME)) + hbp.register_view_class(DashboardView, '/{}/dashboard'.format(BLUEPRINT_NAME)) return hbp diff --git a/lib/hawat/blueprints/events/templates/events/dashboard.html b/lib/hawat/blueprints/events/templates/events/dashboard.html index 6ef67dfd80b157ea4a3930750451b6c3f04696c1..bc7e9502c78ee5a0e5419d1fe8e4eb517aca76ea 100644 --- a/lib/hawat/blueprints/events/templates/events/dashboard.html +++ b/lib/hawat/blueprints/events/templates/events/dashboard.html @@ -6,26 +6,26 @@ <div class="col-lg-12"> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <form method="GET" class="form-inline" action="{{ url_for('events.dashboard') }}"> - {{ macros_site.render_form_item_datetime(search_form.dt_from, 'datetimepicker-hm-from', False) }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_from, 'datetimepicker-hm-from', False) }} - {{ macros_site.render_form_item_datetime(search_form.dt_to, 'datetimepicker-hm-to', False) }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_to, 'datetimepicker-hm-to', False) }} <div class="btn-group" role="group"> - {{ search_form.submit(class_='btn btn-primary') }} + {{ g.search_form.submit(class_='btn btn-primary') }} <a role="button" class="btn btn-default" href="{{ url_for('events.dashboard') }}">{{ gettext('Clear') }}</a> </div> </form> - {%- if search_form.dt_from.errors %} + {%- if g.search_form.dt_from.errors %} <div> - {{ macros_site.form_errors(search_form.dt_from.errors) }} + {{ macros_site.form_errors(g.search_form.dt_from.errors) }} </div> {%- endif %} - {%- if search_form.dt_to.errors %} + {%- if g.search_form.dt_to.errors %} <div> - {{ macros_site.form_errors(search_form.dt_to.errors) }} + {{ macros_site.form_errors(g.search_form.dt_to.errors) }} </div> {%- endif %} </div> <!-- /.jumbotron --> diff --git a/lib/hawat/blueprints/events/templates/events/search.html b/lib/hawat/blueprints/events/templates/events/search.html index 4ed12196e9040729403b5fffe114716f2391e2e6..4fe7a52aef6f43197515c41a61eb9659f05bb7c7 100644 --- a/lib/hawat/blueprints/events/templates/events/search.html +++ b/lib/hawat/blueprints/events/templates/events/search.html @@ -5,27 +5,26 @@ <div class="row"> <div class="col-lg-12"> - <ol class="breadcrumb"> - <li><a href="{{ url_for('index') }}">{{ gettext('Home') }}</a></li> - <li class="active">{{ gettext('Events') }}</li> - </ol> + {{ macros_page.render_breadcrumbs() }} + + <!-- Search form - BEGIN ----------------------------------> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> - <form method="GET" class="form-horizontal" id="form-events-simple" action="{{ url_for('events.search') }}"> + <form method="GET" class="form-horizontal" id="form-events-simple" action="{{ url_for(request.endpoint) }}"> <div> <div class="btn-toolbar" role="toolbar" aria-label="{{ gettext('Toolbar for enabling/disabling event search form fields.') }}"> <div class="btn-group" role="group"> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['dt_from', 'dt_to'], ' btn-success active', ' btn-default', ' btn-success active') %} + {%- set tglbtncls = in_query_params(request.args, ['dt_from', 'dt_to'], ' btn-success active', ' btn-default', ' btn-success active') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-detecttime" data-toggle-form-group="form-group-detecttime" data-disable-form-group="form-group-storagetime" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('DetectTime') }} </button> </div> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['st_from', 'st_to'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtncls = in_query_params(request.args, ['st_from', 'st_to'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-storagetime" data-toggle-form-group="form-group-storagetime" data-disable-form-group="form-group-detecttime" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('StorageTime') }} </button> @@ -34,21 +33,21 @@ <div class="btn-group" role="group"> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['source_addrs', 'source_ports', 'source_types'], ' btn-success active', ' btn-default', ' btn-success active') %} + {%- set tglbtncls = in_query_params(request.args, ['source_addrs', 'source_ports', 'source_types'], ' btn-success active', ' btn-default', ' btn-success active') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-source" data-toggle-form-group="form-group-source" data-disable-form-group="form-group-host" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('Source') }} </button> </div> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['target_addrs', 'target_ports', 'target_types'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtncls = in_query_params(request.args, ['target_addrs', 'target_ports', 'target_types'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-target" data-toggle-form-group="form-group-target" data-disable-form-group="form-group-host" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('Target') }} </button> </div> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['host_addrs', 'host_ports', 'host_types'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtncls = in_query_params(request.args, ['host_addrs', 'host_ports', 'host_types'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-host" data-toggle-form-group="form-group-host" data-disable-form-group="form-group-source,form-group-target" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('Host') }} </button> @@ -56,14 +55,14 @@ </div> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['categories', 'severities', 'groups', 'protocols', 'description'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtncls = in_query_params(request.args, ['categories', 'severities', 'groups', 'protocols', 'description'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-event" data-toggle-form-group="form-group-event-1,form-group-event-2" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('Event') }} </button> </div> <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtncls = in_query_params(request_args, ['detectors', 'detector_types'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtncls = in_query_params(request.args, ['detectors', 'detector_types'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtncls }}" id="switch-form-group-detector" data-toggle-form-group="form-group-detector" data-toggle="button" aria-pressed="false" autocomplete="off"> {{ gettext('Detector') }} </button> @@ -71,9 +70,9 @@ {%- if permission_can('power') %} <div class="btn-group" role="group"> {#- Detect toggle button class #} - {%- set tglbtnadm = in_query_params(request_args, ['inspection_errs', 'classes'], ' btn-success active', ' btn-default', ' btn-default') %} + {%- set tglbtnadm = in_query_params(request.args, ['inspection_errs', 'classes'], ' btn-success active', ' btn-default', ' btn-default') %} <button type="button" class="toggle-search-form btn{{ tglbtnadm }}" id="switch-form-group-admin" data-toggle-form-group="form-group-admin" data-toggle="button" aria-pressed="false" autocomplete="off"> - {{ get_fa_icon('role-admin') }} {{ gettext('Admin') }} + {{ get_icon('role-admin') }} {{ gettext('Admin') }} </button> </div> {%- endif %} @@ -82,219 +81,219 @@ <hr> - {%- set frmctrldsb = in_query_params(request_args, ['dt_from', 'dt_to'], False, True, False) %} - {%- set frmctrlhdn = in_query_params(request_args, ['dt_from', 'dt_to'], '', ' hidden', '') %} + {%- set frmctrldsb = in_query_params(request.args, ['dt_from', 'dt_to'], False, True, False) %} + {%- set frmctrlhdn = in_query_params(request.args, ['dt_from', 'dt_to'], '', ' hidden', '') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-detecttime"> <div class="col-sm-6"> - <label>{{ search_form.dt_from.label }}</label> + <label>{{ g.search_form.dt_from.label }}</label> <div id="datetimepicker-from-1" class="input-group date datetimepicker-ymdhms-from"> - {{ search_form.dt_from(class_='form-control', disabled=frmctrldsb) }} + {{ g.search_form.dt_from(class_='form-control', disabled=frmctrldsb) }} <span class="input-group-addon"> - {{ get_fa_icon('calendar') }} + {{ get_icon('calendar') }} </span> </div> - {%- if search_form.dt_from.errors %} - {{ macros_site.form_errors(search_form.dt_from.errors) }} + {%- if g.search_form.dt_from.errors %} + {{ macros_site.form_errors(g.search_form.dt_from.errors) }} {%- endif %} </div> <div class="col-sm-6"> - <label>{{ search_form.dt_to.label }}</label> + <label>{{ g.search_form.dt_to.label }}</label> <div id="datetimepicker-to-1" class="input-group date datetimepicker-ymdhms-to"> - {{ search_form.dt_to(class_='form-control', disabled=frmctrldsb) }} + {{ g.search_form.dt_to(class_='form-control', disabled=frmctrldsb) }} <span class="input-group-addon"> - {{ get_fa_icon('calendar') }} + {{ get_icon('calendar') }} </span> </div> - {%- if search_form.dt_to.errors %} - {{ macros_site.form_errors(search_form.dt_to.errors) }} + {%- if g.search_form.dt_to.errors %} + {{ macros_site.form_errors(g.search_form.dt_to.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['st_from', 'st_to'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['st_from', 'st_to'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['st_from', 'st_to'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['st_from', 'st_to'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-storagetime"> <div class="col-sm-6"> - <label>{{ search_form.st_from.label }}</label> + <label>{{ g.search_form.st_from.label }}</label> <div id="datetimepicker-from-2" class="input-group date datetimepicker-ymdhms-from"> - {{ search_form.st_from(class_='form-control', disabled=frmctrldsb) }} + {{ g.search_form.st_from(class_='form-control', disabled=frmctrldsb) }} <span class="input-group-addon"> - {{ get_fa_icon('calendar') }} + {{ get_icon('calendar') }} </span> </div> - {%- if search_form.st_from.errors %} - {{ macros_site.form_errors(search_form.st_from.errors) }} + {%- if g.search_form.st_from.errors %} + {{ macros_site.form_errors(g.search_form.st_from.errors) }} {%- endif %} </div> <div class="col-sm-6"> - <label>{{ search_form.st_to.label }}</label> + <label>{{ g.search_form.st_to.label }}</label> <div id="datetimepicker-to-2" class="input-group date datetimepicker-ymdhms-to"> - {{ search_form.st_to(class_='form-control', disabled=frmctrldsb) }} + {{ g.search_form.st_to(class_='form-control', disabled=frmctrldsb) }} <span class="input-group-addon"> - {{ get_fa_icon('calendar') }} + {{ get_icon('calendar') }} </span> </div> - {%- if search_form.st_to.errors %} - {{ macros_site.form_errors(search_form.st_to.errors) }} + {%- if g.search_form.st_to.errors %} + {{ macros_site.form_errors(g.search_form.st_to.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['source_addrs', 'source_ports', 'source_types'], False, True, False) %} - {%- set frmctrlhdn = in_query_params(request_args, ['source_addrs', 'source_ports', 'source_types'], '', ' hidden', '') %} + {%- set frmctrldsb = in_query_params(request.args, ['source_addrs', 'source_ports', 'source_types'], False, True, False) %} + {%- set frmctrlhdn = in_query_params(request.args, ['source_addrs', 'source_ports', 'source_types'], '', ' hidden', '') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-source"> <div class="col-sm-4"> - <label>{{ search_form.source_addrs.label }}</label> - {{ search_form.source_addrs(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.source_addrs.errors %} - {{ macros_site.form_errors(search_form.source_addrs.errors) }} + <label>{{ g.search_form.source_addrs.label }}</label> + {{ g.search_form.source_addrs(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.source_addrs.errors %} + {{ macros_site.form_errors(g.search_form.source_addrs.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.source_ports.label }}</label> - {{ search_form.source_ports(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.source_ports.errors %} - {{ macros_site.form_errors(search_form.source_ports.errors) }} + <label>{{ g.search_form.source_ports.label }}</label> + {{ g.search_form.source_ports(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.source_ports.errors %} + {{ macros_site.form_errors(g.search_form.source_ports.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.source_types.label }}</label> - {{ search_form.source_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.source_types.errors %} - {{ macros_site.form_errors(search_form.source_types.errors) }} + <label>{{ g.search_form.source_types.label }}</label> + {{ g.search_form.source_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.source_types.errors %} + {{ macros_site.form_errors(g.search_form.source_types.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['target_addrs', 'target_ports', 'target_types'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['target_addrs', 'target_ports', 'target_types'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['target_addrs', 'target_ports', 'target_types'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['target_addrs', 'target_ports', 'target_types'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-target"> <div class="col-sm-4"> - <label>{{ search_form.target_addrs.label }}</label> - {{ search_form.target_addrs(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.target_addrs.errors %} - {{ macros_site.form_errors(search_form.target_addrs.errors) }} + <label>{{ g.search_form.target_addrs.label }}</label> + {{ g.search_form.target_addrs(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.target_addrs.errors %} + {{ macros_site.form_errors(g.search_form.target_addrs.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.target_ports.label }}</label> - {{ search_form.target_ports(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.target_ports.errors %} - {{ macros_site.form_errors(search_form.target_ports.errors) }} + <label>{{ g.search_form.target_ports.label }}</label> + {{ g.search_form.target_ports(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.target_ports.errors %} + {{ macros_site.form_errors(g.search_form.target_ports.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.target_types.label }}</label> - {{ search_form.target_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.target_types.errors %} - {{ macros_site.form_errors(search_form.target_types.errors) }} + <label>{{ g.search_form.target_types.label }}</label> + {{ g.search_form.target_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.target_types.errors %} + {{ macros_site.form_errors(g.search_form.target_types.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['host_addrs', 'host_ports', 'host_types'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['host_addrs', 'host_ports', 'host_types'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['host_addrs', 'host_ports', 'host_types'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['host_addrs', 'host_ports', 'host_types'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-host"> <div class="col-sm-4"> - <label>{{ search_form.host_addrs.label }}</label> - {{ search_form.host_addrs(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.host_addrs.errors %} - {{ macros_site.form_errors(search_form.host_addrs.errors) }} + <label>{{ g.search_form.host_addrs.label }}</label> + {{ g.search_form.host_addrs(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.host_addrs.errors %} + {{ macros_site.form_errors(g.search_form.host_addrs.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.host_ports.label }}</label> - {{ search_form.host_ports(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.host_ports.errors %} - {{ macros_site.form_errors(search_form.host_ports.errors) }} + <label>{{ g.search_form.host_ports.label }}</label> + {{ g.search_form.host_ports(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.host_ports.errors %} + {{ macros_site.form_errors(g.search_form.host_ports.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.host_types.label }}</label> - {{ search_form.host_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.host_types.errors %} - {{ macros_site.form_errors(search_form.host_types.errors) }} + <label>{{ g.search_form.host_types.label }}</label> + {{ g.search_form.host_types(class_='form-control selectpicker', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.host_types.errors %} + {{ macros_site.form_errors(g.search_form.host_types.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['categories', 'severities', 'groups', 'protocols', 'description'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['categories', 'severities', 'groups', 'protocols', 'description'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['categories', 'severities', 'groups', 'protocols', 'description'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['categories', 'severities', 'groups', 'protocols', 'description'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-event-1"> <div class="col-sm-4"> - <label>{{ search_form.groups.label }}</label> - {{ search_form.groups(class_='form-control selectpicker', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.groups.errors %} - {{ macros_site.form_errors(search_form.groups.errors) }} + <label>{{ g.search_form.groups.label }}</label> + {{ g.search_form.groups(class_='form-control selectpicker', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.groups.errors %} + {{ macros_site.form_errors(g.search_form.groups.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.protocols.label }}</label> - {{ search_form.protocols(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.protocols.errors %} - {{ macros_site.form_errors(search_form.protocols.errors) }} + <label>{{ g.search_form.protocols.label }}</label> + {{ g.search_form.protocols(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.protocols.errors %} + {{ macros_site.form_errors(g.search_form.protocols.errors) }} {%- endif %} </div> <div class="col-sm-4"> - <label>{{ search_form.description.label }}</label> - {{ search_form.description(class_='form-control', disabled=frmctrldsb) }} - {%- if search_form.description.errors %} - {{ macros_site.form_errors(search_form.description.errors) }} + <label>{{ g.search_form.description.label }}</label> + {{ g.search_form.description(class_='form-control', disabled=frmctrldsb) }} + {%- if g.search_form.description.errors %} + {{ macros_site.form_errors(g.search_form.description.errors) }} {%- endif %} </div> </div> <div class="form-group{{ frmctrlhdn }}" id="form-group-event-2"> <div class="col-sm-6"> - <label>{{ search_form.categories.label }}</label> - {{ search_form.categories(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.categories.errors %} - {{ macros_site.form_errors(search_form.categories.errors) }} + <label>{{ g.search_form.categories.label }}</label> + {{ g.search_form.categories(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.categories.errors %} + {{ macros_site.form_errors(g.search_form.categories.errors) }} {%- endif %} </div> <div class="col-sm-6"> - <label>{{ search_form.severities.label }}</label> - {{ search_form.severities(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.severities.errors %} - {{ macros_site.form_errors(search_form.severities.errors) }} + <label>{{ g.search_form.severities.label }}</label> + {{ g.search_form.severities(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.severities.errors %} + {{ macros_site.form_errors(g.search_form.severities.errors) }} {%- endif %} </div> </div> - {%- set frmctrldsb = in_query_params(request_args, ['detectors', 'detector_types'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['detectors', 'detector_types'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['detectors', 'detector_types'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['detectors', 'detector_types'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-detector"> <div class="col-sm-6"> - <label>{{ search_form.detectors.label }}</label> - {{ search_form.detectors(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.detectors.errors %} - {{ macros_site.form_errors(search_form.detectors.errors) }} + <label>{{ g.search_form.detectors.label }}</label> + {{ g.search_form.detectors(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.detectors.errors %} + {{ macros_site.form_errors(g.search_form.detectors.errors) }} {%- endif %} </div> <div class="col-sm-6"> - <label>{{ search_form.detector_types.label }}</label> - {{ search_form.detector_types(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.detector_types.errors %} - {{ macros_site.form_errors(search_form.detector_types.errors) }} + <label>{{ g.search_form.detector_types.label }}</label> + {{ g.search_form.detector_types(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb, **{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.detector_types.errors %} + {{ macros_site.form_errors(g.search_form.detector_types.errors) }} {%- endif %} </div> </div> {%- if permission_can('power') %} - {%- set frmctrldsb = in_query_params(request_args, ['inspection_errs', 'classes'], False, True, True) %} - {%- set frmctrlhdn = in_query_params(request_args, ['inspection_errs', 'classes'], '', ' hidden', ' hidden') %} + {%- set frmctrldsb = in_query_params(request.args, ['inspection_errs', 'classes'], False, True, True) %} + {%- set frmctrlhdn = in_query_params(request.args, ['inspection_errs', 'classes'], '', ' hidden', ' hidden') %} <div class="form-group{{ frmctrlhdn }}" id="form-group-admin"> <div class="col-sm-6"> - <label>{{ search_form.inspection_errs.label }}</label> - {{ search_form.inspection_errs(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.inspection_errs.errors %} - {{ macros_site.form_errors(search_form.inspection_errs.errors) }} + <label>{{ g.search_form.inspection_errs.label }}</label> + {{ g.search_form.inspection_errs(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.inspection_errs.errors %} + {{ macros_site.form_errors(g.search_form.inspection_errs.errors) }} {%- endif %} </div> <div class="col-sm-6"> - <label>{{ search_form.classes.label }}</label> - {{ search_form.classes(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} - {%- if search_form.classes.errors %} - {{ macros_site.form_errors(search_form.classes.errors) }} + <label>{{ g.search_form.classes.label }}</label> + {{ g.search_form.classes(class_='form-control selectpicker esf-any-empty', disabled=frmctrldsb,**{'data-live-search':'true', 'data-size': '10', 'data-selected-text-format': 'count > 3'}) }} + {%- if g.search_form.classes.errors %} + {{ macros_site.form_errors(g.search_form.classes.errors) }} {%- endif %} </div> </div> @@ -304,30 +303,30 @@ <div class="btn-toolbar" role="toolbar" aria-label="{{ gettext('Admin search menu') }}"> <div class="btn-group" role="group" aria-label="{{ gettext('Admin action search options') }}"> - {{ search_form.submit(class_='btn btn-primary') }} - <a role="button" class="btn btn-default" href="{{ url_for('events.search') }}">{{ gettext('Clear') }}</a> + {{ g.search_form.submit(class_='btn btn-primary') }} + <a role="button" class="btn btn-default" href="{{ url_for(request.endpoint) }}">{{ gettext('Clear') }}</a> </div> {%- if permission_can('power') %} <div class="btn-group" role="group" aria-label="{{ gettext('Admin search menu') }}"> <a data-toggle="dropdown" role="button" aria-haspopup="true" href="#" class="btn btn-default dropdown-toggle" aria-expanded="false"> <span data-toggle="tooltip" title="{{ gettext('Admin search menu') }}"> - {{ get_fa_icon('role-admin') }}<span class="hidden-sm"> {{ gettext('Admin') }}</span> <span class="caret"></span> + {{ get_icon('role-admin') }}<span class="hidden-sm"> {{ gettext('Admin') }}</span> <span class="caret"></span> </span> </a> <ul class="dropdown-menu"> <li> - <a href="{{ url_for('events.search', inspection_errs = ['__ANY__'], submit = gettext('Search')) }}"> - {{ get_endpoint_icon('events.search') }} {{ gettext('Search for events with any inspection errors in event database') | safe }} + <a href="{{ url_for(request.endpoint, inspection_errs = ['__ANY__'], submit = gettext('Search')) }}"> + {{ get_endpoint_icon(request.endpoint) }} {{ gettext('Search for events with any inspection errors in event database') | safe }} </a> </li> <li> - <a href="{{ url_for('events.search', classes = ['__EMPTY__'], submit = gettext('Search')) }}"> - {{ get_endpoint_icon('events.search') }} {{ gettext('Search for events without any classification in event database') | safe }} + <a href="{{ url_for(request.endpoint, classes = ['__EMPTY__'], submit = gettext('Search')) }}"> + {{ get_endpoint_icon(request.endpoint) }} {{ gettext('Search for events without any classification in event database') | safe }} </a> </li> <li> - <a href="{{ url_for('events.search', severities = ['__EMPTY__'], submit = gettext('Search')) }}"> - {{ get_endpoint_icon('events.search') }} {{ gettext('Search for events without assigned severity in event database') | safe }} + <a href="{{ url_for(request.endpoint, severities = ['__EMPTY__'], submit = gettext('Search')) }}"> + {{ get_endpoint_icon(request.endpoint) }} {{ gettext('Search for events without assigned severity in event database') | safe }} </a> </li> </ul> @@ -336,21 +335,22 @@ </div> </form> - {%- if search_form.not_protocols.errors or search_form.not_categories.errors or search_form.not_classess.errors or search_form.not_severities.errors or search_form.not_detectors.errors or search_form.not_detector_types.errors or search_form.not_groups.errors or search_form.not_inspection_errs.errors or search_form.sortby.errors or search_form.limit.errors or search_form.page.errors %} + {%- if g.search_form.not_protocols.errors or g.search_form.not_categories.errors or g.search_form.not_classess.errors or g.search_form.not_severities.errors or g.search_form.not_detectors.errors or g.search_form.not_detector_types.errors or g.search_form.not_groups.errors or g.search_form.not_inspection_errs.errors or g.search_form.sortby.errors or g.search_form.limit.errors or g.search_form.page.errors %} <hr> - {{ macros_site.form_errors_labeled(search_form.not_protocols) }} - {{ macros_site.form_errors_labeled(search_form.not_categories) }} - {{ macros_site.form_errors_labeled(search_form.not_classess) }} - {{ macros_site.form_errors_labeled(search_form.not_severities) }} - {{ macros_site.form_errors_labeled(search_form.not_detectors) }} - {{ macros_site.form_errors_labeled(search_form.not_detector_types) }} - {{ macros_site.form_errors_labeled(search_form.not_groups) }} - {{ macros_site.form_errors_labeled(search_form.not_inspection_errs) }} - {{ macros_site.form_errors_labeled(search_form.sortby) }} - {{ macros_site.form_errors_labeled(search_form.limit) }} - {{ macros_site.form_errors_labeled(search_form.page) }} + {{ macros_site.form_errors_labeled(g.search_form.not_protocols) }} + {{ macros_site.form_errors_labeled(g.search_form.not_categories) }} + {{ macros_site.form_errors_labeled(g.search_form.not_classess) }} + {{ macros_site.form_errors_labeled(g.search_form.not_severities) }} + {{ macros_site.form_errors_labeled(g.search_form.not_detectors) }} + {{ macros_site.form_errors_labeled(g.search_form.not_detector_types) }} + {{ macros_site.form_errors_labeled(g.search_form.not_groups) }} + {{ macros_site.form_errors_labeled(g.search_form.not_inspection_errs) }} + {{ macros_site.form_errors_labeled(g.search_form.sortby) }} + {{ macros_site.form_errors_labeled(g.search_form.limit) }} + {{ macros_site.form_errors_labeled(g.search_form.page) }} {%- endif %} - </div> <!-- .jumbotron --> + </div> + <!-- Search form - END ------------------------------------> </div> <!-- .col-lg-12 --> </div> <!-- .row --> @@ -364,7 +364,7 @@ {%- if items_count %} - {{ macros_site.render_pager('events.search', query_params, pager_index_low, pager_index_high, pager_index_limit) }} + {{ macros_site.render_pager(request.endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} <table class="table table-striped table-bordered table-hover table-condensed table-responsive"> <thead> @@ -374,7 +374,7 @@ # </th> --> - {%- if in_query_params(request_args, ['st_from', 'st_to'], True, False, False) %} + {%- if in_query_params(request.args, ['st_from', 'st_to'], True, False, False) %} <th data-toggle="tooltip" title="{{ gettext('Event storage time') }}"> {{ gettext('Stored at') }} </th> @@ -402,7 +402,7 @@ {{ gettext('Groups') }} </th> <th data-toggle="tooltip" title="{{ gettext('Contextual item actions') }}"> - {{ get_fa_icon('actions') }} + {{ get_icon('actions') }} </th> </tr> </thead> @@ -415,7 +415,7 @@ {{ pager_index_low + loop.index0 }} </td> --> - {%- if in_query_params(request_args, ['st_from', 'st_to'], True, False, False) %} + {%- if in_query_params(request.args, ['st_from', 'st_to'], True, False, False) %} <td> {{ babel_format_datetime(item.get_storage_time()) | replace(' ', ' ' | safe )}} </td> @@ -428,21 +428,21 @@ {%- if item.get_addresses('Source') %} {{ macros_site.render_event_search_widget_hostaddr(item.get_addresses('Source'), [] + form_data['source_addrs'] + form_data['target_addrs'] + form_data['host_addrs'], separate_dropdown = False, item_limit = search_widget_item_limit) }} {%- else %} - <span data-toggle="tooltip" title="{{ gettext('-- undisclosed --') }}">{{ get_fa_icon('undisclosed') }}</span> + <span data-toggle="tooltip" title="{{ gettext('-- undisclosed --') }}">{{ get_icon('undisclosed') }}</span> {%- endif %} </td> <td class="hidden-xs hidden-sm hidden-md"> {%- if item.get_addresses('Target') %} {{ macros_site.render_event_search_widget_hostaddr(item.get_addresses('Target'), [] + form_data['source_addrs'] + form_data['target_addrs'] + form_data['host_addrs'], separate_dropdown = False, item_limit = search_widget_item_limit) }} {%- else %} - <span data-toggle="tooltip" title="{{ gettext('-- undisclosed --') }}">{{ get_fa_icon('undisclosed') }}</span> + <span data-toggle="tooltip" title="{{ gettext('-- undisclosed --') }}">{{ get_icon('undisclosed') }}</span> {%- endif %} </td> <td> {%- if item.get_severity() %} {{ macros_site.render_event_search_widget_severity([item.get_severity()], align_right = True, separate_dropdown = False) }} {%- else %} - <span data-toggle="tooltip" title="{{ gettext('-- unassigned --') }}">{{ get_fa_icon('unassigned') }}</span> + <span data-toggle="tooltip" title="{{ gettext('-- unassigned --') }}">{{ get_icon('unassigned') }}</span> {%- endif %} </td> <td> @@ -456,19 +456,19 @@ {%- if item.get_abuses() %} {{ macros_site.render_event_search_widget_group(item.get_abuses(), form_data['groups'], align_right = True, separate_dropdown = False) }} {%- else %} - <span data-toggle="tooltip" title="{{ gettext('-- unassigned --') }}">{{ get_fa_icon('unassigned') }}</span> + <span data-toggle="tooltip" title="{{ gettext('-- unassigned --') }}">{{ get_icon('unassigned') }}</span> {%- endif %} </td> <td> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_site.render_menu_context_actions(hawat_current_view.get_context_action_menu(), item) }} </td> </tr> {%- endfor %} </tbody> </table> - {{ macros_site.render_pager('events.search', query_params, pager_index_low, pager_index_high, pager_index_limit) }} + {{ macros_site.render_pager(request.endpoint, query_params, pager_index_low, pager_index_high, pager_index_limit) }} {%- if permission_can('developer') %} <hr> @@ -486,7 +486,7 @@ {%- if permission_can('developer') %} <hr> - {{ macros_site.render_raw_var('request_args', request_args) }} + {{ macros_site.render_raw_var('request_args', request.args) }} {{ macros_site.render_raw_var('query_params', query_params) }} {%- endif %} diff --git a/lib/hawat/blueprints/events/templates/events/show.html b/lib/hawat/blueprints/events/templates/events/show.html index 259264ce560533e6bee3a6127a65b5ea2fab6114..1a6054973be84df19525748772df31317f14100f 100644 --- a/lib/hawat/blueprints/events/templates/events/show.html +++ b/lib/hawat/blueprints/events/templates/events/show.html @@ -10,11 +10,11 @@ <li><a href="{{ url_for('events.search') }}">{{ gettext('Event database') }}</a></li> <li class="active">{{ gettext('Event detail') }}</li> </ol> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{% if item['Description'] %}{{ item['Description'] }} <small>{{ item.get_id() }}</small>{% else %}{{ item.get_id() }}{% endif %}</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} </div> {%- if item['Note'] %} <p> @@ -133,7 +133,7 @@ {%- if permission_can('power') %} <tr> <th> - <span class="pull-right">{{ get_fa_icon('role-admin') }}</span> {{ gettext('Event class') }}: + <span class="pull-right">{{ get_icon('role-admin') }}</span> {{ gettext('Event class') }}: </th> <td> {%- if item.get_class() %} @@ -243,11 +243,11 @@ {%- set tmpval = item.get_jpath_values('_CESNET.InspectionErrors') %} {%- if tmpval %} <hr> - <h4>{{ gettext('Inspection errors') }} {{ get_fa_icon('role-admin') }}</h4> + <h4>{{ gettext('Inspection errors') }} {{ get_icon('role-admin') }}</h4> <ul class="list-group"> {%- for subitem in tmpval %} <li class="list-group-item list-group-item-warning"> - {{ get_fa_icon('alert-warning') }} <strong>{{ subitem }}</strong> + {{ get_icon('alert-warning') }} <strong>{{ subitem }}</strong> </li> {%- endfor %} </ul> diff --git a/lib/hawat/blueprints/filters/__init__.py b/lib/hawat/blueprints/filters/__init__.py index a65a8a42702a31e2b0844fbd674c65b71f8a5ad8..9311e2bc1784344a1632edf24f1a3b2d5ff5e895 100644 --- a/lib/hawat/blueprints/filters/__init__.py +++ b/lib/hawat/blueprints/filters/__init__.py @@ -41,22 +41,23 @@ from flask_babel import gettext, lazy_gettext # # Custom modules. # -import hawat.base +from mentat.const import REPORTING_FILTER_BASIC +from mentat.datatype.sqldb import FilterModel, GroupModel, ItemChangeLogModel + import hawat.db import hawat.events +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView,\ + HawatItemShowView, HawatItemCreateView, HawatItemCreateForView, HawatItemUpdateView,\ + HawatItemEnableView, HawatItemDisableView, HawatItemDeleteView, HawatBlueprint from hawat.blueprints.filters.forms import BaseFilterForm, AdminFilterForm -from mentat.const import REPORTING_FILTER_BASIC -from mentat.datatype.sqldb import FilterModel, GroupModel, ItemChangeLogModel _PARSER = pynspect.gparser.PynspectFilterParser() _PARSER.build() -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'filters' +"""Name of the blueprint as module global constant.""" def process_rule(item): @@ -109,7 +110,7 @@ def to_html(rule_tree): return None -class ListView(hawat.base.HawatItemListView): +class ListView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView): """ General reporting filter listing. """ @@ -120,14 +121,7 @@ class ListView(hawat.base.HawatItemListView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -138,33 +132,30 @@ class ListView(hawat.base.HawatItemListView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'create', endpoint = 'filters.create', resptitle = True ) - return action_menu @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'show', @@ -200,11 +191,10 @@ class ListView(hawat.base.HawatItemListView): hidetitle = True, legend = lambda x: lazy_gettext('Delete reporting filter "%(item)s"', item = x.name) ) - return action_menu -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed reporting filter view. """ @@ -213,16 +203,16 @@ class ShowView(hawat.base.HawatItemShowView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Show reporting filter') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show reporting filter details') @@ -231,7 +221,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -252,7 +242,6 @@ class ShowView(hawat.base.HawatItemShowView): Get action menu for particular item. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'update', @@ -289,22 +278,22 @@ class ShowView(hawat.base.HawatItemShowView): icon = 'action-delete', legend = lazy_gettext('Delete reporting filter "%(item)s"', item = item.name) ) - return action_menu - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ + item = self.response_context['item'] filter_tree = to_tree(item.filter) - context.update( + self.response_context.update( filter_tree = filter_tree, filter_preview = to_html(filter_tree) ) - if self.can_access_endpoint('filters.update', item) and self.has_view_class('changelogs.search'): - context.update( - context_action_menu_changelogs = self.get_view_class('changelogs.search').get_context_action_menu() + if self.can_access_endpoint('filters.update', item) and self.has_endpoint('changelogs.search'): + self.response_context.update( + context_action_menu_changelogs = self.get_endpoint_class('changelogs.search').get_context_action_menu() ) item_changelog = self.dbsession.query(ItemChangeLogModel).\ @@ -313,10 +302,10 @@ class ShowView(hawat.base.HawatItemShowView): order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(item_changelog = item_changelog) + self.response_context.update(item_changelog = item_changelog) -class CreateView(hawat.base.HawatItemCreateView): +class CreateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new reporting filters. """ @@ -325,16 +314,16 @@ class CreateView(hawat.base.HawatItemCreateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create reporting filter') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new reporting filter') @@ -343,7 +332,7 @@ class CreateView(hawat.base.HawatItemCreateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -359,28 +348,28 @@ class CreateView(hawat.base.HawatItemCreateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully created.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new reporting filter for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new reporting filter for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ detectors = hawat.events.get_event_detectors() categories = hawat.events.get_event_categories() @@ -390,24 +379,25 @@ class CreateView(hawat.base.HawatItemCreateView): choices_categories = list(zip(categories,categories)) ) - def do_before_action(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_action(self, item): # pylint: disable=locally-disabled,no-self-use,unused-argument """ *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.do_before_action` interface. """ process_rule(item) - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ + item = self.response_context['item'] filter_tree = to_tree(item.filter) - context.update( + self.response_context.update( filter_tree = filter_tree, filter_preview = to_html(filter_tree) ) -class CreateForView(hawat.base.HawatItemCreateForView): +class CreateForView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateForView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new reporting filters. """ @@ -423,16 +413,16 @@ class CreateForView(hawat.base.HawatItemCreateForView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create reporting filter') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new reporting filter for group') @@ -441,14 +431,14 @@ class CreateForView(hawat.base.HawatItemCreateForView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @property def dbmodel_par(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -467,28 +457,28 @@ class CreateForView(hawat.base.HawatItemCreateForView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully created.', item_id = str(kwargs['item']), parent_id = str(kwargs['parent'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new reporting filter for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['parent'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new reporting filter for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['parent'])) @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ detectors = hawat.events.get_event_detectors() categories = hawat.events.get_event_categories() @@ -505,24 +495,25 @@ class CreateForView(hawat.base.HawatItemCreateForView): """ item.group = parent - def do_before_action(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_action(self, item): # pylint: disable=locally-disabled,no-self-use,unused-argument """ *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.do_before_action` interface. """ process_rule(item) - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ + item = self.response_context['item'] filter_tree = to_tree(item.filter) - context.update( + self.response_context.update( filter_tree = filter_tree, filter_preview = to_html(filter_tree) ) -class UpdateView(hawat.base.HawatItemUpdateView): +class UpdateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing reporting filters. """ @@ -531,16 +522,16 @@ class UpdateView(hawat.base.HawatItemUpdateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Update reporting filter') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Update reporting filter details') @@ -549,7 +540,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -568,21 +559,21 @@ class UpdateView(hawat.base.HawatItemUpdateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully updated.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to update reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled updating reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @@ -608,24 +599,25 @@ class UpdateView(hawat.base.HawatItemUpdateView): choices_categories = list(zip(categories,categories)) ) - def do_before_action(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_action(self, item): # pylint: disable=locally-disabled,no-self-use,unused-argument """ *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.do_before_action` interface. """ process_rule(item) - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ + item = self.response_context['item'] filter_tree = to_tree(item.filter) - context.update( + self.response_context.update( filter_tree = filter_tree, filter_preview = to_html(filter_tree) ) -class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors +class EnableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for enabling existing groups. """ @@ -634,7 +626,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -645,7 +637,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -664,26 +656,26 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully enabled.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to enable reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled enabling reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) -class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors +class DisableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -692,7 +684,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -703,7 +695,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -722,26 +714,26 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully disabled.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to disable reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled disabling reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) -class DeleteView(hawat.base.HawatItemDeleteView): +class DeleteView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing reporting filters. """ @@ -750,7 +742,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -761,7 +753,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return FilterModel @@ -780,21 +772,21 @@ class DeleteView(hawat.base.HawatItemDeleteView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully and permanently deleted.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to permanently delete reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting reporting filter <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @@ -802,7 +794,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): #------------------------------------------------------------------------------- -class FiltersBlueprint(hawat.base.HawatBlueprint): +class FiltersBlueprint(HawatBlueprint): """ Hawat pluggable module - reporting filters. """ diff --git a/lib/hawat/blueprints/filters/templates/filters/creatupdate.html b/lib/hawat/blueprints/filters/templates/filters/creatupdate.html index 1efd995dec218caad419826f89e1a48500eb0819..337934a99a5b31f50386d13607a1ed4a2d5ffa97 100644 --- a/lib/hawat/blueprints/filters/templates/filters/creatupdate.html +++ b/lib/hawat/blueprints/filters/templates/filters/creatupdate.html @@ -17,7 +17,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {%- if item_action == 'createfor' %} diff --git a/lib/hawat/blueprints/filters/templates/filters/list.html b/lib/hawat/blueprints/filters/templates/filters/list.html index 029b2f2706efad86995c4734ce7e31391291d73f..9f7d1308299d8321b7f5bc4aaae867009310f527 100644 --- a/lib/hawat/blueprints/filters/templates/filters/list.html +++ b/lib/hawat/blueprints/filters/templates/filters/list.html @@ -24,7 +24,7 @@ {{ gettext('State') }} </th> <th data-toggle="tooltip" title="{{ gettext('Contextual item actions') }}"> - {{ get_fa_icon('actions') }} {{ gettext('Actions') }} + {{ get_icon('actions') }} {{ gettext('Actions') }} </th> </tr> </thead> @@ -52,7 +52,7 @@ {{ macros_site.render_label_item_state(item.enabled, True) }} </td> <td class="column-actions"> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_page.render_menu_context_actions(item) }} </td> </tr> {%- endfor %} diff --git a/lib/hawat/blueprints/filters/templates/filters/show.html b/lib/hawat/blueprints/filters/templates/filters/show.html index 2b183836cdcc3c5620cbdb4bb57d4e0c55a7f256..28306490c1bf4a0125cbd1d13e0fd54c7adb277b 100644 --- a/lib/hawat/blueprints/filters/templates/filters/show.html +++ b/lib/hawat/blueprints/filters/templates/filters/show.html @@ -4,12 +4,14 @@ <div class="row"> <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ item.name }}</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} + </div> <p> <small> @@ -22,20 +24,20 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('alert-info') }} {{ gettext('General information') }}</strong> + <strong>{{ get_icon('alert-info') }} {{ gettext('General information') }}</strong> </a> </li> {%- if filter_preview %} <li role="presentation"> <a href="#tab-tree" aria-controls="tab-tree" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('structure') }} {{ gettext('Filter tree structure') }}</strong> + <strong>{{ get_icon('structure') }} {{ gettext('Filter tree structure') }}</strong> </a> </li> {%- endif %} {%- if can_access_endpoint('filters.update', item) %} <li role="presentation"> <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> </a> </li> {%- endif %} diff --git a/lib/hawat/blueprints/geoip/__init__.py b/lib/hawat/blueprints/geoip/__init__.py index 434391bd09fbead0dd0756903a19ef75be5a74dc..5776fd68e59915518be306b135e77864d4eb4453 100644 --- a/lib/hawat/blueprints/geoip/__init__.py +++ b/lib/hawat/blueprints/geoip/__init__.py @@ -42,20 +42,18 @@ from flask_babel import lazy_gettext # Custom modules. # import mentat.services.geoip -import hawat.base import hawat.const import hawat.db import hawat.acl +from hawat.base import HTMLViewMixin, HawatRenderableView, HawatBlueprint from hawat.blueprints.geoip.forms import GeoipSearchForm -# -# Name of the blueprint as global constant. -# BLUEPRINT_NAME = 'geoip' +"""Name of the blueprint as module global constant.""" -class SearchView(hawat.base.HawatRenderableView): +class SearchView(HTMLViewMixin, HawatRenderableView): """ Application view providing search form for internal IP geolocation service and appropriate result page. @@ -76,16 +74,16 @@ class SearchView(hawat.base.HawatRenderableView): return 'search' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Internal geoip') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Search internal geoip database') @@ -96,8 +94,6 @@ class SearchView(hawat.base.HawatRenderableView): Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. Will be called by the *Flask* framework to service the request. """ - context = self.get_template_context() - form = GeoipSearchForm(flask.request.args, meta = {'csrf': False}) if hawat.const.HAWAT_FORM_ACTION_SUBMIT in flask.request.args: @@ -105,22 +101,22 @@ class SearchView(hawat.base.HawatRenderableView): form_data = form.data geoip_manager = mentat.services.geoip.GeoipServiceManager(flask.current_app.mconfig) geoip_service = geoip_manager.service() - context.update( + self.response_context.update( search_item = form.search.data, search_result = geoip_service.lookup(form.search.data), form_data = form_data ) - context.update( + self.response_context.update( search_form = form, request_args = flask.request.args, ) - return flask.render_template(self.get_view_template(), **context) + return self.generate_response() #------------------------------------------------------------------------------- -class GeoipBlueprint(hawat.base.HawatBlueprint): +class GeoipBlueprint(HawatBlueprint): """ Hawat pluggable module - IP geolocation service. """ diff --git a/lib/hawat/blueprints/geoip/templates/geoip/search.html b/lib/hawat/blueprints/geoip/templates/geoip/search.html index e51e25ca28630f36e477c43c49ef3396d03b0440..b27d492cca216465f2fa28c552e84ac72b06c7f3 100644 --- a/lib/hawat/blueprints/geoip/templates/geoip/search.html +++ b/lib/hawat/blueprints/geoip/templates/geoip/search.html @@ -6,12 +6,12 @@ <div class="col-lg-12"> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <form method="GET" class="form-inline" action="{{ url_for('geoip.search') }}"> <div class="form-group{% if search_form.search.errors %}{{ ' has-error' }}{% endif %}"> {{ search_form.search.label(class_='sr-only') }} <div class="input-group"> - <div data-toggle="tooltip" class="input-group-addon" title="{{ gettext('Search local IP geolocation database for:') }}">{{ get_fa_icon('action-search') }}</div> + <div data-toggle="tooltip" class="input-group-addon" title="{{ gettext('Search local IP geolocation database for:') }}">{{ get_icon('action-search') }}</div> {{ search_form.search(class_='form-control', placeholder=gettext('IPv4 or IPv6 address'), size='50') }} </div> diff --git a/lib/hawat/blueprints/groups/__init__.py b/lib/hawat/blueprints/groups/__init__.py index 42cf7e8e7ba0d8317038ed199eb48ae921edb270..124a01d106723fe58db8889153f02d57898a7adb 100644 --- a/lib/hawat/blueprints/groups/__init__.py +++ b/lib/hawat/blueprints/groups/__init__.py @@ -42,23 +42,25 @@ from sqlalchemy import and_, or_ # # Custom modules. # -import hawat.base +from mentat.datatype.sqldb import GroupModel, SettingsReportingModel,\ + FilterModel, NetworkModel, ItemChangeLogModel + import hawat.acl import hawat.db import hawat.menu +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView,\ + HawatItemShowView, HawatItemCreateView, HawatItemUpdateView, HawatItemEnableView,\ + HawatItemDisableView, HawatItemDeleteView, HawatBlueprint + from hawat.blueprints.groups.forms import CreateGroupForm, UpdateGroupForm,\ AdminUpdateGroupForm -from mentat.datatype.sqldb import GroupModel, SettingsReportingModel,\ - FilterModel, NetworkModel, ItemChangeLogModel -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'groups' +"""Name of the blueprint as module global constant.""" -class ListView(hawat.base.HawatItemListView): +class ListView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView): """ General group listing. """ @@ -70,14 +72,7 @@ class ListView(hawat.base.HawatItemListView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -88,33 +83,30 @@ class ListView(hawat.base.HawatItemListView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'create', endpoint = 'groups.create', resptitle = True ) - return action_menu @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'show', @@ -150,11 +142,10 @@ class ListView(hawat.base.HawatItemListView): hidetitle = True, legend = lambda x: lazy_gettext('Delete group "%(item)s"', item = str(x)) ) - return action_menu -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed group view. """ @@ -164,16 +155,16 @@ class ShowView(hawat.base.HawatItemShowView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Show group') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show group details') @@ -182,7 +173,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -203,7 +194,6 @@ class ShowView(hawat.base.HawatItemShowView): Get action menu for particular item. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'update', @@ -258,12 +248,11 @@ class ShowView(hawat.base.HawatItemShowView): title = lazy_gettext('Create new reporting filter'), link = flask.url_for('filters.createfor', parent_id = item.id) ) - return action_menu - def do_before_render(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_response(self, **kwargs): # pylint: disable=locally-disabled,no-self-use,unused-argument """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.do_before_render`. + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -273,7 +262,7 @@ class ShowView(hawat.base.HawatItemShowView): hidetitle = True, legend = lambda x: lazy_gettext('View details of user account "%(item)s"', item = x.login) ) - context.update(context_action_menu_users = action_menu) + self.response_context.update(context_action_menu_users = action_menu) action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -283,7 +272,7 @@ class ShowView(hawat.base.HawatItemShowView): hidetitle = True, legend = lambda x: lazy_gettext('View details of network record "%(item)s"', item = x.netname) ) - context.update(context_action_menu_networks = action_menu) + self.response_context.update(context_action_menu_networks = action_menu) action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -293,11 +282,12 @@ class ShowView(hawat.base.HawatItemShowView): hidetitle = True, legend = lambda x: lazy_gettext('View details of reporting filter "%(item)s"', item = x.name) ) - context.update(context_action_menu_filters = action_menu) + self.response_context.update(context_action_menu_filters = action_menu) - if self.can_access_endpoint('groups.update', item) and self.has_view_class('changelogs.search'): - context.update( - context_action_menu_changelogs = self.get_view_class('changelogs.search').get_context_action_menu() + item = self.response_context['item'] + if self.can_access_endpoint('groups.update', item) and self.has_endpoint('changelogs.search'): + self.response_context.update( + context_action_menu_changelogs = self.get_endpoint_class('changelogs.search').get_context_action_menu() ) item_changelog = self.dbsession.query(ItemChangeLogModel).\ @@ -334,10 +324,10 @@ class ShowView(hawat.base.HawatItemShowView): order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(item_changelog = item_changelog) + self.response_context.update(item_changelog = item_changelog) -class ShowByNameView(ShowView): +class ShowByNameView(ShowView): # pylint: disable=locally-disabled,too-many-ancestors """ Detailed group view by group name. """ @@ -364,7 +354,7 @@ class ShowByNameView(ShowView): return self.dbmodel.name -class CreateView(hawat.base.HawatItemCreateView): +class CreateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new groups. """ @@ -376,16 +366,16 @@ class CreateView(hawat.base.HawatItemCreateView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create group') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new group') @@ -394,7 +384,7 @@ class CreateView(hawat.base.HawatItemCreateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -403,32 +393,32 @@ class CreateView(hawat.base.HawatItemCreateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Group <strong>%(item_id)s</strong> was successfully created.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new group.') @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new group.') @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ return CreateGroupForm() - def do_before_action(self, item, context): + def do_before_action(self, item): """ *Hook method*. Will be called before successfull insertion of the item into the database. @@ -437,7 +427,7 @@ class CreateView(hawat.base.HawatItemCreateView): SettingsReportingModel(group = item) -class UpdateView(hawat.base.HawatItemUpdateView): +class UpdateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing groups. """ @@ -447,16 +437,16 @@ class UpdateView(hawat.base.HawatItemUpdateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Update group') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Update group details') @@ -465,7 +455,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -484,21 +474,21 @@ class UpdateView(hawat.base.HawatItemUpdateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Group <strong>%(item_id)s</strong> was successfully updated.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to update group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled updating group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -515,7 +505,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): return form -class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors +class EnableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for enabling existing groups. """ @@ -526,7 +516,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -537,7 +527,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -546,26 +536,26 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Group <strong>%(item_id)s</strong> was successfully enabled.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to enable group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled enabling group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) -class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors +class DisableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -576,7 +566,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -587,7 +577,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -596,26 +586,26 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Group <strong>%(item_id)s</strong> was successfully disabled.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to disable group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled disabling group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) -class DeleteView(hawat.base.HawatItemDeleteView): +class DeleteView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing groups. """ @@ -627,7 +617,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): authorization = [hawat.acl.PERMISSION_ADMIN] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -638,7 +628,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -647,21 +637,21 @@ class DeleteView(hawat.base.HawatItemDeleteView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Group <strong>%(item_id)s</strong> was successfully and permanently deleted.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to delete group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting group <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -669,7 +659,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): #------------------------------------------------------------------------------- -class GroupsBlueprint(hawat.base.HawatBlueprint): +class GroupsBlueprint(HawatBlueprint): """ Hawat pluggable module - abuse groups. """ @@ -696,7 +686,7 @@ class GroupsBlueprint(hawat.base.HawatBlueprint): groups = {} for i in list(flask_login.current_user.memberships) + list(flask_login.current_user.managements): groups[str(i)] = i - return list(sorted(groups.values(), key = lambda x: str(x))) + return list(sorted(groups.values(), key = str)) app.menu_main.add_entry( 'view', diff --git a/lib/hawat/blueprints/groups/templates/groups/creatupdate.html b/lib/hawat/blueprints/groups/templates/groups/creatupdate.html index edaa16e7afc2c3b8bc0c34f6d9eef2cef1c0c509..ad19f94ac9a119ced4d89a2582d5ae9a3f2d6dd8 100644 --- a/lib/hawat/blueprints/groups/templates/groups/creatupdate.html +++ b/lib/hawat/blueprints/groups/templates/groups/creatupdate.html @@ -17,7 +17,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {%- if item_action == 'create' or current_user.has_role('admin') %} {{ macros_site.render_form_item_default(form.name) }} diff --git a/lib/hawat/blueprints/groups/templates/groups/list.html b/lib/hawat/blueprints/groups/templates/groups/list.html index a3df183fa39dc88fa819682689593757f1119dff..1e53bb9ab9c65648001ba60930ec07aa1dedcf7f 100644 --- a/lib/hawat/blueprints/groups/templates/groups/list.html +++ b/lib/hawat/blueprints/groups/templates/groups/list.html @@ -18,7 +18,7 @@ {{ gettext('State') }} </th> <th data-toggle="tooltip" title="{{ gettext('Contextual item actions') }}"> - {{ get_fa_icon('actions') }} {{ gettext('Actions') }} + {{ get_icon('actions') }} {{ gettext('Actions') }} </th> </tr> </thead> @@ -38,7 +38,7 @@ {{ macros_site.render_label_item_state(item.enabled, True) }} </td> <td class="column-actions"> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_page.render_menu_context_actions(item) }} </td> </tr> {%- endfor %} diff --git a/lib/hawat/blueprints/groups/templates/groups/show.html b/lib/hawat/blueprints/groups/templates/groups/show.html index 456529e1a9bfbf26e1ae432907d3b04fd26058e5..8e7f76c075017d873bacb2350338637e51a6c769 100644 --- a/lib/hawat/blueprints/groups/templates/groups/show.html +++ b/lib/hawat/blueprints/groups/templates/groups/show.html @@ -4,12 +4,14 @@ <div class="row"> <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ item.name }}{% if item.description %} <small>{{ item.description }}</small>{% endif %}</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} + </div> <p> <small> @@ -22,23 +24,23 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('alert-info') }} {{ gettext('General information') }}</strong> + <strong>{{ get_icon('alert-info') }} {{ gettext('General information') }}</strong> </a> </li> <li role="presentation"> <a href="#tab-networks" aria-controls="tab-networks" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-networks') }} {{ gettext('Networks') }}</strong> <span class="badge">{{ item.networks | length }}</span> + <strong>{{ get_icon('module-networks') }} {{ gettext('Networks') }}</strong> <span class="badge">{{ item.networks | length }}</span> </a> </li> <li role="presentation"> <a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-filters') }} {{ gettext('Filters') }}</strong> <span class="badge">{{ item.filters | length }}</span> + <strong>{{ get_icon('module-filters') }} {{ gettext('Filters') }}</strong> <span class="badge">{{ item.filters | length }}</span> </a> </li> {%- if can_access_endpoint('groups.update', item) %} <li role="presentation"> <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> </a> </li> {%- endif %} @@ -90,7 +92,7 @@ </th> <td> <a role="button" class="btn btn-default btn-sm" data-toggle="tooltip" href="{{ url_for('settings_reporting.show', item_id = item.settings_rep.id ) }}" title="{{ gettext('View details of reporting settings for group "%(item)s"', item = item.name) }}"> - {{ get_fa_icon('module-settings-reporting') }} {{ gettext('View reporting settings') }} + {{ get_icon('module-settings-reporting') }} {{ gettext('View reporting settings') }} </a> </td> </tr> diff --git a/lib/hawat/blueprints/help/__init__.py b/lib/hawat/blueprints/help/__init__.py index a8e8f6aba5b859cdda43c29ae4f21f029199cfef..d57d9cb2176eedd3f50d43dba299736d8ff32746 100644 --- a/lib/hawat/blueprints/help/__init__.py +++ b/lib/hawat/blueprints/help/__init__.py @@ -21,18 +21,22 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" +# +# Flask related modules. +# from flask_babel import lazy_gettext -import hawat.base - - # -# Name of the blueprint as module global constant. +# Custom modules. # +from hawat.base import HTMLViewMixin, HawatSimpleView, HawatBlueprint + + BLUEPRINT_NAME = 'help' +"""Name of the blueprint as module global constant.""" -class ViewView(hawat.base.HawatSimpleView): +class ViewView(HTMLViewMixin, HawatSimpleView): """ View responsible for presenting built-in help. """ @@ -53,7 +57,7 @@ class ViewView(hawat.base.HawatSimpleView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -63,7 +67,7 @@ class ViewView(hawat.base.HawatSimpleView): #------------------------------------------------------------------------------- -class HelpBlueprint(hawat.base.HawatBlueprint): +class HelpBlueprint(HawatBlueprint): """ Hawat pluggable module - built-in help. """ diff --git a/lib/hawat/blueprints/help/templates/help/view.html b/lib/hawat/blueprints/help/templates/help/view.html index d4ca09c1542fdd2e9ac276c38d50ddb43ea97cc4..b28d13e12721a7896141d75444560a1208748b1d 100644 --- a/lib/hawat/blueprints/help/templates/help/view.html +++ b/lib/hawat/blueprints/help/templates/help/view.html @@ -4,7 +4,7 @@ <div class="row"> <div class="col-lg-12"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> </div> <!-- /.col-lg-12 --> </div> diff --git a/lib/hawat/blueprints/networks/__init__.py b/lib/hawat/blueprints/networks/__init__.py index ebade003c0a110e3a0bf7f0d5cd0268cadea9d02..ae4472128cd4adb706c1fc1567d33f957c964e79 100644 --- a/lib/hawat/blueprints/networks/__init__.py +++ b/lib/hawat/blueprints/networks/__init__.py @@ -37,19 +37,21 @@ from flask_babel import gettext, lazy_gettext # # Custom modules. # +from mentat.datatype.sqldb import NetworkModel, GroupModel, ItemChangeLogModel + import hawat.base import hawat.db +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView,\ + HawatItemShowView, HawatItemCreateView, HawatItemCreateForView, HawatItemUpdateView,\ + HawatItemDeleteView, HawatBlueprint from hawat.blueprints.networks.forms import BaseNetworkForm, AdminNetworkForm -from mentat.datatype.sqldb import NetworkModel, GroupModel, ItemChangeLogModel -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'networks' +"""Name of the blueprint as module global constant.""" -class ListView(hawat.base.HawatItemListView): +class ListView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView): """ General network record listing. """ @@ -60,14 +62,7 @@ class ListView(hawat.base.HawatItemListView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -78,33 +73,30 @@ class ListView(hawat.base.HawatItemListView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'create', endpoint = 'networks.create', resptitle = True ) - return action_menu @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'show', @@ -126,11 +118,10 @@ class ListView(hawat.base.HawatItemListView): hidetitle = True, legend = lambda x: lazy_gettext('Delete network record "%(item)s"', item = x.netname) ) - return action_menu -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed network record view. """ @@ -139,16 +130,16 @@ class ShowView(hawat.base.HawatItemShowView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Show network record') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show network record details') @@ -157,7 +148,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @@ -200,13 +191,14 @@ class ShowView(hawat.base.HawatItemShowView): return action_menu - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ - if self.can_access_endpoint('networks.update', item) and self.has_view_class('changelogs.search'): - context.update( - context_action_menu_changelogs = self.get_view_class('changelogs.search').get_context_action_menu() + item = self.response_context['item'] + if self.can_access_endpoint('networks.update', item) and self.has_endpoint('changelogs.search'): + self.response_context.update( + context_action_menu_changelogs = self.get_endpoint_class('changelogs.search').get_context_action_menu() ) item_changelog = self.dbsession.query(ItemChangeLogModel).\ @@ -215,10 +207,10 @@ class ShowView(hawat.base.HawatItemShowView): order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(item_changelog = item_changelog) + self.response_context.update(item_changelog = item_changelog) -class CreateView(hawat.base.HawatItemCreateView): +class CreateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new network records. """ @@ -227,16 +219,16 @@ class CreateView(hawat.base.HawatItemCreateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create network record') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new network record') @@ -245,7 +237,7 @@ class CreateView(hawat.base.HawatItemCreateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @@ -261,33 +253,33 @@ class CreateView(hawat.base.HawatItemCreateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully created.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new network record for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new network record for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ return AdminNetworkForm() -class CreateForView(hawat.base.HawatItemCreateForView): +class CreateForView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateForView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new network records. """ @@ -303,16 +295,16 @@ class CreateForView(hawat.base.HawatItemCreateForView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create network record') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new network record for group') @@ -321,14 +313,14 @@ class CreateForView(hawat.base.HawatItemCreateForView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @property def dbmodel_par(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GroupModel @@ -347,28 +339,28 @@ class CreateForView(hawat.base.HawatItemCreateForView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully created.', item_id = str(kwargs['item']), parent_id = str(kwargs['parent'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new network record for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['parent'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new network record for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['parent'])) @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ return BaseNetworkForm() @@ -380,7 +372,7 @@ class CreateForView(hawat.base.HawatItemCreateForView): item.group = parent -class UpdateView(hawat.base.HawatItemUpdateView): +class UpdateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing network records. """ @@ -389,16 +381,16 @@ class UpdateView(hawat.base.HawatItemUpdateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Update network record') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Update network record details') @@ -407,7 +399,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @@ -426,21 +418,21 @@ class UpdateView(hawat.base.HawatItemUpdateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully updated.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to update network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled updating network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @@ -456,7 +448,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): return AdminNetworkForm(obj = item) -class DeleteView(hawat.base.HawatItemDeleteView): +class DeleteView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing network records. """ @@ -465,7 +457,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -476,7 +468,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return NetworkModel @@ -495,21 +487,21 @@ class DeleteView(hawat.base.HawatItemDeleteView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> was successfully and permanently deleted.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to permanently delete network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting network record <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @@ -517,7 +509,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): #------------------------------------------------------------------------------- -class NetworksBlueprint(hawat.base.HawatBlueprint): +class NetworksBlueprint(HawatBlueprint): """ Hawat pluggable module - networks. """ diff --git a/lib/hawat/blueprints/networks/templates/networks/creatupdate.html b/lib/hawat/blueprints/networks/templates/networks/creatupdate.html index 3f489efb35533723603283579dffa9d8e7017c8e..63767bedefe2073a6e89caba2b9a2bd5a4fda4c7 100644 --- a/lib/hawat/blueprints/networks/templates/networks/creatupdate.html +++ b/lib/hawat/blueprints/networks/templates/networks/creatupdate.html @@ -17,7 +17,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {%- if item_action == 'createfor' %} diff --git a/lib/hawat/blueprints/networks/templates/networks/list.html b/lib/hawat/blueprints/networks/templates/networks/list.html index d6c05bf254f0b9592379f6a95993de21702b0b3f..ae4053c16f1f46415985faba01e29f1ff8f8b822 100644 --- a/lib/hawat/blueprints/networks/templates/networks/list.html +++ b/lib/hawat/blueprints/networks/templates/networks/list.html @@ -21,7 +21,7 @@ {{ gettext('Description') }} </th> <th data-toggle="tooltip" title="{{ gettext('Contextual item actions') }}"> - {{ get_fa_icon('actions') }} {{ gettext('Actions') }} + {{ get_icon('actions') }} {{ gettext('Actions') }} </th> </tr> </thead> @@ -46,7 +46,7 @@ {{ item.description | default(gettext('<< unknown >>'), True) | truncate(50) }} </td> <td class="column-actions"> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_page.render_menu_context_actions(item) }} </td> </tr> {%- endfor %} diff --git a/lib/hawat/blueprints/networks/templates/networks/show.html b/lib/hawat/blueprints/networks/templates/networks/show.html index f6b03dfe9367ba6a44b3f8b8039c6fd0a1259958..feae43b29e54056cf129068dfe931847140291b7 100644 --- a/lib/hawat/blueprints/networks/templates/networks/show.html +++ b/lib/hawat/blueprints/networks/templates/networks/show.html @@ -4,12 +4,14 @@ <div class="row"> <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ item.netname }}</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} + </div> <p> <small> @@ -22,13 +24,13 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('alert-info') }} {{ gettext('General information') }}</strong> + <strong>{{ get_icon('alert-info') }} {{ gettext('General information') }}</strong> </a> </li> {%- if can_access_endpoint('networks.update', item) %} <li role="presentation"> <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> </a> </li> {%- endif %} diff --git a/lib/hawat/blueprints/performance/__init__.py b/lib/hawat/blueprints/performance/__init__.py index d6cc9ed503359d1b02083d93e0a8c628f0856a26..61747d5b8f1767256ce2290575550e43a33f3aba 100644 --- a/lib/hawat/blueprints/performance/__init__.py +++ b/lib/hawat/blueprints/performance/__init__.py @@ -13,7 +13,6 @@ Description ----------- This pluggable module provides access to system performance statistics. - """ @@ -26,20 +25,18 @@ import collections from flask_babel import lazy_gettext import mentat.stats.rrd -import hawat.base import hawat.acl +from hawat.base import HTMLViewMixin, HawatSimpleView, HawatFileNameView, HawatBlueprint RRD_DB_DIR = '/var/mentat/rrds' RRD_REPORTS_DIR = '/var/mentat/reports/statistician' -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'performance' +"""Name of the blueprint as module global constant.""" -class ViewView(hawat.base.HawatSimpleView): +class ViewView(HTMLViewMixin, HawatSimpleView): """ View reponsible for presenting system performance in using RRD charts. """ @@ -62,23 +59,24 @@ class ViewView(hawat.base.HawatSimpleView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('System performance') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('System processing performance') - def get_template_context(self): - context = super().get_template_context() - + def do_before_response(self, **kwargs): + """ + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. + """ rrd_stats = mentat.stats.rrd.RrdStats(RRD_DB_DIR, RRD_REPORTS_DIR) charts = rrd_stats.lookup() chartdict = collections.OrderedDict() @@ -95,12 +93,10 @@ class ViewView(hawat.base.HawatSimpleView): for key, val in chartdict.items(): chartdict[key] = sorted(val, key=lambda x: x['size']) - context['chartdict'] = chartdict - - return context + self.response_context['chartdict'] = chartdict -class DataView(hawat.base.HawatFileNameView): +class DataView(HawatFileNameView): """ View reponsible for accessing raw performance data in RRD databases. """ @@ -116,14 +112,7 @@ class DataView(hawat.base.HawatFileNameView): return 'data' @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -131,10 +120,13 @@ class DataView(hawat.base.HawatFileNameView): @classmethod def get_directory_path(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatFileNameView.get_directory_path`. + """ return RRD_REPORTS_DIR -class RRDDBView(hawat.base.HawatFileNameView): +class RRDDBView(HawatFileNameView): """ View reponsible for accessing performance RRD databases. """ @@ -150,14 +142,7 @@ class RRDDBView(hawat.base.HawatFileNameView): return 'rrds' @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -165,13 +150,16 @@ class RRDDBView(hawat.base.HawatFileNameView): @classmethod def get_directory_path(cls): + """ + *Interface implementation* of :py:func:`hawat.base.HawatFileNameView.get_directory_path`. + """ return RRD_DB_DIR #------------------------------------------------------------------------------- -class PerformanceBlueprint(hawat.base.HawatBlueprint): +class PerformanceBlueprint(HawatBlueprint): """ Hawat pluggable module - system processing performance. """ diff --git a/lib/hawat/blueprints/performance/templates/performance/view.html b/lib/hawat/blueprints/performance/templates/performance/view.html index 4d1af81676be73fe260b57da56d7991330a2ce19..62e6c32feefe0a9154834b7269206e44e2963d7a 100644 --- a/lib/hawat/blueprints/performance/templates/performance/view.html +++ b/lib/hawat/blueprints/performance/templates/performance/view.html @@ -4,7 +4,7 @@ <div class="row"> <div class="col-lg-12"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <!-- Nav tabs --> diff --git a/lib/hawat/blueprints/reports/__init__.py b/lib/hawat/blueprints/reports/__init__.py index 20b028ea40af6cd6287138d5dfd004c0776b99ff..b3153fce517af17f238d37e84eb0ebbfbaac7145 100644 --- a/lib/hawat/blueprints/reports/__init__.py +++ b/lib/hawat/blueprints/reports/__init__.py @@ -21,25 +21,30 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" +# +# Flask related modules. +# import flask import flask_login import flask_principal from flask_babel import gettext, lazy_gettext +# +# Custom modules. +# import mentat.const import mentat.stats.idea from mentat.datatype.sqldb import EventReportModel, GroupModel, UserModel -import hawat.base import hawat.menu import hawat.acl +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView,\ + HawatItemShowView, HawatItemDeleteView, HawatFileIdView, HawatBlueprint from hawat.blueprints.reports.forms import EventReportSearchForm, ReportingDashboardForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'reports' +"""Name of the blueprint as module global constant.""" def adjust_query_for_groups(query, groups): @@ -66,7 +71,7 @@ def adjust_query_for_groups(query, groups): return query -class SearchView(hawat.base.HawatSearchView): +class SearchView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView): """ View responsible for searching IDEA event report database and presenting result. """ @@ -77,16 +82,16 @@ class SearchView(hawat.base.HawatSearchView): authorization = [hawat.acl.PERMISSION_ANY] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Reports') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Search event reports') @@ -95,7 +100,7 @@ class SearchView(hawat.base.HawatSearchView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return EventReportModel @@ -107,45 +112,45 @@ class SearchView(hawat.base.HawatSearchView): return EventReportSearchForm(request_args, meta = {'csrf': False}) @staticmethod - def build_query(query, model, args, context): + def build_query(query, model, form_args): """ - *Interface implementation* of :py:func:`hawat.base.HawatSearchView.build_query`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.build_query`. """ # Adjust query based on group selection. - query = adjust_query_for_groups(query, args.get('groups', None)) + query = adjust_query_for_groups(query, form_args.get('groups', None)) # Adjust query based on text search string. - if 'label' in args and args['label']: - query = query.filter(model.label.like('%{}%'.format(args['label']))) + if 'label' in form_args and form_args['label']: + query = query.filter(model.label.like('%{}%'.format(form_args['label']))) # Adjust query based on lower time boudary selection. - if 'dt_from' in args and args['dt_from']: - query = query.filter(model.dt_from >= args['dt_from']) + if 'dt_from' in form_args and form_args['dt_from']: + query = query.filter(model.dt_from >= form_args['dt_from']) # Adjust query based on upper time boudary selection. - if 'dt_to' in args and args['dt_to']: - query = query.filter(model.dt_to <= args['dt_to']) + if 'dt_to' in form_args and form_args['dt_to']: + query = query.filter(model.dt_to <= form_args['dt_to']) # Adjust query based on report severity selection. - if 'severities' in args and args['severities']: - query = query.filter(model.severity.in_(args['severities'])) + if 'severities' in form_args and form_args['severities']: + query = query.filter(model.severity.in_(form_args['severities'])) # Adjust query based on report type selection. - if 'severities' in args and args['types']: - query = query.filter(model.type.in_(args['types'])) + if 'severities' in form_args and form_args['types']: + query = query.filter(model.type.in_(form_args['types'])) # Return the result sorted by creation time in descending order and by label. return query.order_by(model.createtime.desc()).order_by(model.label.desc()) - def do_before_render(self, item, context): + def do_after_search(self, items): """ - *Hook method*. Will be called before rendering the template. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.do_after_search`. """ - if item: - context.update( + if items: + self.response_context.update( statistics = mentat.stats.idea.truncate_stats( - mentat.stats.idea.aggregate_stats_reports(item) + mentat.stats.idea.aggregate_stats_reports(items) ), - max_evcount_rep = max([x.evcount_rep for x in item]) + max_evcount_rep = max([x.evcount_rep for x in items]) ) -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed report view. """ @@ -154,7 +159,7 @@ class ShowView(hawat.base.HawatItemShowView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -168,9 +173,9 @@ class ShowView(hawat.base.HawatItemShowView): return lazy_gettext('View details of event report "%(item)s"', item = str(item)) @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show report') @@ -179,14 +184,14 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return EventReportModel @classmethod def authorize_item_action(cls, item): """ - Perform access authorization for current user to particular item. + *Interface implementation* of :py:func:`hawat.base.HawatItemShowView.authorize_item_action`. """ permission_mm = flask_principal.Permission( hawat.acl.MembershipNeed(item.group.id), @@ -197,10 +202,9 @@ class ShowView(hawat.base.HawatItemShowView): @classmethod def get_breadcrumbs_menu(cls, item): # pylint: disable=locally-disabled,unused-argument """ - Get breadcrumbs menu. + *Interface implementation* of :py:func:`hawat.base.HawatItemShowView.get_breadcrumbs_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'link', 'index', @@ -218,13 +222,12 @@ class ShowView(hawat.base.HawatItemShowView): endpoint = '{}.show'.format(cls.module_name), item = item ) - return action_menu @classmethod def get_action_menu(cls, item): """ - Get action menu for particular item. + *Interface implementation* of :py:func:`hawat.base.HawatItemShowView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() @@ -305,19 +308,21 @@ class ShowView(hawat.base.HawatItemShowView): icon = 'action-download-zip', hidelegend = True ) - return action_menu - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ - *Hook method*. Will be called before rendering the template. + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ - context.update( - statistics = mentat.stats.idea.calculate_secondary_stats(item.statistics) - ) + if 'item' in self.response_context and self.response_context['item']: + self.response_context.update( + statistics = mentat.stats.idea.calculate_secondary_stats( + self.response_context['item'].statistics + ) + ) -class UnauthShowView(ShowView): +class UnauthShowView(ShowView): # pylint: disable=locally-disabled,too-many-ancestors """ Unauthorized access to report detail view. """ @@ -342,19 +347,19 @@ class UnauthShowView(ShowView): @classmethod def authorize_item_action(cls, item): """ - Perform access authorization for current user to particular item. + *Interface implementation* of :py:func:`hawat.base.HawatItemShowView.authorize_item_action`. """ return True @property def search_by(self): """ - Return model`s attribute (column) according to which to search for the item. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.search_by`. """ return self.dbmodel.label -class DataView(hawat.base.HawatFileIdView): +class DataView(HawatFileIdView): """ View responsible for providing access to report data. """ @@ -370,14 +375,7 @@ class DataView(hawat.base.HawatFileIdView): return 'data' @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -412,7 +410,7 @@ class DataView(hawat.base.HawatFileIdView): return 'security-report-{}.{}'.format(fileid, fileext) -class DashboardView(hawat.base.HawatSearchView): +class DashboardView(HTMLViewMixin, SQLAlchemyViewMixin, HawatSearchView): """ View responsible for presenting reporting dashboard. """ @@ -428,16 +426,16 @@ class DashboardView(hawat.base.HawatSearchView): return 'dashboard' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Reporting') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Event reporting dashboards') @@ -453,7 +451,7 @@ class DashboardView(hawat.base.HawatSearchView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return EventReportModel @@ -465,18 +463,18 @@ class DashboardView(hawat.base.HawatSearchView): return ReportingDashboardForm(request_args, meta = {'csrf': False}) @staticmethod - def build_query(query, model, args, context): + def build_query(query, model, form_args): """ - *Interface implementation* of :py:func:`hawat.base.HawatSearchView.build_query`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.build_query`. """ # Adjust query based on group selection. - query = adjust_query_for_groups(query, args.get('groups', None)) + query = adjust_query_for_groups(query, form_args.get('groups', None)) # Adjust query based on lower time boudary selection. - if 'dt_from' in args and args['dt_from']: - query = query.filter(model.dt_from >= args['dt_from']) + if 'dt_from' in form_args and form_args['dt_from']: + query = query.filter(model.dt_from >= form_args['dt_from']) # Adjust query based on upper time boudary selection. - if 'dt_to' in args and args['dt_to']: - query = query.filter(model.dt_to <= args['dt_to']) + if 'dt_to' in form_args and form_args['dt_to']: + query = query.filter(model.dt_to <= form_args['dt_to']) # Restrict the result set only to summary reports. query = query.filter(model.type == mentat.const.REPORT_TYPE_SUMMARY) @@ -484,18 +482,23 @@ class DashboardView(hawat.base.HawatSearchView): # Return the result sorted by label. return query.order_by(model.label) - def do_before_render(self, item, context): + def do_after_search(self, items): """ - *Hook method*. Will be called before rendering the template. + *Interface implementation* of :py:func:`hawat.base.HawatSearchView.do_after_search`. """ - context.update( - statistics = mentat.stats.idea.truncate_stats( - mentat.stats.idea.aggregate_stats_reports(item) - ) + self.logger.debug( + "Calculating event reporting dashboard overview from %d records.", + len(items) ) + if items: + self.response_context.update( + statistics = mentat.stats.idea.truncate_stats( + mentat.stats.idea.aggregate_stats_reports(items) + ) + ) -class DeleteView(hawat.base.HawatItemDeleteView): +class DeleteView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -506,7 +509,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): authorization = [hawat.acl.PERMISSION_ADMIN] @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -523,7 +526,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return EventReportModel @@ -532,21 +535,21 @@ class DeleteView(hawat.base.HawatItemDeleteView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Event report <strong>%(item_id)s</strong> was successfully and permanently deleted.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to delete event report <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting event report <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -554,7 +557,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): #------------------------------------------------------------------------------- -class ReportsBlueprint(hawat.base.HawatBlueprint): +class ReportsBlueprint(HawatBlueprint): """ Hawat pluggable module - event reports. """ diff --git a/lib/hawat/blueprints/reports/templates/reports/dashboard.html b/lib/hawat/blueprints/reports/templates/reports/dashboard.html index 18a7fbd3b340d5e498017a00d0c60d38a64bda85..b7f8e0e744422650eb53902ef5e00a8ae48012ca 100644 --- a/lib/hawat/blueprints/reports/templates/reports/dashboard.html +++ b/lib/hawat/blueprints/reports/templates/reports/dashboard.html @@ -6,18 +6,18 @@ <div class="col-lg-12"> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <form method="GET" class="form" action="{{ url_for('reports.dashboard') }}"> <div class="row"> <div class="col-sm-4"> - {{ macros_site.render_form_item_select(search_form.groups) }} + {{ macros_site.render_form_item_select(g.search_form.groups) }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_datetime(search_form.dt_from, 'datetimepicker-hm-from') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_from, 'datetimepicker-hm-from') }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_datetime(search_form.dt_to, 'datetimepicker-hm-to') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_to, 'datetimepicker-hm-to') }} </div> </div> @@ -25,7 +25,7 @@ <div class="btn-toolbar" role="toolbar" aria-label="{{ gettext('Form submission buttons') }}"> <div class="btn-group" role="group"> - {{ search_form.submit(class_='btn btn-primary') }} + {{ g.search_form.submit(class_='btn btn-primary') }} <a role="button" class="btn btn-default" href="{{ url_for('reports.dashboard') }}">{{ gettext('Clear') }}</a> </div> </div> diff --git a/lib/hawat/blueprints/reports/templates/reports/search.html b/lib/hawat/blueprints/reports/templates/reports/search.html index 69602459ae23343e522459d84d61b17b6a688746..3bdb27f1b1b7c353f55aff6a984c146e85c97c91 100644 --- a/lib/hawat/blueprints/reports/templates/reports/search.html +++ b/lib/hawat/blueprints/reports/templates/reports/search.html @@ -4,24 +4,24 @@ <div class="row"> <div class="col-sm-4"> - {{ macros_site.render_form_item_default(search_form.label) }} + {{ macros_site.render_form_item_default(g.search_form.label) }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_datetime(search_form.dt_from, 'datetimepicker-hm-from') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_from, 'datetimepicker-hm-from') }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_datetime(search_form.dt_to, 'datetimepicker-hm-to') }} + {{ macros_site.render_form_item_datetime(g.search_form.dt_to, 'datetimepicker-hm-to') }} </div> </div> <div class="row"> <div class="col-sm-4"> - {{ macros_site.render_form_item_select(search_form.groups) }} + {{ macros_site.render_form_item_select(g.search_form.groups) }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_select(search_form.severities) }} + {{ macros_site.render_form_item_select(g.search_form.severities) }} </div> <div class="col-sm-4"> - {{ macros_site.render_form_item_select(search_form.types) }} + {{ macros_site.render_form_item_select(g.search_form.types) }} </div> </div> @@ -34,7 +34,7 @@ <li class="list-group-item"> <h4 class="list-group-item-heading"> <div class="pull-right"> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_site.render_menu_context_actions(hawat_current_view.get_context_action_menu(), item) }} </div> {{ item.label }} <hr> @@ -42,16 +42,16 @@ {{ macros_site.render_report_label_severity(item) }} {{ macros_site.render_report_label_weight(item) }} {%- if item.evcount_flt_blk %} - <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Some of the data for this report were filtered out by reporting filters') }}" >{{ get_fa_icon('report-data-filtered') }}</span> + <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Some of the data for this report were filtered out by reporting filters') }}" >{{ get_icon('report-data-filtered') }}</span> {%- endif %} {%- if item.evcount_rlp %} - <span class="label label-danger" data-toggle="tooltip" title="{{ gettext('Report contains relapsed events') }}" >{{ get_fa_icon('report-data-relapsed') }}</span> + <span class="label label-danger" data-toggle="tooltip" title="{{ gettext('Report contains relapsed events') }}" >{{ get_icon('report-data-relapsed') }}</span> {%- endif %} {%- if item.flag_testdata %} - <span class="label label-default" title="{{ gettext('Report was generated from test data') }}" data-toggle="tooltip">{{ get_fa_icon('report-data-test') }}</span> + <span class="label label-default" title="{{ gettext('Report was generated from test data') }}" data-toggle="tooltip">{{ get_icon('report-data-test') }}</span> {%- endif %} {%- if item.flag_mailed %} - <span class="label label-default" title="{{ gettext('Report was mailed at') }} {{ babel_format_datetime(item.mail_dt) }} ({{ gettext('before') }} {{ babel_format_timedelta(current_datetime_utc - item.mail_dt) }}){% if item.mail_to %} {{ gettext('to') }} {{ item.mail_to | join(', ') }}{% endif %}" data-toggle="tooltip">{{ get_fa_icon('report-data-mailed') }}</span> + <span class="label label-default" title="{{ gettext('Report was mailed at') }} {{ babel_format_datetime(item.mail_dt) }} ({{ gettext('before') }} {{ babel_format_timedelta(current_datetime_utc - item.mail_dt) }}){% if item.mail_to %} {{ gettext('to') }} {{ item.mail_to | join(', ') }}{% endif %}" data-toggle="tooltip">{{ get_icon('report-data-mailed') }}</span> {%- endif %} </h4> <hr> diff --git a/lib/hawat/blueprints/reports/templates/reports/show.html b/lib/hawat/blueprints/reports/templates/reports/show.html index e488016852d460e0ea3bc064156ecfb99ca70e6c..4b491b91cf917eb4b8beca76acb154073f52f29a 100644 --- a/lib/hawat/blueprints/reports/templates/reports/show.html +++ b/lib/hawat/blueprints/reports/templates/reports/show.html @@ -13,7 +13,7 @@ </ol> {%- endif %} - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3> {{ item.label }} @@ -21,20 +21,20 @@ {{ macros_site.render_report_label_severity(item) }} {{ macros_site.render_report_label_weight(item) }} {%- if item.evcount_flt_blk %} - <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Some of the data for this report were filtered out by reporting filters') }}" >{{ get_fa_icon('report-data-filtered') }}</span> + <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Some of the data for this report were filtered out by reporting filters') }}" >{{ get_icon('report-data-filtered') }}</span> {%- endif %} {%- if item.evcount_rlp %} - <span class="label label-danger" data-toggle="tooltip" title="{{ gettext('Report contains relapsed events') }}" >{{ get_fa_icon('report-data-relapsed') }}</span> + <span class="label label-danger" data-toggle="tooltip" title="{{ gettext('Report contains relapsed events') }}" >{{ get_icon('report-data-relapsed') }}</span> {%- endif %} {%- if item.flag_testdata %} - <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Report was generated from test data') }}" >{{ get_fa_icon('report-data-test') }}</span> + <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Report was generated from test data') }}" >{{ get_icon('report-data-test') }}</span> {%- endif %} {%- if item.flag_mailed %} - <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Report was mailed at') }} {{ babel_format_datetime(item.mail_dt) }} ({{ gettext('before') }} {{ babel_format_timedelta(current_datetime_utc - item.mail_dt) }}){% if item.mail_to %} {{ gettext('to') }} {{ item.mail_to | join(', ') }}{% endif %}">{{ get_fa_icon('report-data-mailed') }}</span> + <span class="label label-default" data-toggle="tooltip" title="{{ gettext('Report was mailed at') }} {{ babel_format_datetime(item.mail_dt) }} ({{ gettext('before') }} {{ babel_format_timedelta(current_datetime_utc - item.mail_dt) }}){% if item.mail_to %} {{ gettext('to') }} {{ item.mail_to | join(', ') }}{% endif %}">{{ get_icon('report-data-mailed') }}</span> {%- endif %} </h3> {%- if item.flag_testdata %} - <p class="alert alert-warning">{{ get_fa_icon('debug') }} {{ gettext('This report was generated from test data.') }}</p> + <p class="alert alert-warning">{{ get_icon('debug') }} {{ gettext('This report was generated from test data.') }}</p> {%- endif %} <p> <strong>{{ gettext('Target abuse group') }}:</strong> @@ -57,7 +57,7 @@ <a href="{{ url_for('reports.unauth', item_id = item.label ) }}" data-toggle="tooltip" title="{{ gettext('Unprotected access to report "%(item)s"', item = item.label) }}">{{ item.label }}</a> </p> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} </div> <p> <small> @@ -236,7 +236,7 @@ <div role="tabpanel" class="tab-pane fade" id="tab-report-data-json"> <br> <button id="ajax-load-report-data" class="btn btn-default"> - {{ get_fa_icon('action-reload') }} {{ gettext('Load report data') }} + {{ get_icon('action-reload') }} {{ gettext('Load report data') }} </button> <div id="ajax-load-report-data-result" style="display: none"> <br> diff --git a/lib/hawat/blueprints/settings_reporting/__init__.py b/lib/hawat/blueprints/settings_reporting/__init__.py index d1c4a878a242533f9789efa53a3529816102e09e..811e3752755362022908c3b20062e7416f60153c 100644 --- a/lib/hawat/blueprints/settings_reporting/__init__.py +++ b/lib/hawat/blueprints/settings_reporting/__init__.py @@ -39,19 +39,18 @@ from flask_babel import gettext, lazy_gettext import mentat.reports.utils from mentat.datatype.sqldb import SettingsReportingModel, ItemChangeLogModel -import hawat.base import hawat.db +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView,\ + HawatItemCreateView, HawatItemUpdateView, HawatBlueprint from hawat.blueprints.settings_reporting.forms import CreateSettingsReportingForm,\ UpdateSettingsReportingForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'settings_reporting' +"""Name of the blueprint as module global constant.""" -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed reporting settings view. """ @@ -67,7 +66,7 @@ class ShowView(hawat.base.HawatItemShowView): return 'module-settings-reporting' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -78,7 +77,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return SettingsReportingModel @@ -152,20 +151,21 @@ class ShowView(hawat.base.HawatItemShowView): return action_menu - def do_before_render(self, item, context): + def do_before_response(self, **kwargs): """ *Hook method*. Implementation of :py:func:`hawat.base.HawatDbmodelView.do_before_render` interface. """ + item = self.response_context['item'] system_default_repsettings = mentat.reports.utils.ReportingSettings( item.group ) - context.update( + self.response_context.update( system_default_repsettings = system_default_repsettings ) - if self.can_access_endpoint('settings_reporting.update', item) and self.has_view_class('changelogs.search'): - context.update( - context_action_menu_changelogs = self.get_view_class('changelogs.search').get_context_action_menu() + if self.can_access_endpoint('settings_reporting.update', item) and self.has_endpoint('changelogs.search'): + self.response_context.update( + context_action_menu_changelogs = self.get_endpoint_class('changelogs.search').get_context_action_menu() ) item_changelog = self.dbsession.query(ItemChangeLogModel).\ @@ -174,10 +174,10 @@ class ShowView(hawat.base.HawatItemShowView): order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(item_changelog = item_changelog) + self.response_context.update(item_changelog = item_changelog) -class CreateView(hawat.base.HawatItemCreateView): +class CreateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new reporting settings. """ @@ -186,16 +186,16 @@ class CreateView(hawat.base.HawatItemCreateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create reporting settings') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new reporting settings') @@ -204,7 +204,7 @@ class CreateView(hawat.base.HawatItemCreateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return SettingsReportingModel @@ -223,33 +223,33 @@ class CreateView(hawat.base.HawatItemCreateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting settings <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> were successfully created.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new reporting settings for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new reporting settings for group <strong>%(parent_id)s</strong>.', parent_id = str(kwargs['item'].group)) @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ return CreateSettingsReportingForm() -class UpdateView(hawat.base.HawatItemUpdateView): +class UpdateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing reporting settings. """ @@ -258,16 +258,16 @@ class UpdateView(hawat.base.HawatItemUpdateView): authentication = True @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Update reporting settings') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Update reporting settings details') @@ -276,7 +276,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return SettingsReportingModel @@ -295,21 +295,21 @@ class UpdateView(hawat.base.HawatItemUpdateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('Reporting settings <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong> were successfully updated.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to update reporting settings <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled updating reporting settings <strong>%(item_id)s</strong> for group <strong>%(parent_id)s</strong>.', item_id = str(kwargs['item']), parent_id = str(kwargs['item'].group)) @@ -330,7 +330,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): #------------------------------------------------------------------------------- -class SettingsReportingBlueprint(hawat.base.HawatBlueprint): +class SettingsReportingBlueprint(HawatBlueprint): """ Hawat pluggable module - reporting settings. """ diff --git a/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/creatupdate.html b/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/creatupdate.html index fcea35f6bc0a776668257902fdfa8afad97fec66..065de10f3ad20c3c9cc5ad2ca2f47641af0f4036 100644 --- a/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/creatupdate.html +++ b/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/creatupdate.html @@ -16,7 +16,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {%- if item_action == 'create' %} {{ macros_site.render_form_item_default(form.group) }} diff --git a/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/show.html b/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/show.html index 70d1a93b8cbd79a3169feb69a6cb2f440b077959..697db1b2d1e1fc49c74ef6be649d76e38a136473 100644 --- a/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/show.html +++ b/lib/hawat/blueprints/settings_reporting/templates/settings_reporting/show.html @@ -3,14 +3,15 @@ {%- block content %} <div class="row"> - <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ gettext('Reporting settings for group %(item)s', item = item.group.name) }}{% if item.group.description %} <small>{{ item.group.description }}</small>{% endif %}</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} + </div> <p> <small> @@ -23,13 +24,13 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('alert-info') }} {{ gettext('General information') }}</strong> + <strong>{{ get_icon('alert-info') }} {{ gettext('General information') }}</strong> </a> </li> {%- if can_access_endpoint('settings_reporting.update', item) %} <li role="presentation"> <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> </a> </li> {%- endif %} diff --git a/lib/hawat/blueprints/skeleton/__init__.py b/lib/hawat/blueprints/skeleton/__init__.py index 71a7296c3753fa3ff99202b54cf25fe64485f447..776876e4cdef52dead929958e78a15866517be62 100644 --- a/lib/hawat/blueprints/skeleton/__init__.py +++ b/lib/hawat/blueprints/skeleton/__init__.py @@ -21,7 +21,6 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" -import flask import flask_login from flask_babel import lazy_gettext @@ -29,13 +28,11 @@ import hawat.base import hawat.db -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'skeleton' +"""Name of the blueprint as module global constant.""" -class ExampleView(hawat.base.HawatSimpleView): +class ExampleView(hawat.base.HTMLViewMixin, hawat.base.HawatSimpleView): """ Example simple view. """ @@ -57,16 +54,16 @@ class ExampleView(hawat.base.HawatSimpleView): return 'example' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return 'Example view' @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return 'Example view' @@ -74,15 +71,11 @@ class ExampleView(hawat.base.HawatSimpleView): def get_view_template(cls): return '{}/example.html'.format(BLUEPRINT_NAME) - def get_template_context(self): - context = super().get_template_context() - - return context - - def dispatch_request(self): - context = self.get_template_context() - - return flask.render_template(self.get_view_template(), **context) + def do_before_response(self, **kwargs): + """ + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. + """ + pass #------------------------------------------------------------------------------- diff --git a/lib/hawat/blueprints/skeleton/templates/skeleton/search.html b/lib/hawat/blueprints/skeleton/templates/skeleton/search.html index 82e34ab35983172937643ea801fe564c5f39079b..b38b74718a7ce114a06192dc4fada81d138b39b2 100644 --- a/lib/hawat/blueprints/skeleton/templates/skeleton/search.html +++ b/lib/hawat/blueprints/skeleton/templates/skeleton/search.html @@ -4,13 +4,13 @@ <div class="row"> <div class="col-lg-12"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <form class="form-inline" id="frm-whi" method="get" action="{{ url_for('whois.search') }}"> <div class="form-group"> <label class="sr-only" for="frm-whi-search">Search local whois:</label> <div class="input-group"> - <div data-toggle="tooltip" class="input-group-addon" title="Search local whois">{{ get_fa_icon('search') }}</div> + <div data-toggle="tooltip" class="input-group-addon" title="Search local whois">{{ get_icon('search') }}</div> <input class="form-control" type="search" id="frm-whi-search" name="search" placeholder="IP address, range, network or abuse email" value="" size="40"> </div> </div> diff --git a/lib/hawat/blueprints/status/__init__.py b/lib/hawat/blueprints/status/__init__.py index c846d1b6c2653ac4711fb2ff3c68fc9e13b27d18..1518dabcac8a63496cd0796180128b9d8abc4d78 100644 --- a/lib/hawat/blueprints/status/__init__.py +++ b/lib/hawat/blueprints/status/__init__.py @@ -35,21 +35,26 @@ __author__ = "Jan Mach <jan.mach@cesnet.cz>" __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>" +# +# Flask related modules. +# import flask from flask_babel import lazy_gettext +# +# Custom modules. +# import pyzenkit.jsonconf -import hawat.base import mentat.system +import hawat.acl +from hawat.base import HTMLViewMixin, HawatSimpleView, HawatBlueprint -# -# Name of the blueprint as global constant. -# BLUEPRINT_NAME = 'status' +"""Name of the blueprint as module global constant.""" -class ViewView(hawat.base.HawatSimpleView): +class ViewView(HTMLViewMixin, HawatSimpleView): """ Application view providing access Mentat system status information. """ @@ -72,58 +77,53 @@ class ViewView(hawat.base.HawatSimpleView): return 'module-{}'.format(BLUEPRINT_NAME) @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('System status') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('System status') - def get_template_context(self): + def do_before_response(self, **kwargs): """ - Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. - Will be called by the *Flask* framework to service the request. + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ - context = super().get_template_context() - controller_cfg = pyzenkit.jsonconf.json_load( flask.current_app.config['MENTAT_CONTROLLER_CFG'], ) - context['mentat_modules'] = mentat.system.make_module_list( + self.response_context['mentat_modules'] = mentat.system.make_module_list( controller_cfg.get('modules', {}) ) - context['mentat_cronjobs'] = mentat.system.make_cronjob_list( + self.response_context['mentat_cronjobs'] = mentat.system.make_cronjob_list( controller_cfg.get('cronjobs', {}) ) - context['mentat_runlogs'] = mentat.system.analyze_runlog_files( + self.response_context['mentat_runlogs'] = mentat.system.analyze_runlog_files( flask.current_app.config['MENTAT_PATHS']['path_run'], limit = 20 ) - context['mentat_status'] = mentat.system.system_status( - context['mentat_modules'], - context['mentat_cronjobs'], + self.response_context['mentat_status'] = mentat.system.system_status( + self.response_context['mentat_modules'], + self.response_context['mentat_cronjobs'], flask.current_app.config['MENTAT_PATHS']['path_cfg'], flask.current_app.config['MENTAT_PATHS']['path_crn'], flask.current_app.config['MENTAT_PATHS']['path_log'], flask.current_app.config['MENTAT_PATHS']['path_run'] ) - return context - #------------------------------------------------------------------------------- -class StatusBlueprint(hawat.base.HawatBlueprint): +class StatusBlueprint(HawatBlueprint): """ Hawat pluggable module - Mentat system status. """ diff --git a/lib/hawat/blueprints/status/templates/status/view.html b/lib/hawat/blueprints/status/templates/status/view.html index 91172cdde82c4100cc1cf6322dbde08fa7511a9c..495856a7530119648a1c33ac5fa5acdbe6580f81 100644 --- a/lib/hawat/blueprints/status/templates/status/view.html +++ b/lib/hawat/blueprints/status/templates/status/view.html @@ -8,7 +8,7 @@ <li><a href="{{ url_for('index') }}">{{ gettext('Home') }}</a></li> <li class="active">{{ gettext('System status') }}</li> </ol> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <p> <strong>{{ gettext('Overall status:')}}</strong> diff --git a/lib/hawat/blueprints/users/__init__.py b/lib/hawat/blueprints/users/__init__.py index 4d4cfda659b02926e22027a1f5ea6f2941d2974c..a45fb32d6f67fd286d1d21c28184a8e7286e81f4 100644 --- a/lib/hawat/blueprints/users/__init__.py +++ b/lib/hawat/blueprints/users/__init__.py @@ -41,23 +41,24 @@ from flask_babel import gettext, lazy_gettext, force_locale # # Custom modules. # +from mentat.datatype.sqldb import ItemChangeLogModel + import hawat.const -import hawat.base import hawat.db import hawat.acl -from mentat.datatype.sqldb import ItemChangeLogModel +from hawat.base import HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView,\ + HawatItemShowView, HawatItemCreateView, HawatItemUpdateView, HawatItemEnableView,\ + HawatItemDisableView, HawatItemDeleteView, HawatBlueprint from hawat.models.user import GuiUserModel from hawat.blueprints.users.forms import CreateUserAccountForm, UpdateUserAccountForm,\ AdminUpdateUserAccountForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'users' +"""Name of the blueprint as module global constant.""" -class ListView(hawat.base.HawatItemListView): +class ListView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemListView): """ General user account listing. """ @@ -68,14 +69,7 @@ class ListView(hawat.base.HawatItemListView): authorization = [hawat.acl.PERMISSION_POWER] @classmethod - def get_menu_icon(cls): - """ - *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_icon`. - """ - return 'module-{}'.format(BLUEPRINT_NAME) - - @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -86,33 +80,30 @@ class ListView(hawat.base.HawatItemListView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @classmethod - def get_action_menu(cls): + def get_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'create', endpoint = 'users.create', resptitle = True ) - return action_menu @classmethod - def get_context_action_menu(cls): + def get_context_action_menu(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatItemListView.get_context_action_menu`. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'show', @@ -151,11 +142,10 @@ class ListView(hawat.base.HawatItemListView): icon = 'action-delete-user', legend = lambda x: lazy_gettext('Delete user account "%(item)s"', item = x.login) ) - return action_menu -class ShowView(hawat.base.HawatItemShowView): +class ShowView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemShowView): """ Detailed user account view. """ @@ -171,16 +161,16 @@ class ShowView(hawat.base.HawatItemShowView): return 'action-show-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Show user account') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Show user account details') @@ -189,7 +179,7 @@ class ShowView(hawat.base.HawatItemShowView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -213,7 +203,6 @@ class ShowView(hawat.base.HawatItemShowView): Get action menu for particular item. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'endpoint', 'update', @@ -246,12 +235,11 @@ class ShowView(hawat.base.HawatItemShowView): title = lazy_gettext('Delete'), legend = lazy_gettext('Delete user account "%(item)s"', item = item.login) ) - return action_menu - def do_before_render(self, item, context): # pylint: disable=locally-disabled,no-self-use,unused-argument + def do_before_response(self, **kwargs): # pylint: disable=locally-disabled,no-self-use,unused-argument """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.do_before_render`. + *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.do_before_response`. """ action_menu = hawat.menu.HawatMenu() action_menu.add_entry( @@ -261,32 +249,32 @@ class ShowView(hawat.base.HawatItemShowView): hidetitle = True, legend = lambda x: lazy_gettext('View details of group "%(item)s"', item = str(x)) ) - context.update(context_action_menu_groups = action_menu) + self.response_context.update(context_action_menu_groups = action_menu) - if self.has_view_class('changelogs.search'): - context.update( - context_action_menu_changelogs = self.get_view_class('changelogs.search').get_context_action_menu() + if self.has_endpoint('changelogs.search'): + self.response_context.update( + context_action_menu_changelogs = self.get_endpoint_class('changelogs.search').get_context_action_menu() ) - if self.can_access_endpoint('users.update', item) and self.has_view_class('changelogs.search'): + if self.can_access_endpoint('users.update', self.response_context['item']) and self.has_endpoint('changelogs.search'): item_changelog = self.dbsession.query(ItemChangeLogModel).\ - filter(ItemChangeLogModel.model == item.__class__.__name__).\ - filter(ItemChangeLogModel.model_id == item.id).\ + filter(ItemChangeLogModel.model == self.response_context['item'].__class__.__name__).\ + filter(ItemChangeLogModel.model_id == self.response_context['item'].id).\ order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(item_changelog = item_changelog) + self.response_context.update(item_changelog = item_changelog) user_changelog = self.dbsession.query(ItemChangeLogModel).\ - filter(ItemChangeLogModel.author_id == item.id).\ + filter(ItemChangeLogModel.author_id == self.response_context['item'].id).\ order_by(ItemChangeLogModel.createtime.desc()).\ limit(100).\ all() - context.update(user_changelog = user_changelog) + self.response_context.update(user_changelog = user_changelog) -class MeView(ShowView): +class MeView(ShowView): # pylint: disable=locally-disabled,too-many-ancestors """ Detailed user account view for currently logged-in user. """ @@ -309,16 +297,16 @@ class MeView(ShowView): return 'profile' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('My account') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('My user account') @@ -342,7 +330,6 @@ class MeView(ShowView): Get breadcrumbs menu. """ action_menu = hawat.menu.HawatMenu() - action_menu.add_entry( 'link', 'index', @@ -354,7 +341,6 @@ class MeView(ShowView): 'show', endpoint = '{}.me'.format(cls.module_name) ) - return action_menu #--------------------------------------------------------------------------- @@ -368,22 +354,23 @@ class MeView(ShowView): and injected into template to be displayed to the user. """ item_id = flask_login.current_user.get_id() - item = self.dbquery.filter(self.dbmodel.id == item_id).first_or_404() + item = self.dbquery().filter(self.dbmodel.id == item_id).first() + if not item: + self.abort(404) - context = self.get_template_context() - context.update( + self.response_context.update( item_id = item_id, item = item, breadcrumbs_menu = self.get_breadcrumbs_menu(item), action_menu = self.get_action_menu(item) ) - self.do_before_render(item, context) + self.do_before_response() - return flask.render_template(self.get_view_template(), **context) + return self.generate_response() -class CreateView(hawat.base.HawatItemCreateView): +class CreateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new user accounts. """ @@ -401,16 +388,16 @@ class CreateView(hawat.base.HawatItemCreateView): return 'action-create-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Create user account') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Create new user account') @@ -419,7 +406,7 @@ class CreateView(hawat.base.HawatItemCreateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -428,28 +415,28 @@ class CreateView(hawat.base.HawatItemCreateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('User account <strong>%(item_id)s</strong> was successfully created.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to create new user account.') @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled creating new user account.') @staticmethod def get_item_form(): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemCreateView.get_item_form` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemCreateView.get_item_form`. """ # # Inject list of choices for supported locales and roles. Another approach @@ -465,7 +452,7 @@ class CreateView(hawat.base.HawatItemCreateView): ) -class UpdateView(hawat.base.HawatItemUpdateView): +class UpdateView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing user accounts. """ @@ -481,16 +468,16 @@ class UpdateView(hawat.base.HawatItemUpdateView): return 'action-update-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Update user account') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Update user account details') @@ -499,7 +486,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -518,21 +505,21 @@ class UpdateView(hawat.base.HawatItemUpdateView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('User account <strong>%(item_id)s</strong> was successfully updated.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to update user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled updating user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -566,7 +553,7 @@ class UpdateView(hawat.base.HawatItemUpdateView): return form -class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors +class EnableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for enabling existing user accounts. """ @@ -584,7 +571,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis return 'action-enable-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -595,7 +582,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -604,29 +591,29 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('User account <strong>%(item_id)s</strong> was successfully enabled.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to enable user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled enabling user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) #--------------------------------------------------------------------------- - def do_after_action(self, item, context): + def do_after_action(self, item): """ - *Hook method*. Will be called after successfull action. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.do_after_action`. """ mail_locale = item.locale if not mail_locale: @@ -648,7 +635,7 @@ class EnableView(hawat.base.HawatItemEnableView): # pylint: disable=locally-dis flask.current_app.mailer.send(msg) -class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors +class DisableView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -666,7 +653,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d return 'action-disable-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -677,7 +664,7 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -686,26 +673,26 @@ class DisableView(hawat.base.HawatItemDisableView): # pylint: disable=locally-d @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('User account <strong>%(item_id)s</strong> was successfully disabled.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to disable user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled disabling user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) -class DeleteView(hawat.base.HawatItemDeleteView): +class DeleteView(HTMLViewMixin, SQLAlchemyViewMixin, HawatItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing user accounts. """ @@ -723,7 +710,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): return 'action-delete-user' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ @@ -734,7 +721,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): @property def dbmodel(self): """ - *Interface implementation* of :py:func:`hawat.base.HawatDbmodelView.dbmodel`. + *Interface implementation* of :py:func:`hawat.base.SQLAlchemyViewMixin.dbmodel`. """ return GuiUserModel @@ -743,21 +730,21 @@ class DeleteView(hawat.base.HawatItemDeleteView): @staticmethod def get_message_success(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_success` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_success`. """ return gettext('User account <strong>%(item_id)s</strong> was successfully and permanently deleted.', item_id = str(kwargs['item'])) @staticmethod def get_message_failure(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_failure` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_failure`. """ return gettext('Unable to delete user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @staticmethod def get_message_cancel(**kwargs): """ - *Hook method*. Implementation of :py:func:`hawat.base.HawatItemActionView.get_message_cancel` interface. + *Interface implementation* of :py:func:`hawat.base.HawatItemActionView.get_message_cancel`. """ return gettext('Canceled deleting user account <strong>%(item_id)s</strong>.', item_id = str(kwargs['item'])) @@ -765,7 +752,7 @@ class DeleteView(hawat.base.HawatItemDeleteView): #------------------------------------------------------------------------------- -class UsersBlueprint(hawat.base.HawatBlueprint): +class UsersBlueprint(HawatBlueprint): """ Hawat pluggable module - users. """ diff --git a/lib/hawat/blueprints/users/templates/users/creatupdate.html b/lib/hawat/blueprints/users/templates/users/creatupdate.html index 6d1106989e2cee68e426ae066d09e15f57281c45..ec72844dabf202f9c3b565999e08da25c17030ca 100644 --- a/lib/hawat/blueprints/users/templates/users/creatupdate.html +++ b/lib/hawat/blueprints/users/templates/users/creatupdate.html @@ -17,7 +17,7 @@ <form method="POST" action="{{ form_url }}"> <fieldset> - <legend>{{ hawat_this_view_title }}</legend> + <legend>{{ hawat_current_view.get_view_title() }}</legend> {%- if item_action == 'create' or current_user.has_role('admin') %} {{ macros_site.render_form_item_default(form.login) }} diff --git a/lib/hawat/blueprints/users/templates/users/list.html b/lib/hawat/blueprints/users/templates/users/list.html index 606441aa6da10c3b74e101f88c6dec9f4b4a497d..a8269c75530ab36ad61f4de94d4e5db5aae3bab5 100644 --- a/lib/hawat/blueprints/users/templates/users/list.html +++ b/lib/hawat/blueprints/users/templates/users/list.html @@ -21,7 +21,7 @@ {{ gettext('State') }} </th> <th data-toggle="tooltip" title="{{ gettext('Contextual item actions') }}"> - {{ get_fa_icon('actions') }} {{ gettext('Actions') }} + {{ get_icon('actions') }} {{ gettext('Actions') }} </th> </tr> </thead> @@ -44,7 +44,7 @@ {{ macros_site.render_label_item_state(item.enabled, True) }} </td> <td class="column-actions"> - {{ macros_site.render_menu_context_actions(context_action_menu, item) }} + {{ macros_page.render_menu_context_actions(item) }} </td> </tr> {%- endfor %} diff --git a/lib/hawat/blueprints/users/templates/users/show.html b/lib/hawat/blueprints/users/templates/users/show.html index e0678ac9762453ee89fb37159f994958c6510e28..f34d9bbad55466f9846f892ab30cc6651c29841e 100644 --- a/lib/hawat/blueprints/users/templates/users/show.html +++ b/lib/hawat/blueprints/users/templates/users/show.html @@ -4,12 +4,14 @@ <div class="row"> <div class="col-lg-12"> - {{ macros_site.render_menu_breadcrumbs(breadcrumbs_menu) }} - <h2>{{ hawat_this_view_title }}</h2> + {{ macros_page.render_breadcrumbs(item) }} + + <h2>{{ hawat_current_view.get_view_title() }}</h2> <hr> <h3>{{ item.fullname }} ({{ item.login }})</h3> <div class="pull-right"> - {{ macros_site.render_menu_actions(action_menu) }} + {{ macros_page.render_menu_actions(item) }} + </div> <p> <small> @@ -24,20 +26,20 @@ <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('alert-info') }} {{ gettext('General information') }}</strong> + <strong>{{ get_icon('alert-info') }} {{ gettext('General information') }}</strong> </a> </li> {%- if can_access_endpoint('users.update', item) %} <li role="presentation"> <a href="#tab-actionlog" aria-controls="tab-actionlog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Actionlogs') }}</strong> <span class="badge">{{ user_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Actionlogs') }}</strong> <span class="badge">{{ user_changelog | length }}</span> </a> </li> {%- endif %} {%- if can_access_endpoint('users.update', item) %} <li role="presentation"> <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab"> - <strong>{{ get_fa_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> + <strong>{{ get_icon('module-changelogs') }} {{ gettext('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span> </a> </li> {%- endif %} @@ -117,13 +119,13 @@ <input type="text" value="{{ item.apikey }}" readonly="readonly" class="form-control"> <span class="input-group-btn"> <a role="button" class="btn btn-default" href="{{ url_for('auth_api.key-delete', item_id = item.id, next = request.url) }}" data-toggle="tooltip" title="{{ get_endpoint_class('auth_api.key-delete').get_menu_legend() }}"> - {{ get_fa_icon(get_endpoint_class('auth_api.key-delete').get_menu_icon()) }} + {{ get_icon(get_endpoint_class('auth_api.key-delete').get_menu_icon()) }} </a> </span> </div> {%- else %} <a role="button" class="btn btn-default btn-sm" href="{{ url_for('auth_api.key-generate', item_id = item.id, next = request.url) }}" data-toggle="tooltip" title="{{ get_endpoint_class('auth_api.key-generate').get_menu_legend() }}"> - {{ get_fa_icon(get_endpoint_class('auth_api.key-generate').get_menu_icon()) }} {{ get_endpoint_class('auth_api.key-generate').get_menu_title()}} + {{ get_icon(get_endpoint_class('auth_api.key-generate').get_menu_icon()) }} {{ get_endpoint_class('auth_api.key-generate').get_menu_title()}} </a> {%- endif %} </td> diff --git a/lib/hawat/blueprints/whois/__init__.py b/lib/hawat/blueprints/whois/__init__.py index 03185f3072d87285c484f3c0adbb10d2c569331a..2b42f4e2a3182d8881cbe1c1faffa382b8350549 100644 --- a/lib/hawat/blueprints/whois/__init__.py +++ b/lib/hawat/blueprints/whois/__init__.py @@ -49,19 +49,17 @@ from flask_babel import lazy_gettext # Custom modules. # import mentat.services.whois -import hawat.base import hawat.db import hawat.acl +from hawat.base import HTMLViewMixin, HawatRenderableView, HawatBlueprint from hawat.blueprints.whois.forms import WhoisSearchForm -# -# Name of the blueprint as module global constant. -# BLUEPRINT_NAME = 'whois' +"""Name of the blueprint as module global constant.""" -class SearchView(hawat.base.HawatRenderableView): +class SearchView(HTMLViewMixin, HawatRenderableView): """ Application view providing search form for internal whois resolving service and appropriate result page. @@ -82,16 +80,16 @@ class SearchView(hawat.base.HawatRenderableView): return 'search' @classmethod - def get_menu_title(cls): + def get_menu_title(cls, item = None): """ *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_menu_title`. """ return lazy_gettext('Internal whois') @classmethod - def get_view_title(cls): + def get_view_title(cls, item = None): """ - *Interface implementation* of :py:func:`hawat.base.HawatRenderableView.get_view_title`. + *Interface implementation* of :py:func:`hawat.base.HawatBaseView.get_view_title`. """ return lazy_gettext('Search internal whois database') @@ -102,8 +100,6 @@ class SearchView(hawat.base.HawatRenderableView): Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`. Will be called by the *Flask* framework to service the request. """ - context = self.get_template_context() - form = WhoisSearchForm(flask.request.args, meta = {'csrf': False}) if hawat.const.HAWAT_FORM_ACTION_SUBMIT in flask.request.args: @@ -111,23 +107,23 @@ class SearchView(hawat.base.HawatRenderableView): form_data = form.data whois_manager = mentat.services.whois.WhoisServiceManager(flask.current_app.mconfig) whois_service = whois_manager.service() - context.update( + self.response_context.update( search_item = form.search.data, search_result = whois_service.lookup(form.search.data), form_data = form_data ) - context.update( + self.response_context.update( search_form = form, request_args = flask.request.args, ) - return flask.render_template(self.get_view_template(), **context) + return self.generate_response() #------------------------------------------------------------------------------- -class WhoisBlueprint(hawat.base.HawatBlueprint): +class WhoisBlueprint(HawatBlueprint): """ Hawat pluggable module - whois resolving service. """ diff --git a/lib/hawat/blueprints/whois/templates/whois/search.html b/lib/hawat/blueprints/whois/templates/whois/search.html index a33e46cc242b6ec5f0ea98baffae20d5c3630506..ec21b6ae2527935449eeb17453eb7908e0914249 100644 --- a/lib/hawat/blueprints/whois/templates/whois/search.html +++ b/lib/hawat/blueprints/whois/templates/whois/search.html @@ -5,12 +5,12 @@ <div class="col-lg-12"> <div class="jumbotron" style="margin-top: 1em;"> - <h2>{{ hawat_this_view_title }}</h2> + <h2>{{ hawat_current_view.get_view_title() }}</h2> <form method="GET" class="form-inline" action="{{ url_for('whois.search') }}"> <div class="form-group{% if search_form.search.errors %}{{ ' has-error' }}{% endif %}"> {{ search_form.search.label(class_='sr-only') }} <div class="input-group"> - <div data-toggle="tooltip" class="input-group-addon" title="{{ gettext('Search local whois database for:') }}">{{ get_fa_icon('action-search') }}</div> + <div data-toggle="tooltip" class="input-group-addon" title="{{ gettext('Search local whois database for:') }}">{{ get_icon('action-search') }}</div> {{ search_form.search(class_='form-control', placeholder=gettext('IP address, range, network or abuse email'), size='50') }} </div> diff --git a/lib/hawat/forms.py b/lib/hawat/forms.py index 34a0522d15afcbd5814da33df9a06d3e1e983f49..76006a11e600bc0f86dd9be03f6e15f39b6442c1 100644 --- a/lib/hawat/forms.py +++ b/lib/hawat/forms.py @@ -86,7 +86,10 @@ def str_to_int_with_none(value): raise ValueError('Invalid string value {} to be converted to integer'.format(str(value))) -def is_safe_url(target): +#------------------------------------------------------------------------------- + + +def _is_safe_url(target): """ Check, if the URL is safe enough to be redirected to. """ @@ -95,8 +98,7 @@ def is_safe_url(target): return test_url.scheme in ('http', 'https') and \ ref_url.netloc == test_url.netloc - -def is_same_path(first, second): +def _is_same_path(first, second): """ Check, if both URL point to same path. """ @@ -104,39 +106,33 @@ def is_same_path(first, second): second_url = urllib.parse.urlparse(second) return first_url.path == second_url.path - -def get_redirect_target(): +def get_redirect_target(target_url = None, default_url = None, exclude_url = None): """ Get redirection target, either from GET request variable, or from referrer header. """ - for target in flask.request.args.get('next'), flask.request.referrer: + options = ( + target_url, + flask.request.form.get('next'), + flask.request.args.get('next'), + flask.request.referrer, + default_url, + flask.url_for('index') + ) + for target in options: if not target: continue - if is_same_path(target, flask.request.base_url): + if _is_same_path(target, flask.request.base_url): + continue + if exclude_url and _is_same_path(target, exclude_url): continue - if is_safe_url(target): + if _is_safe_url(target): return target - - return None - -def flask_redirect(default_url = None, exclude_url = None): - """ - Perfom HTTP redirection to designated target URL. - """ - target_url = get_redirect_target() - if target_url: - if not exclude_url or not is_same_path(target_url, exclude_url): - return flask.redirect(target_url) - - if default_url: - if not exclude_url or not is_same_path(default_url, exclude_url): - return flask.redirect(default_url) - - return flask.redirect(flask.url_for('index')) + raise RuntimeError("Unable to choose apropriate redirection target.") #------------------------------------------------------------------------------- + RE_LOGIN = re.compile('^[-_@.a-zA-Z0-9]+$') """Compiled regular expression for login validation.""" @@ -381,16 +377,6 @@ class BaseItemForm(flask_wtf.FlaskForm): if not self.next.data: self.next.data = get_redirect_target() or '' - def flask_redirect(self, default_url = None, exclude_url = None): - """ - Perfom HTTP redirection to designated target URL. - """ - if self.next.data and is_safe_url(self.next.data): - if not exclude_url or not is_same_path(self.next.data, exclude_url): - return flask.redirect(self.next.data) - - return flask_redirect(default_url, exclude_url) - @staticmethod def is_multivalue(field_name): # pylint: disable=locally-disabled,unused-argument """ diff --git a/lib/hawat/menu.py b/lib/hawat/menu.py index 9d961c6737ccd4ab0e8e2c3f3dbbfab4eb4d28a0..c3668b0266896ec820368ea670cf66194ca08043 100644 --- a/lib/hawat/menu.py +++ b/lib/hawat/menu.py @@ -434,7 +434,7 @@ class HawatEndpointMenuEntry(HawatViewMenuEntry): """ def __init__(self, ident, endpoint, **kwargs): - kwargs['view'] = flask.current_app.get_view_class(endpoint) + kwargs['view'] = flask.current_app.get_endpoint_class(endpoint) super().__init__(ident, **kwargs) diff --git a/lib/hawat/templates/index.html b/lib/hawat/templates/index.html index 4d50ff81ab382dcfb61942ec3cf459bffc81967c..1fd0c5f5dba88dfc2fe5b4dbb068aea835e3839e 100644 --- a/lib/hawat/templates/index.html +++ b/lib/hawat/templates/index.html @@ -19,8 +19,8 @@ <br> <div class="text-center"> <div class="btn-group" role="group"> - <a class="btn btn-default btn-lg" href="{{ url_for('auth_env.register') }}" role="button">{{ get_fa_icon('register') }} {{ gettext('Register') }}</a> - <a class="btn btn-primary btn-lg" href="{{ url_for('auth_env.login') }}" role="button">{{ get_fa_icon('login') }} {{ gettext('Login') }}</a> + <a class="btn btn-default btn-lg" href="{{ url_for('auth_env.register') }}" role="button">{{ get_icon('register') }} {{ gettext('Register') }}</a> + <a class="btn btn-primary btn-lg" href="{{ url_for('auth_env.login') }}" role="button">{{ get_icon('login') }} {{ gettext('Login') }}</a> </div> </div> {% endif %}