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.
This commit is contained in:
Goffi 2017-05-19 12:54:31 +02:00
commit 6c9255e31d

View file

@ -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 @@ class TemplateLoader(jinja2.FileSystemLoader):
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 @@ class Renderer(object):
)
# 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 @@ class Renderer(object):
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 @@ class Renderer(object):
"""
# 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 @@ class Renderer(object):
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)