comparison sat_frontends/jp/output_template.py @ 2671:0fa217fafabf

tools (common/template), jp: refactoring to handle multiple sites: - site can now be specified in template header before theme, for instance: (some_site/some_theme)path/to/template.ext - absolute template paths are now implemented, but Renderer must be instanciated with trusted to True for security reason (it's the case for jp) - a new "front_url_filter" callable can be given to Renderer, which will convert template path to URL seen by end-user (default to real path). - the "front_url_filter" can be used in templates with… "front_url" filter - template_data is a new named tuple available in templates, which give site, theme and template relative URL - search order is site/theme, site/default_theme, and default/default_theme where default link to sat_pubsub templates - when loading CSS files, files with _noscript suffixes are now loaded, and used when javascript is not available - "styles_extra.css" is also loaded before "styles.css", useful when a theme want to reuse default style, and just override some rules - new site can be specified in sat.conf [DEFAULT] section, using sites_path_public_dict or sites_path_private_dict (where sites_path_private_dict won't be used in public frontends, like Libervia) - "private" argument of Renderer tells the renderer to load private sites or not - templates are now loaded from "templates" subdirectory, to differenciate them from other data like i18n - jp template output has been updated to handle those changes, and to manage absolute templates
author Goffi <goffi@goffi.org>
date Mon, 10 Sep 2018 08:58:18 +0200
parents 56f94936df1e
children 003b8b4b56a7
comparison
equal deleted inserted replaced
2670:ef93fcbaa749 2671:0fa217fafabf
19 """Standard outputs""" 19 """Standard outputs"""
20 20
21 21
22 from sat_frontends.jp.constants import Const as C 22 from sat_frontends.jp.constants import Const as C
23 from sat.core.i18n import _ 23 from sat.core.i18n import _
24 from sat.core import log
24 from sat.tools.common import template 25 from sat.tools.common import template
26 from functools import partial
27 import logging
25 import webbrowser 28 import webbrowser
26 import tempfile 29 import tempfile
27 import os.path 30 import os.path
28 31
29 __outputs__ = ["Template"] 32 __outputs__ = ["Template"]
35 """outputs data using SàT templates""" 38 """outputs data using SàT templates"""
36 39
37 def __init__(self, jp): 40 def __init__(self, jp):
38 self.host = jp 41 self.host = jp
39 jp.register_output(C.OUTPUT_COMPLEX, TEMPLATE, self.render) 42 jp.register_output(C.OUTPUT_COMPLEX, TEMPLATE, self.render)
43
44 def _front_url_tmp_dir(self, ctx, relative_url, tmp_dir):
45 """Get front URL for temporary directory"""
46 template_data = ctx[u'template_data']
47 return u"file://" + os.path.join(tmp_dir, template_data.theme, relative_url)
48
49 def _do_render(self, template_path, css_inline, **kwargs):
50 try:
51 return self.renderer.render(template_path, css_inline=css_inline, **kwargs)
52 except template.TemplateNotFound:
53 self.host.disp(_(u"Can't find requested template: {template_path}")
54 .format(template_path=template_path), error=True)
55 self.host.quit(C.EXIT_NOT_FOUND)
40 56
41 def render(self, data): 57 def render(self, data):
42 """render output data using requested template 58 """render output data using requested template
43 59
44 template to render the data can be either command's TEMPLATE or 60 template to render the data can be either command's TEMPLATE or
53 cmd = self.host.command 69 cmd = self.host.command
54 try: 70 try:
55 template_path = cmd.TEMPLATE 71 template_path = cmd.TEMPLATE
56 except AttributeError: 72 except AttributeError:
57 if not "template" in cmd.args.output_opts: 73 if not "template" in cmd.args.output_opts:
58 self.host.disp( 74 self.host.disp(_(
59 u"no default template set for this command, " 75 u"no default template set for this command, you need to specify a "
60 u"you need to specify a template using --oo template=[path/to/template.html]", 76 u"template using --oo template=[path/to/template.html]"),
61 error=True, 77 error=True,
62 ) 78 )
63 self.host.quit(C.EXIT_BAD_ARG) 79 self.host.quit(C.EXIT_BAD_ARG)
64 80
65 options = self.host.parse_output_options() 81 options = self.host.parse_output_options()
66 self.host.check_output_options(OPTIONS, options) 82 self.host.check_output_options(OPTIONS, options)
67 self.renderer = template.Renderer(self.host)
68 try: 83 try:
69 template_path = options["template"] 84 template_path = options["template"]
70 except KeyError: 85 except KeyError:
71 # template is not specified, we use default one 86 # template is not specified, we use default one
72 pass 87 pass
73 if template_path is None: 88 if template_path is None:
74 self.host.disp(u"Can't parse template, please check its syntax", error=True) 89 self.host.disp(_(u"Can't parse template, please check its syntax"),
90 error=True)
75 self.host.quit(C.EXIT_BAD_ARG) 91 self.host.quit(C.EXIT_BAD_ARG)
76 92
77 try: 93 try:
78 mapping_cb = cmd.template_data_mapping 94 mapping_cb = cmd.template_data_mapping
79 except AttributeError: 95 except AttributeError:
80 kwargs = data 96 kwargs = data
81 else: 97 else:
82 kwargs = mapping_cb(data) 98 kwargs = mapping_cb(data)
83 99
84 css_inline = u"inline-css" in options 100 css_inline = u"inline-css" in options
85 rendered = self.renderer.render(template_path, css_inline=css_inline, **kwargs)
86 101
87 if "browser" in options: 102 if "browser" in options:
88 template_name = os.path.basename(template_path) 103 template_name = os.path.basename(template_path)
89 tmp_dir = tempfile.mkdtemp() 104 tmp_dir = tempfile.mkdtemp()
90 self.host.disp( 105 front_url_filter = partial(self._front_url_tmp_dir, tmp_dir=tmp_dir)
91 _( 106 self.renderer = template.Renderer(
92 u"Browser opening requested.\nTemporary files are put in the following directory, " 107 self.host, front_url_filter=front_url_filter, trusted=True)
93 u"you'll have to delete it yourself once finished viewing: {}" 108 rendered = self._do_render(template_path, css_inline=css_inline, **kwargs)
94 ).format(tmp_dir) 109 self.host.disp(_(
95 ) 110 u"Browser opening requested.\n"
111 u"Temporary files are put in the following directory, you'll have to "
112 u"delete it yourself once finished viewing: {}").format(tmp_dir))
96 tmp_file = os.path.join(tmp_dir, template_name) 113 tmp_file = os.path.join(tmp_dir, template_name)
97 with open(tmp_file, "w") as f: 114 with open(tmp_file, "w") as f:
98 f.write(rendered.encode("utf-8")) 115 f.write(rendered.encode("utf-8"))
99 theme, theme_root_path = self.renderer.getThemeAndRoot(template_path) 116 theme, theme_root_path = self.renderer.getThemeAndRoot(template_path)
117 if theme is None:
118 # we have an absolute path
119 webbrowser
100 static_dir = os.path.join(theme_root_path, C.TEMPLATE_STATIC_DIR) 120 static_dir = os.path.join(theme_root_path, C.TEMPLATE_STATIC_DIR)
101 if os.path.exists(static_dir): 121 if os.path.exists(static_dir):
122 # we have to copy static files in a subdirectory, to avoid file download
123 # to be blocked by same origin policy
102 import shutil 124 import shutil
103
104 shutil.copytree( 125 shutil.copytree(
105 static_dir, os.path.join(tmp_dir, theme, C.TEMPLATE_STATIC_DIR) 126 static_dir, os.path.join(tmp_dir, theme, C.TEMPLATE_STATIC_DIR)
106 ) 127 )
107 webbrowser.open(tmp_file) 128 webbrowser.open(tmp_file)
108 else: 129 else:
130 # FIXME: Q&D way to disable template logging
131 # logs are overcomplicated, and need to be reworked
132 template_logger = log.getLogger(u"sat.tools.common.template")
133 template_logger.log = lambda *args: None
134
135 logging.disable(logging.WARNING)
136 self.renderer = template.Renderer(self.host, trusted=True)
137 rendered = self._do_render(template_path, css_inline=css_inline, **kwargs)
109 self.host.disp(rendered) 138 self.host.disp(rendered)