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:
parent
bd779e8ddd
commit
591b9fe8bc
1 changed files with 61 additions and 34 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue