# HG changeset patch # User Goffi # Date 1495191271 -7200 # Node ID e09048cb7595247196a5d4a5bde5cd56896971d7 # Parent 9d49e66bdbf257037ab41f795c20ce186c474a09 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. diff -r 9d49e66bdbf2 -r e09048cb7595 src/tools/common/template.py --- 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 elements + @return (unicode): HTML tags + """ + scripts = [] + tpl = u'' + 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)