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:
parent
2359f3908a
commit
6c9255e31d
1 changed files with 104 additions and 17 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue