view src/tools/trigger.py @ 2169:f472179305a1

tools(templates): workflow improvments: - template theme can be specified in parenthesis: (some_theme)path/to/template.html. Withtout parenthesis, "default" is used - static content are supposed to be in [theme]/static, error pages in [theme]/error/[err_code].html - default page are used in some case (2 for now): if error page with specified code doesn't exists, a base page is used, and if a page doesn't exist for a theme, the same one for default theme is looked for - CSS files are automatically found for HTML pages - CSS files can be split, the'll be added in the template according to the page requested. - theme CSS file is looked for, and if not found the default theme equivalent is looked for. - each element of a path can be associated to a CSS file, and styles.css is always there. For instance if blog/articles.html is requested, the following CSS can be included: "styles.css", "blog.css", "blog_article.css". They all must be in /static - if the automatic finding of CSS files is not wanted, css_files arguments can be used instead, with full relative path (i.e. including theme) - CSS files can be merged and included inline with css_inline argument - root_path can be specified, it will be used as a prefix for static files - requested theme (which may differ from actual theme, e.g. if the template is not found and default one is used instead) is available in template with "theme" variable - added getThemeAndRoot method to retrieve theme and theme root path from template
author Goffi <goffi@goffi.org>
date Sun, 05 Mar 2017 23:41:10 +0100
parents 2daf7b4c6756
children 8b37a62336c3
line wrap: on
line source

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

# SAT: a jabber client
# Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Misc usefull classes"""

from sat.core.i18n import _
from sat.core.log import getLogger
log = getLogger(__name__)


class TriggerException(Exception):
    pass


class SkipOtherTriggers(Exception):
    """ Exception to raise if normal behaviour must be followed instead of
        following triggers list """
    pass


class TriggerManager(object):
    """This class manage triggers: code which interact to change the behaviour
    of SàT"""

    try:  # FIXME: to be removed when a better solution is found
        MIN_PRIORITY = float('-inf')
        MAX_PRIORITY = float('+inf')
    except:  # XXX: Pyjamas will bug if you specify ValueError here
        # Pyjamas uses the JS Float class
        MIN_PRIORITY = Number.NEGATIVE_INFINITY
        MAX_PRIORITY = Number.POSITIVE_INFINITY

    def __init__(self):
        self.__triggers = {}

    def add(self, point_name, callback, priority=0):
        """Add a trigger to a point
        @param point_name: name of the point when the trigger should be run
        @param callback: method to call at the trigger point
        @param priority: callback will be called in priority order, biggest
        first
        """
        if point_name not in self.__triggers:
            self.__triggers[point_name] = []
        if priority != 0 and priority in [trigger_tuple[0] for trigger_tuple in self.__triggers[point_name]]:
            if priority in (self.MIN_PRIORITY, self.MAX_PRIORITY):
                log.warning(_(u"There is already a bound priority [%s]") % point_name)
            else:
                log.debug(_(u"There is already a trigger with the same priority [%s]") % point_name)
        self.__triggers[point_name].append((priority, callback))
        self.__triggers[point_name].sort(key=lambda trigger_tuple:
                                         trigger_tuple[0], reverse=True)

    def remove(self, point_name, callback):
        """Remove a trigger from a point
        @param point_name: name of the point when the trigger should be run
        @param callback: method to remove, must exists in the trigger point"""
        for trigger_tuple in self.__triggers[point_name]:
            if trigger_tuple[1] == callback:
                self.__triggers[point_name].remove(trigger_tuple)
                return
        raise TriggerException("Trying to remove an unexisting trigger")

    def point(self, point_name, *args, **kwargs):
        """This put a trigger point
        All the trigger for that point will be run
        @param point_name: name of the trigger point
        @return: True if the action must be continued, False else"""
        if point_name not in self.__triggers:
            return True

        for priority, trigger in self.__triggers[point_name]:
            try:
                if not trigger(*args, **kwargs):
                    return False
            except SkipOtherTriggers:
                break
        return True