tools (common/template): theme settings with possibility to disable default fallback for CSS:
- themes can now have a `settings.json` file at their root to modify the renderer behaviour - the `css_default_fallback` boolean (true by default) can be used to disable the fallback mechanism when CSS files are checked. Useful when a theme doesn't follow the CSS naming of default theme. - introduced some type hints
This commit is contained in:
parent
2908be8898
commit
19a9e38983
1 changed files with 72 additions and 21 deletions
|
|
@ -24,6 +24,7 @@ import re
|
|||
import json
|
||||
from pathlib import Path
|
||||
from collections import namedtuple
|
||||
from typing import Optional, List, Tuple
|
||||
from xml.sax.saxutils import quoteattr
|
||||
from babel import support
|
||||
from babel import Locale
|
||||
|
|
@ -159,18 +160,22 @@ class TemplateLoader(jinja2.BaseLoader):
|
|||
return TemplateData(site, theme, template_path)
|
||||
|
||||
@staticmethod
|
||||
def getSitesAndThemes(site, theme):
|
||||
def getSitesAndThemes(
|
||||
site: str,
|
||||
theme: str,
|
||||
default_fallback: bool=True
|
||||
) -> 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
|
||||
@param site(unicode): site requested
|
||||
@param theme(unicode): theme requested
|
||||
@return (list[tuple[unicode, unicode]]): site and theme couples to check
|
||||
@param site: site requested
|
||||
@param theme: theme requested
|
||||
@return: site and theme couples to check
|
||||
"""
|
||||
sites_and_themes = [[site, theme]]
|
||||
if theme != C.TEMPLATE_THEME_DEFAULT:
|
||||
if theme != C.TEMPLATE_THEME_DEFAULT and default_fallback:
|
||||
sites_and_themes.append([site, C.TEMPLATE_THEME_DEFAULT])
|
||||
if site:
|
||||
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])
|
||||
return sites_and_themes
|
||||
|
|
@ -383,7 +388,22 @@ class Renderer(object):
|
|||
if not p.is_dir():
|
||||
continue
|
||||
log.debug(f"theme found for {site or 'default site'}: {p.name}")
|
||||
theme_data = self.sites_themes.setdefault(site, {})[p.name] = {'path': p}
|
||||
theme_data = self.sites_themes.setdefault(site, {})[p.name] = {
|
||||
'path': p,
|
||||
'settings': {}}
|
||||
theme_settings = p / "settings.json"
|
||||
if theme_settings.is_file:
|
||||
try:
|
||||
with theme_settings.open() as f:
|
||||
settings = json.load(f)
|
||||
except Exception as e:
|
||||
log.warning(_(
|
||||
"Can't load theme settings at {path}").format(
|
||||
path=theme_settings))
|
||||
else:
|
||||
log.debug(
|
||||
f"found settings for theme {p.name!r} at {theme_settings}")
|
||||
theme_data['settings'] = settings
|
||||
browser_path = p / BROWSER_DIR
|
||||
if browser_path.is_dir():
|
||||
theme_data['browser_path'] = browser_path
|
||||
|
|
@ -548,22 +568,30 @@ class Renderer(object):
|
|||
except KeyError:
|
||||
raise exceptions.NotFound(f"no theme found for {site_name}")
|
||||
|
||||
def getStaticPath(self, template_data, filename):
|
||||
def getStaticPath(
|
||||
self,
|
||||
template_data: TemplateData,
|
||||
filename: str,
|
||||
default_fallback: bool=True
|
||||
) -> Optional[TemplateData]:
|
||||
"""Retrieve path of a static file if it exists with current theme or default
|
||||
|
||||
File will be looked at <site_root_dir>/<theme_dir>/<static_dir>/filename,
|
||||
then <site_root_dir>/<default_theme_dir>/<static_dir>/filename anf finally
|
||||
<default_site>/<default_theme_dir>/<static_dir> (i.e. sat_templates).
|
||||
In case of absolue URL, base dir of template is used as base. For instance if
|
||||
In case of absolute URL, base dir of template is used as base. For instance if
|
||||
template is an absolute template to "/some/path/template.html", file will be
|
||||
checked at "/some/path/<filename>"
|
||||
@param template_data(TemplateData): data of current template
|
||||
@return (TemplateData, None): built template data instance where .path is
|
||||
@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.
|
||||
@return: built template data instance where .path is
|
||||
the relative path to the file, from theme root dir.
|
||||
None if not found.
|
||||
"""
|
||||
if template_data.site is None:
|
||||
# we have and absolue path
|
||||
# we have an absolue path
|
||||
if (not template_data.theme is None
|
||||
or not template_data.path.startswith('/')):
|
||||
raise exceptions.InternalError(
|
||||
|
|
@ -576,7 +604,8 @@ class Renderer(object):
|
|||
return None
|
||||
|
||||
sites_and_themes = TemplateLoader.getSitesAndThemes(template_data.site,
|
||||
template_data.theme)
|
||||
template_data.theme,
|
||||
default_fallback)
|
||||
for site, theme in sites_and_themes:
|
||||
site_root_dir = self.sites_paths[site]
|
||||
relative_path = os.path.join(C.TEMPLATE_STATIC_DIR, filename)
|
||||
|
|
@ -587,19 +616,28 @@ class Renderer(object):
|
|||
|
||||
return None
|
||||
|
||||
def _appendCSSPaths(self, template_data, css_files, css_files_noscript, name_root):
|
||||
def _appendCSSPaths(
|
||||
self,
|
||||
template_data: TemplateData,
|
||||
css_files: list,
|
||||
css_files_noscript: list,
|
||||
name_root: str,
|
||||
default_fallback: bool
|
||||
|
||||
) -> None:
|
||||
"""Append found css to css_files and css_files_noscript
|
||||
|
||||
@param css_files(list): list to fill of relative path to found css file
|
||||
@param css_files_noscript(list): list to fill of relative path to found css file
|
||||
@param css_files: list to fill of relative path to found css file
|
||||
@param css_files_noscript: list to fill of relative path to found css file
|
||||
with "_noscript" suffix
|
||||
"""
|
||||
name = name_root + ".css"
|
||||
css_path = self.getStaticPath(template_data, name)
|
||||
css_path = self.getStaticPath(template_data, name, default_fallback)
|
||||
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)
|
||||
noscript_path = self.getStaticPath(
|
||||
template_data, noscript_name, default_fallback)
|
||||
if noscript_path is not None:
|
||||
css_files_noscript.append(self.getFrontURL(noscript_path))
|
||||
|
||||
|
|
@ -624,6 +662,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.
|
||||
@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
|
||||
|
|
@ -634,17 +675,27 @@ class Renderer(object):
|
|||
css_files_noscript = []
|
||||
path_elems = template_data.path.split('/')
|
||||
path_elems[-1] = os.path.splitext(path_elems[-1])[0]
|
||||
site = template_data.site
|
||||
if site is None:
|
||||
# absolute path
|
||||
default_fallback = True
|
||||
else:
|
||||
default_fallback = (
|
||||
self.sites_themes[site][template_data.theme]['settings']
|
||||
).get('css_default_fallback', True)
|
||||
|
||||
css_path = self.getStaticPath(template_data, 'fonts.css')
|
||||
css_path = self.getStaticPath(template_data, 'fonts.css', default_fallback)
|
||||
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)
|
||||
self._appendCSSPaths(
|
||||
template_data, css_files, css_files_noscript, name_root, default_fallback)
|
||||
|
||||
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)
|
||||
self._appendCSSPaths(
|
||||
template_data, css_files, css_files_noscript, name_root, default_fallback)
|
||||
|
||||
return css_files, css_files_noscript
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue