Mercurial > libervia-backend
comparison sat/tools/common/template.py @ 3266:8ec5ddb4e759
tools (common/template): list themes and parse their browser data, available through new `getThemesData` method
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 01 May 2020 16:26:39 +0200 |
parents | 559a625a236b |
children | 2eeca6fd08f7 |
comparison
equal
deleted
inserted
replaced
3265:1649bbe8d07e | 3266:8ec5ddb4e759 |
---|---|
1 #!/usr/bin/env python3 | 1 #!/usr/bin/env python3 |
2 | 2 |
3 | 3 # SAT: an XMPP client |
4 # SAT: a jabber client | |
5 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) | 4 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) |
6 | 5 |
7 # This program is free software: you can redistribute it and/or modify | 6 # This program is free software: you can redistribute it and/or modify |
8 # it under the terms of the GNU Affero General Public License as published by | 7 # it under the terms of the GNU Affero General Public License as published by |
9 # the Free Software Foundation, either version 3 of the License, or | 8 # the Free Software Foundation, either version 3 of the License, or |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 18 |
20 """Template generation""" | 19 """Template generation""" |
21 | 20 |
22 import os.path | 21 import os.path |
22 import time | |
23 import re | |
24 import json | |
25 from pathlib import Path | |
23 from collections import namedtuple | 26 from collections import namedtuple |
27 from xml.sax.saxutils import quoteattr | |
28 from babel import support | |
29 from babel import Locale | |
30 from babel.core import UnknownLocaleError | |
31 import pygments | |
32 from pygments import lexers | |
33 from pygments import formatters | |
24 from sat.core.constants import Const as C | 34 from sat.core.constants import Const as C |
25 from sat.core.i18n import _ | 35 from sat.core.i18n import _ |
26 from sat.core import exceptions | 36 from sat.core import exceptions |
27 from sat.tools import config | 37 from sat.tools import config |
28 from sat.tools.common import date_utils | 38 from sat.tools.common import date_utils |
29 from sat.core.log import getLogger | 39 from sat.core.log import getLogger |
30 from xml.sax.saxutils import quoteattr | |
31 import time | |
32 import re | |
33 from babel import support | |
34 from babel import Locale | |
35 from babel.core import UnknownLocaleError | |
36 import pygments | |
37 from pygments import lexers | |
38 from pygments import formatters | |
39 | 40 |
40 log = getLogger(__name__) | 41 log = getLogger(__name__) |
41 | 42 |
42 try: | 43 try: |
43 import sat_templates | 44 import sat_templates |
55 raise exceptions.MissingModule( | 56 raise exceptions.MissingModule( |
56 "Missing module jinja2, please install it from http://jinja.pocoo.org or with " | 57 "Missing module jinja2, please install it from http://jinja.pocoo.org or with " |
57 "pip install jinja2" | 58 "pip install jinja2" |
58 ) | 59 ) |
59 | 60 |
61 from lxml import etree | |
60 from jinja2 import Markup as safe | 62 from jinja2 import Markup as safe |
61 from jinja2 import is_undefined | 63 from jinja2 import is_undefined |
62 from jinja2 import utils | 64 from jinja2 import utils |
63 from jinja2 import TemplateNotFound | 65 from jinja2 import TemplateNotFound |
64 from jinja2 import contextfilter | 66 from jinja2 import contextfilter |
65 from jinja2.loaders import split_template_path | 67 from jinja2.loaders import split_template_path |
66 from lxml import etree | |
67 | 68 |
68 HTML_EXT = ("html", "xhtml") | 69 HTML_EXT = ("html", "xhtml") |
69 RE_ATTR_ESCAPE = re.compile(r"[^a-z_-]") | 70 RE_ATTR_ESCAPE = re.compile(r"[^a-z_-]") |
70 SITE_RESERVED_NAMES = ("sat",) | 71 SITE_RESERVED_NAMES = ("sat",) |
71 TPL_RESERVED_CHARS = r"()/." | 72 TPL_RESERVED_CHARS = r"()/." |
72 RE_TPL_RESERVED_CHARS = re.compile("[" + TPL_RESERVED_CHARS + "]") | 73 RE_TPL_RESERVED_CHARS = re.compile("[" + TPL_RESERVED_CHARS + "]") |
74 BROWSER_DIR = "_browser" | |
75 BROWSER_META_FILE = "browser_meta.json" | |
73 | 76 |
74 TemplateData = namedtuple("TemplateData", ['site', 'theme', 'path']) | 77 TemplateData = namedtuple("TemplateData", ['site', 'theme', 'path']) |
75 | 78 |
76 | 79 |
77 class TemplateLoader(jinja2.BaseLoader): | 80 class TemplateLoader(jinja2.BaseLoader): |
348 self.host = host | 351 self.host = host |
349 self.trusted = trusted | 352 self.trusted = trusted |
350 self.sites_paths = { | 353 self.sites_paths = { |
351 "": os.path.dirname(sat_templates.__file__), | 354 "": os.path.dirname(sat_templates.__file__), |
352 } | 355 } |
356 self.sites_themes = { | |
357 } | |
353 conf = config.parseMainConf() | 358 conf = config.parseMainConf() |
354 public_sites = config.getConfig(conf, None, "sites_path_public_dict", {}) | 359 public_sites = config.getConfig(conf, None, "sites_path_public_dict", {}) |
355 sites_data = [public_sites] | 360 sites_data = [public_sites] |
356 if private: | 361 if private: |
357 private_sites = config.getConfig(conf, None, "sites_path_private_dict", {}) | 362 private_sites = config.getConfig(conf, None, "sites_path_private_dict", {}) |
369 log.warning(_("Can't add \"{name}\" site, it should map to an " | 374 log.warning(_("Can't add \"{name}\" site, it should map to an " |
370 "absolute path").format(name=name)) | 375 "absolute path").format(name=name)) |
371 continue | 376 continue |
372 normalised[name] = path | 377 normalised[name] = path |
373 self.sites_paths.update(normalised) | 378 self.sites_paths.update(normalised) |
379 | |
380 for site, site_path in self.sites_paths.items(): | |
381 tpl_path = Path(site_path) / C.TEMPLATE_TPL_DIR | |
382 for p in tpl_path.iterdir(): | |
383 if not p.is_dir(): | |
384 continue | |
385 log.debug(f"theme found for {site or 'default site'}: {p.name}") | |
386 theme_data = self.sites_themes.setdefault(site, {})[p.name] = {'path': p} | |
387 browser_path = p / BROWSER_DIR | |
388 if browser_path.is_dir(): | |
389 theme_data['browser_path'] = browser_path | |
390 browser_meta_path = browser_path / BROWSER_META_FILE | |
391 if browser_meta_path.is_file(): | |
392 try: | |
393 with browser_meta_path.open() as f: | |
394 theme_data['browser_meta'] = json.load(f) | |
395 except Exception as e: | |
396 log.error( | |
397 f"Can't parse browser metadata at {browser_meta_path}: {e}" | |
398 ) | |
399 continue | |
374 | 400 |
375 self.env = Environment( | 401 self.env = Environment( |
376 loader=TemplateLoader(sites_paths=self.sites_paths, trusted=trusted), | 402 loader=TemplateLoader(sites_paths=self.sites_paths, trusted=trusted), |
377 autoescape=jinja2.select_autoescape(["html", "xhtml", "xml"]), | 403 autoescape=jinja2.select_autoescape(["html", "xhtml", "xml"]), |
378 trim_blocks=True, | 404 trim_blocks=True, |
514 site_root_dir = self.sites_paths[site] | 540 site_root_dir = self.sites_paths[site] |
515 except KeyError: | 541 except KeyError: |
516 raise exceptions.NotFound | 542 raise exceptions.NotFound |
517 return theme, os.path.join(site_root_dir, C.TEMPLATE_TPL_DIR, theme) | 543 return theme, os.path.join(site_root_dir, C.TEMPLATE_TPL_DIR, theme) |
518 | 544 |
545 def getThemesData(self, site_name): | |
546 try: | |
547 return self.sites_themes[site_name] | |
548 except KeyError: | |
549 raise exceptions.NotFound(f"no theme found for {site_name}") | |
550 | |
519 def getStaticPath(self, template_data, filename): | 551 def getStaticPath(self, template_data, filename): |
520 """Retrieve path of a static file if it exists with current theme or default | 552 """Retrieve path of a static file if it exists with current theme or default |
521 | 553 |
522 File will be looked at <site_root_dir>/<theme_dir>/<static_dir>/filename, | 554 File will be looked at <site_root_dir>/<theme_dir>/<static_dir>/filename, |
523 then <site_root_dir>/<default_theme_dir>/<static_dir>/filename anf finally | 555 then <site_root_dir>/<default_theme_dir>/<static_dir>/filename anf finally |