changeset 2245:e09048cb7595

core (tools/common/template): helping methods/filters for templates: - Indexer class (used with next_gidx/cur_gidx) allows to get per page indexes, usefull to have unique id to reference - blog_date is a Q&D filter to get relative dates, should be replaced/improved in the future - ScriptsHandler (used with scripts_handler) is intended to be a way to add scripts from template, and group them at a specific location of the page. It is not used yet and may change heavily or disappear in the future.
author Goffi <goffi@goffi.org>
date Fri, 19 May 2017 12:54:31 +0200
parents 9d49e66bdbf2
children e8641b7718dc
files src/tools/common/template.py
diffstat 1 files changed, 104 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/tools/common/template.py	Fri May 19 12:45:26 2017 +0200
+++ b/src/tools/common/template.py	Fri May 19 12:54:31 2017 +0200
@@ -20,10 +20,14 @@
 """ template generation """
 
 from sat.core.constants import Const as C
+from sat.core.i18n import _
 from sat.core import exceptions
 from sat.core.log import getLogger
 log = getLogger(__name__)
 import os.path
+from collections import OrderedDict
+from xml.sax.saxutils import quoteattr
+import time
 try:
     import sat_templates
 except ImportError:
@@ -113,6 +117,51 @@
             raise e
 
 
+class Indexer(object):
+    """Index global to a page"""
+
+    def __init__(self):
+        self._idx = 0
+
+    def next(self):
+        self._idx+=1
+        return self._idx
+
+    def current(self):
+        return self._idx
+
+
+class ScriptsHandler(object):
+    # TODO: this class is not finished/used yet, and add_script is referenced in default/script/base.html
+    #       but doesn't exist yet here
+
+    def __init__(self, renderer, template_path, template_root_dir):
+        self.renderer = renderer
+        self.template_root_dir = template_root_dir
+        self.scripts = OrderedDict
+        dummy, self.theme, self.is_default_theme = renderer.getThemeData(template_path)
+
+    def import_script(self, library_name):
+        if library_name.endswith('.js'):
+            library_name = library_name[:-3]
+        if library_name not in self.scripts:
+            self.scripts[library_name] = {}
+
+    def generate(self):
+        """Generate the <scripts> elements
+        @return (unicode): <scripts> HTML tags
+        """
+        scripts = []
+        tpl = u'<script src="{src}"></script>'
+        for library,data in self.scripts:
+            path = self.renderer.getStaticPath(library, self.template_root_dir, self.theme, self.is_default_theme, '.js')
+            if path is None:
+                log.warning(_(u"Can't find {}.js javascript library").format(library))
+                continue
+            scripts.append(tpl.format(src=quoteattr(path)))
+        return u'\n'.join(scripts)
+
+
 class Renderer(object):
 
     def __init__(self, host):
@@ -126,6 +175,10 @@
             )
         # we want to have access to SàT constants in templates
         self.env.globals[u'C'] = C
+        # custom filters
+        self.env.filters['next_gidx'] = self._next_gidx
+        self.env.filters['cur_gidx'] = self._cur_gidx
+        self.env.filters['blog_date'] = self._blog_date
 
     def getThemeAndRoot(self, template):
         """retrieve theme and root dir of a given tempalte
@@ -136,24 +189,39 @@
         theme, dummy = self.env.loader.parse_template(template)
         return theme, os.path.join(self.base_dir, theme)
 
-    def _appendCSSIfExists(self, css_files, template_root_dir, theme, name, is_default):
-        """append CSS file to list if it exists, else try with default theme
+    def getStaticPath(self, name, template_root_dir, theme, is_default, ext='.css'):
+        """retrieve path of a static file if it exists with current theme or default
 
-        CSS file will be looked at [theme]/static/[name].css, and then default
+        File will be looked at [theme]/static/[name][ext], and then default
         if not found.
-        @param css_files(list): list of CSS file to be completed
+        @param name(unicode): name of the file to look for
         @param template_root_dir(unicode): absolute path to template root used
         @param theme(unicode): name of the template theme used
-        @param name(unicode): name of the CSS file to look for
         @param is_default(bool): True if theme is the default theme
+        @return (unicode, None): relative path if found, else None
         """
-        css_path = os.path.join(theme, C.TEMPLATE_STATIC_DIR, name + '.css')
-        if os.path.exists(os.path.join(template_root_dir, css_path)):
-            css_files.append(css_path)
+        file_ = None
+        path = os.path.join(theme, C.TEMPLATE_STATIC_DIR, name + ext)
+        if os.path.exists(os.path.join(template_root_dir, path)):
+            file_ = path
         elif not is_default:
-            css_path = os.path.join(C.TEMPLATE_THEME_DEFAULT, C.TEMPLATE_STATIC_DIR, name + '.css')
-            if os.path.exists(os.path.join(template_root_dir, css_path)):
-                css_files.append(css_path)
+            path = os.path.join(C.TEMPLATE_THEME_DEFAULT, C.TEMPLATE_STATIC_DIR, name + ext)
+            if os.path.exists(os.path.join(template_root_dir, path)):
+                file_.append(path)
+        return file_
+
+    def getThemeData(self, template_path):
+        """return template data got from template_path
+
+        @return tuple(unicde, unicode, bool):
+            path_elems: elements of the path
+            theme: theme of the page
+            is_default: True if the theme is the default theme
+        """
+        path_elems = template_path.split(u'/')
+        theme = path_elems.pop(0)
+        is_default = theme == C.TEMPLATE_THEME_DEFAULT
+        return (path_elems, theme, is_default)
 
     def getCSSFiles(self, template_path, template_root_dir):
         """retrieve CSS files to use according to theme and template path
@@ -170,16 +238,33 @@
         """
         # TODO: some caching would be nice
         css_files = []
-        path_elems = template_path.split(u'/')
-        theme = path_elems.pop(0)
-        is_default = theme == C.TEMPLATE_THEME_DEFAULT
-        self._appendCSSIfExists(css_files, template_root_dir, theme, u'styles', is_default)
+        path_elems, theme, is_default = self.getThemeData(template_path)
+        for css in (u'fonts', u'styles'):
+            css_path = self.getStaticPath(css, template_root_dir, theme, is_default)
+            if css_path is not None:
+                css_files.append(css_path)
 
         for idx, path in enumerate(path_elems):
-            self._appendCSSIfExists(css_files, template_root_dir, theme, u'_'.join(path_elems[:idx+1]), is_default)
+            css_path = self.getStaticPath(u'_'.join(path_elems[:idx+1]), template_root_dir, theme, is_default)
+            if css_path is not None:
+                css_files.append(css_path)
 
         return css_files
 
+    @jinja2.contextfilter
+    def _next_gidx(self, ctx, value):
+        """Use next current global index as suffix"""
+        return u"{}_{}".format(value, ctx['gidx'].next())
+
+    @jinja2.contextfilter
+    def _cur_gidx(self, ctx, value):
+        """Use current current global index as suffix"""
+        return u"{}_{}".format(value, ctx['gidx'].current())
+
+    def _blog_date(self, timestamp):
+        # FIXME: Q&D, need to be done properly
+        return unicode(int(time.time() - int(timestamp))/(3600*24)) + u" days ago"
+
     def render(self, template, theme=None, root_path=u'', css_files=None, css_inline=False, **kwargs):
         """render a template
 
@@ -222,6 +307,8 @@
                     css_contents.append(f.read())
             if css_contents:
                 kwargs['css_content'] = '\n'.join(css_contents)
+
+        scripts_handler = ScriptsHandler(self, template_path, template_root_dir)
         # XXX: theme used in template arguments is the requested theme, which may differ from actual theme
         #      if the template doesn't exist in the requested theme.
-        return template_source.render(theme=theme, root_path=root_path, css_files=css_files, **kwargs)
+        return template_source.render(theme=theme, root_path=root_path, css_files=css_files, gidx=Indexer(), scripts_handler=scripts_handler, **kwargs)