tools (common/template): new fallback settings:

`fallback` can be used to change fallback behaviour. By default, fallback is done on
`default` theme, this can be set to an other theme with a string, or to a list of
fallback. The list can also be empty is no fallback is desired (notably usefull for "main"
themes, on which other themes may fallback).

This setting replaces `css_default_fallback`.
This commit is contained in:
Goffi 2021-03-19 14:01:52 +01:00
commit 591b9fe8bc

View file

@ -82,7 +82,7 @@ class TemplateLoader(jinja2.BaseLoader):
"""A template loader which handle site, theme and absolute paths"""
# TODO: list_templates should be implemented
def __init__(self, sites_paths, trusted=False):
def __init__(self, sites_paths, sites_themes, trusted=False):
"""
@param trusted(bool): if True, absolue template paths will be allowed
be careful when using this option and sure that you can trust the template,
@ -93,6 +93,7 @@ class TemplateLoader(jinja2.BaseLoader):
raise exceptions.InternalError("Invalid sites_paths")
super(jinja2.BaseLoader, self).__init__()
self.sites_paths = sites_paths
self.sites_themes = sites_themes
self.trusted = trusted
@staticmethod
@ -163,21 +164,29 @@ class TemplateLoader(jinja2.BaseLoader):
def getSitesAndThemes(
site: str,
theme: str,
default_fallback: bool=True
settings: Optional[dict] = None,
) -> List[Tuple[str, str]]:
"""Get sites and themes to check for template/file
Will add default theme and default site in search list when suitable
Will add default theme and default site in search list when suitable. Settings'
`fallback` can be used to modify behaviour: themes in this list will then be used
instead of default (it can also be empty list or None, in which case no fallback
is used).
@param site: site requested
@param theme: theme requested
@return: site and theme couples to check
"""
if settings is None:
settings = {}
sites_and_themes = [[site, theme]]
if theme != C.TEMPLATE_THEME_DEFAULT and default_fallback:
sites_and_themes.append([site, C.TEMPLATE_THEME_DEFAULT])
if site and default_fallback:
# the site is not the default one, so we add default at the end
sites_and_themes.append(['', C.TEMPLATE_THEME_DEFAULT])
fallback = settings.get("fallback", [C.TEMPLATE_THEME_DEFAULT])
for fb_theme in fallback:
if theme != fb_theme:
sites_and_themes.append([site, fb_theme])
if site:
for fb_theme in fallback:
sites_and_themes.append(["", fb_theme])
return sites_and_themes
def _get_template_f(self, site, theme, path_elts):
@ -194,13 +203,20 @@ class TemplateLoader(jinja2.BaseLoader):
if site is None:
raise exceptions.InternalError(
"_get_template_f must not be used with absolute path")
for site, theme in self.getSitesAndThemes(site, theme):
settings = self.sites_themes[site][theme]['settings']
for site_to_check, theme_to_check in self.getSitesAndThemes(
site, theme, settings):
try:
base_path = self.sites_paths[site]
base_path = self.sites_paths[site_to_check]
except KeyError:
log.warning(_("Unregistered site requested: {site}").format(
site=site))
filepath = os.path.join(base_path, C.TEMPLATE_TPL_DIR, theme, *path_elts)
log.warning(_("Unregistered site requested: {site_to_check}").format(
site_to_check=site_to_check))
filepath = os.path.join(
base_path,
C.TEMPLATE_TPL_DIR,
theme_to_check,
*path_elts
)
f = utils.open_if_exists(filepath, 'r')
if f is not None:
return f, filepath
@ -398,11 +414,21 @@ class Renderer(object):
settings = json.load(f)
except Exception as e:
log.warning(_(
"Can't load theme settings at {path}").format(
path=theme_settings))
"Can't load theme settings at {path}: {e}").format(
path=theme_settings, e=e))
else:
log.debug(
f"found settings for theme {p.name!r} at {theme_settings}")
fallback = settings.get("fallback")
if fallback is None:
settings["fallback"] = []
elif isinstance(fallback, str):
settings["fallback"] = [fallback]
elif not isinstance(fallback, list):
raise ValueError(
'incorrect type for "fallback" in settings '
f'({type(fallback)}) at {theme_settings}: {fallback}'
)
theme_data['settings'] = settings
browser_path = p / BROWSER_DIR
if browser_path.is_dir():
@ -419,7 +445,11 @@ class Renderer(object):
continue
self.env = Environment(
loader=TemplateLoader(sites_paths=self.sites_paths, trusted=trusted),
loader=TemplateLoader(
sites_paths=self.sites_paths,
sites_themes=self.sites_themes,
trusted=trusted
),
autoescape=jinja2.select_autoescape(["html", "xhtml", "xml"]),
trim_blocks=True,
lstrip_blocks=True,
@ -550,7 +580,7 @@ class Renderer(object):
self._locale_str = locale_str
def getThemeAndRoot(self, template):
"""retrieve theme and root dir of a given tempalte
"""retrieve theme and root dir of a given template
@param template(unicode): template to parse
@return (tuple[unicode, unicode]): theme and absolute path to theme's root dir
@ -577,7 +607,7 @@ class Renderer(object):
self,
template_data: TemplateData,
filename: str,
default_fallback: bool=True
settings: Optional[dict]=None
) -> Optional[TemplateData]:
"""Retrieve path of a static file if it exists with current theme or default
@ -589,8 +619,7 @@ class Renderer(object):
checked at "/some/path/<filename>"
@param template_data: data of current template
@param filename: name of the file to retrieve
@param default_fallback: if True, default theme will be checked if the file is
not found in current theme, then default site with default theme will be used.
@param settings: theme settings, can be used to modify behaviour
@return: built template data instance where .path is
the relative path to the file, from theme root dir.
None if not found.
@ -610,7 +639,7 @@ class Renderer(object):
sites_and_themes = TemplateLoader.getSitesAndThemes(template_data.site,
template_data.theme,
default_fallback)
settings)
for site, theme in sites_and_themes:
site_root_dir = self.sites_paths[site]
relative_path = os.path.join(C.TEMPLATE_STATIC_DIR, filename)
@ -627,7 +656,7 @@ class Renderer(object):
css_files: list,
css_files_noscript: list,
name_root: str,
default_fallback: bool
settings: dict
) -> None:
"""Append found css to css_files and css_files_noscript
@ -637,12 +666,12 @@ class Renderer(object):
with "_noscript" suffix
"""
name = name_root + ".css"
css_path = self.getStaticPath(template_data, name, default_fallback)
css_path = self.getStaticPath(template_data, name, settings)
if css_path is not None:
css_files.append(self.getFrontURL(css_path))
noscript_name = name_root + "_noscript.css"
noscript_path = self.getStaticPath(
template_data, noscript_name, default_fallback)
template_data, noscript_name, settings)
if noscript_path is not None:
css_files_noscript.append(self.getFrontURL(noscript_path))
@ -667,9 +696,9 @@ class Renderer(object):
else default/static/blog_articles.css (if it exists too)
and for each found files, if same file with _noscript suffix exists, it is put
in noscript list (for instance (some_theme/static/styles_noscript.css)).
The behaviour may be changed with theme settings: if "css_default_fallback" is
False, only CSS from the theme is returned if it exists, default CSS is never
used.
The behaviour may be changed with theme settings: if "fallback" is set, specified
themes will be checked instead of default. The theme will be checked in given
order, and "fallback" may be None or empty list to not check anything.
@param template_data(TemplateData): data of the current template
@return (tuple[list[unicode], list[unicode]]): a tuple with:
- front URLs of CSS files to use
@ -683,24 +712,22 @@ class Renderer(object):
site = template_data.site
if site is None:
# absolute path
default_fallback = True
settings = {}
else:
default_fallback = (
self.sites_themes[site][template_data.theme]['settings']
).get('css_default_fallback', True)
settings = self.sites_themes[site][template_data.theme]['settings']
css_path = self.getStaticPath(template_data, 'fonts.css', default_fallback)
css_path = self.getStaticPath(template_data, 'fonts.css', settings)
if css_path is not None:
css_files.append(self.getFrontURL(css_path))
for name_root in ('styles', 'styles_extra', 'highlight'):
self._appendCSSPaths(
template_data, css_files, css_files_noscript, name_root, default_fallback)
template_data, css_files, css_files_noscript, name_root, settings)
for idx in range(len(path_elems)):
name_root = "_".join(path_elems[:idx+1])
self._appendCSSPaths(
template_data, css_files, css_files_noscript, name_root, default_fallback)
template_data, css_files, css_files_noscript, name_root, settings)
return css_files, css_files_noscript