##// END OF EJS Templates
jupyter-rendering: limit fonts types to the default ones....
marcink -
r1492:6e516631 default
parent child Browse files
Show More
@@ -1,358 +1,359 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Renderer for markup languages with ability to parse using rst or markdown
23 Renderer for markup languages with ability to parse using rst or markdown
24 """
24 """
25
25
26 import re
26 import re
27 import os
27 import os
28 import logging
28 import logging
29 import itertools
29 import itertools
30
30
31 from mako.lookup import TemplateLookup
31 from mako.lookup import TemplateLookup
32 from mako.template import Template as MakoTemplate
32 from mako.template import Template as MakoTemplate
33
33
34 from docutils.core import publish_parts
34 from docutils.core import publish_parts
35 from docutils.parsers.rst import directives
35 from docutils.parsers.rst import directives
36 import markdown
36 import markdown
37
37
38 from rhodecode.lib.markdown_ext import (
38 from rhodecode.lib.markdown_ext import (
39 UrlizeExtension, GithubFlavoredMarkdownExtension)
39 UrlizeExtension, GithubFlavoredMarkdownExtension)
40 from rhodecode.lib.utils2 import safe_unicode, md5_safe, MENTIONS_REGEX
40 from rhodecode.lib.utils2 import safe_unicode, md5_safe, MENTIONS_REGEX
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 # default renderer used to generate automated comments
44 # default renderer used to generate automated comments
45 DEFAULT_COMMENTS_RENDERER = 'rst'
45 DEFAULT_COMMENTS_RENDERER = 'rst'
46
46
47
47
48 class MarkupRenderer(object):
48 class MarkupRenderer(object):
49 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
49 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
50
50
51 MARKDOWN_PAT = re.compile(r'\.(md|mkdn?|mdown|markdown)$', re.IGNORECASE)
51 MARKDOWN_PAT = re.compile(r'\.(md|mkdn?|mdown|markdown)$', re.IGNORECASE)
52 RST_PAT = re.compile(r'\.re?st$', re.IGNORECASE)
52 RST_PAT = re.compile(r'\.re?st$', re.IGNORECASE)
53 JUPYTER_PAT = re.compile(r'\.(ipynb)$', re.IGNORECASE)
53 JUPYTER_PAT = re.compile(r'\.(ipynb)$', re.IGNORECASE)
54 PLAIN_PAT = re.compile(r'^readme$', re.IGNORECASE)
54 PLAIN_PAT = re.compile(r'^readme$', re.IGNORECASE)
55
55
56 extensions = ['codehilite', 'extra', 'def_list', 'sane_lists']
56 extensions = ['codehilite', 'extra', 'def_list', 'sane_lists']
57 markdown_renderer = markdown.Markdown(
57 markdown_renderer = markdown.Markdown(
58 extensions, safe_mode=True, enable_attributes=False)
58 extensions, safe_mode=True, enable_attributes=False)
59
59
60 markdown_renderer_flavored = markdown.Markdown(
60 markdown_renderer_flavored = markdown.Markdown(
61 extensions + [GithubFlavoredMarkdownExtension()], safe_mode=True,
61 extensions + [GithubFlavoredMarkdownExtension()], safe_mode=True,
62 enable_attributes=False)
62 enable_attributes=False)
63
63
64 # extension together with weights. Lower is first means we control how
64 # extension together with weights. Lower is first means we control how
65 # extensions are attached to readme names with those.
65 # extensions are attached to readme names with those.
66 PLAIN_EXTS = [
66 PLAIN_EXTS = [
67 # prefer no extension
67 # prefer no extension
68 ('', 0), # special case that renders READMES names without extension
68 ('', 0), # special case that renders READMES names without extension
69 ('.text', 2), ('.TEXT', 2),
69 ('.text', 2), ('.TEXT', 2),
70 ('.txt', 3), ('.TXT', 3)
70 ('.txt', 3), ('.TXT', 3)
71 ]
71 ]
72
72
73 RST_EXTS = [
73 RST_EXTS = [
74 ('.rst', 1), ('.rest', 1),
74 ('.rst', 1), ('.rest', 1),
75 ('.RST', 2), ('.REST', 2)
75 ('.RST', 2), ('.REST', 2)
76 ]
76 ]
77
77
78 MARKDOWN_EXTS = [
78 MARKDOWN_EXTS = [
79 ('.md', 1), ('.MD', 1),
79 ('.md', 1), ('.MD', 1),
80 ('.mkdn', 2), ('.MKDN', 2),
80 ('.mkdn', 2), ('.MKDN', 2),
81 ('.mdown', 3), ('.MDOWN', 3),
81 ('.mdown', 3), ('.MDOWN', 3),
82 ('.markdown', 4), ('.MARKDOWN', 4)
82 ('.markdown', 4), ('.MARKDOWN', 4)
83 ]
83 ]
84
84
85 def _detect_renderer(self, source, filename=None):
85 def _detect_renderer(self, source, filename=None):
86 """
86 """
87 runs detection of what renderer should be used for generating html
87 runs detection of what renderer should be used for generating html
88 from a markup language
88 from a markup language
89
89
90 filename can be also explicitly a renderer name
90 filename can be also explicitly a renderer name
91
91
92 :param source:
92 :param source:
93 :param filename:
93 :param filename:
94 """
94 """
95
95
96 if MarkupRenderer.MARKDOWN_PAT.findall(filename):
96 if MarkupRenderer.MARKDOWN_PAT.findall(filename):
97 detected_renderer = 'markdown'
97 detected_renderer = 'markdown'
98 elif MarkupRenderer.RST_PAT.findall(filename):
98 elif MarkupRenderer.RST_PAT.findall(filename):
99 detected_renderer = 'rst'
99 detected_renderer = 'rst'
100 elif MarkupRenderer.JUPYTER_PAT.findall(filename):
100 elif MarkupRenderer.JUPYTER_PAT.findall(filename):
101 detected_renderer = 'jupyter'
101 detected_renderer = 'jupyter'
102 elif MarkupRenderer.PLAIN_PAT.findall(filename):
102 elif MarkupRenderer.PLAIN_PAT.findall(filename):
103 detected_renderer = 'plain'
103 detected_renderer = 'plain'
104 else:
104 else:
105 detected_renderer = 'plain'
105 detected_renderer = 'plain'
106
106
107 return getattr(MarkupRenderer, detected_renderer)
107 return getattr(MarkupRenderer, detected_renderer)
108
108
109 @classmethod
109 @classmethod
110 def renderer_from_filename(cls, filename, exclude):
110 def renderer_from_filename(cls, filename, exclude):
111 """
111 """
112 Detect renderer markdown/rst from filename and optionally use exclude
112 Detect renderer markdown/rst from filename and optionally use exclude
113 list to remove some options. This is mostly used in helpers.
113 list to remove some options. This is mostly used in helpers.
114 Returns None when no renderer can be detected.
114 Returns None when no renderer can be detected.
115 """
115 """
116 def _filter(elements):
116 def _filter(elements):
117 if isinstance(exclude, (list, tuple)):
117 if isinstance(exclude, (list, tuple)):
118 return [x for x in elements if x not in exclude]
118 return [x for x in elements if x not in exclude]
119 return elements
119 return elements
120
120
121 if filename.endswith(
121 if filename.endswith(
122 tuple(_filter([x[0] for x in cls.MARKDOWN_EXTS if x[0]]))):
122 tuple(_filter([x[0] for x in cls.MARKDOWN_EXTS if x[0]]))):
123 return 'markdown'
123 return 'markdown'
124 if filename.endswith(tuple(_filter([x[0] for x in cls.RST_EXTS if x[0]]))):
124 if filename.endswith(tuple(_filter([x[0] for x in cls.RST_EXTS if x[0]]))):
125 return 'rst'
125 return 'rst'
126
126
127 return None
127 return None
128
128
129 def render(self, source, filename=None):
129 def render(self, source, filename=None):
130 """
130 """
131 Renders a given filename using detected renderer
131 Renders a given filename using detected renderer
132 it detects renderers based on file extension or mimetype.
132 it detects renderers based on file extension or mimetype.
133 At last it will just do a simple html replacing new lines with <br/>
133 At last it will just do a simple html replacing new lines with <br/>
134
134
135 :param file_name:
135 :param file_name:
136 :param source:
136 :param source:
137 """
137 """
138
138
139 renderer = self._detect_renderer(source, filename)
139 renderer = self._detect_renderer(source, filename)
140 readme_data = renderer(source)
140 readme_data = renderer(source)
141 return readme_data
141 return readme_data
142
142
143 @classmethod
143 @classmethod
144 def _flavored_markdown(cls, text):
144 def _flavored_markdown(cls, text):
145 """
145 """
146 Github style flavored markdown
146 Github style flavored markdown
147
147
148 :param text:
148 :param text:
149 """
149 """
150
150
151 # Extract pre blocks.
151 # Extract pre blocks.
152 extractions = {}
152 extractions = {}
153
153
154 def pre_extraction_callback(matchobj):
154 def pre_extraction_callback(matchobj):
155 digest = md5_safe(matchobj.group(0))
155 digest = md5_safe(matchobj.group(0))
156 extractions[digest] = matchobj.group(0)
156 extractions[digest] = matchobj.group(0)
157 return "{gfm-extraction-%s}" % digest
157 return "{gfm-extraction-%s}" % digest
158 pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL)
158 pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL)
159 text = re.sub(pattern, pre_extraction_callback, text)
159 text = re.sub(pattern, pre_extraction_callback, text)
160
160
161 # Prevent foo_bar_baz from ending up with an italic word in the middle.
161 # Prevent foo_bar_baz from ending up with an italic word in the middle.
162 def italic_callback(matchobj):
162 def italic_callback(matchobj):
163 s = matchobj.group(0)
163 s = matchobj.group(0)
164 if list(s).count('_') >= 2:
164 if list(s).count('_') >= 2:
165 return s.replace('_', r'\_')
165 return s.replace('_', r'\_')
166 return s
166 return s
167 text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text)
167 text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text)
168
168
169 # Insert pre block extractions.
169 # Insert pre block extractions.
170 def pre_insert_callback(matchobj):
170 def pre_insert_callback(matchobj):
171 return '\n\n' + extractions[matchobj.group(1)]
171 return '\n\n' + extractions[matchobj.group(1)]
172 text = re.sub(r'\{gfm-extraction-([0-9a-f]{32})\}',
172 text = re.sub(r'\{gfm-extraction-([0-9a-f]{32})\}',
173 pre_insert_callback, text)
173 pre_insert_callback, text)
174
174
175 return text
175 return text
176
176
177 @classmethod
177 @classmethod
178 def urlify_text(cls, text):
178 def urlify_text(cls, text):
179 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
179 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
180 r'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
180 r'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
181
181
182 def url_func(match_obj):
182 def url_func(match_obj):
183 url_full = match_obj.groups()[0]
183 url_full = match_obj.groups()[0]
184 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
184 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
185
185
186 return url_pat.sub(url_func, text)
186 return url_pat.sub(url_func, text)
187
187
188 @classmethod
188 @classmethod
189 def plain(cls, source, universal_newline=True):
189 def plain(cls, source, universal_newline=True):
190 source = safe_unicode(source)
190 source = safe_unicode(source)
191 if universal_newline:
191 if universal_newline:
192 newline = '\n'
192 newline = '\n'
193 source = newline.join(source.splitlines())
193 source = newline.join(source.splitlines())
194
194
195 source = cls.urlify_text(source)
195 source = cls.urlify_text(source)
196 return '<br />' + source.replace("\n", '<br />')
196 return '<br />' + source.replace("\n", '<br />')
197
197
198 @classmethod
198 @classmethod
199 def markdown(cls, source, safe=True, flavored=True, mentions=False):
199 def markdown(cls, source, safe=True, flavored=True, mentions=False):
200 # It does not allow to insert inline HTML. In presence of HTML tags, it
200 # It does not allow to insert inline HTML. In presence of HTML tags, it
201 # will replace them instead with [HTML_REMOVED]. This is controlled by
201 # will replace them instead with [HTML_REMOVED]. This is controlled by
202 # the safe_mode=True parameter of the markdown method.
202 # the safe_mode=True parameter of the markdown method.
203
203
204 if flavored:
204 if flavored:
205 markdown_renderer = cls.markdown_renderer_flavored
205 markdown_renderer = cls.markdown_renderer_flavored
206 else:
206 else:
207 markdown_renderer = cls.markdown_renderer
207 markdown_renderer = cls.markdown_renderer
208
208
209 if mentions:
209 if mentions:
210 mention_pat = re.compile(MENTIONS_REGEX)
210 mention_pat = re.compile(MENTIONS_REGEX)
211
211
212 def wrapp(match_obj):
212 def wrapp(match_obj):
213 uname = match_obj.groups()[0]
213 uname = match_obj.groups()[0]
214 return ' **@%(uname)s** ' % {'uname': uname}
214 return ' **@%(uname)s** ' % {'uname': uname}
215 mention_hl = mention_pat.sub(wrapp, source).strip()
215 mention_hl = mention_pat.sub(wrapp, source).strip()
216 # we extracted mentions render with this using Mentions false
216 # we extracted mentions render with this using Mentions false
217 return cls.markdown(mention_hl, safe=safe, flavored=flavored,
217 return cls.markdown(mention_hl, safe=safe, flavored=flavored,
218 mentions=False)
218 mentions=False)
219
219
220 source = safe_unicode(source)
220 source = safe_unicode(source)
221 try:
221 try:
222 if flavored:
222 if flavored:
223 source = cls._flavored_markdown(source)
223 source = cls._flavored_markdown(source)
224 return markdown_renderer.convert(source)
224 return markdown_renderer.convert(source)
225 except Exception:
225 except Exception:
226 log.exception('Error when rendering Markdown')
226 log.exception('Error when rendering Markdown')
227 if safe:
227 if safe:
228 log.debug('Fallback to render in plain mode')
228 log.debug('Fallback to render in plain mode')
229 return cls.plain(source)
229 return cls.plain(source)
230 else:
230 else:
231 raise
231 raise
232
232
233 @classmethod
233 @classmethod
234 def rst(cls, source, safe=True, mentions=False):
234 def rst(cls, source, safe=True, mentions=False):
235 if mentions:
235 if mentions:
236 mention_pat = re.compile(MENTIONS_REGEX)
236 mention_pat = re.compile(MENTIONS_REGEX)
237
237
238 def wrapp(match_obj):
238 def wrapp(match_obj):
239 uname = match_obj.groups()[0]
239 uname = match_obj.groups()[0]
240 return ' **@%(uname)s** ' % {'uname': uname}
240 return ' **@%(uname)s** ' % {'uname': uname}
241 mention_hl = mention_pat.sub(wrapp, source).strip()
241 mention_hl = mention_pat.sub(wrapp, source).strip()
242 # we extracted mentions render with this using Mentions false
242 # we extracted mentions render with this using Mentions false
243 return cls.rst(mention_hl, safe=safe, mentions=False)
243 return cls.rst(mention_hl, safe=safe, mentions=False)
244
244
245 source = safe_unicode(source)
245 source = safe_unicode(source)
246 try:
246 try:
247 docutils_settings = dict(
247 docutils_settings = dict(
248 [(alias, None) for alias in
248 [(alias, None) for alias in
249 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
249 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
250
250
251 docutils_settings.update({'input_encoding': 'unicode',
251 docutils_settings.update({'input_encoding': 'unicode',
252 'report_level': 4})
252 'report_level': 4})
253
253
254 for k, v in docutils_settings.iteritems():
254 for k, v in docutils_settings.iteritems():
255 directives.register_directive(k, v)
255 directives.register_directive(k, v)
256
256
257 parts = publish_parts(source=source,
257 parts = publish_parts(source=source,
258 writer_name="html4css1",
258 writer_name="html4css1",
259 settings_overrides=docutils_settings)
259 settings_overrides=docutils_settings)
260
260
261 return parts['html_title'] + parts["fragment"]
261 return parts['html_title'] + parts["fragment"]
262 except Exception:
262 except Exception:
263 log.exception('Error when rendering RST')
263 log.exception('Error when rendering RST')
264 if safe:
264 if safe:
265 log.debug('Fallbacking to render in plain mode')
265 log.debug('Fallbacking to render in plain mode')
266 return cls.plain(source)
266 return cls.plain(source)
267 else:
267 else:
268 raise
268 raise
269
269
270 @classmethod
270 @classmethod
271 def jupyter(cls, source):
271 def jupyter(cls, source):
272 from rhodecode.lib import helpers
272 from rhodecode.lib import helpers
273 import nbformat
273 import nbformat
274 from nbconvert import HTMLExporter
274 from nbconvert import HTMLExporter
275 from traitlets.config import Config
275 from traitlets.config import Config
276
276
277 class CustomHTMLExporter(HTMLExporter):
277 class CustomHTMLExporter(HTMLExporter):
278 def _template_file_default(self):
278 def _template_file_default(self):
279 return 'basic'
279 return 'basic'
280
280
281 def _sanitize_resources(resources):
281 def _sanitize_resources(resources):
282 """
282 """
283 Skip/sanitize some of the CSS generated and included in jupyter
283 Skip/sanitize some of the CSS generated and included in jupyter
284 so it doesn't messes up UI so much
284 so it doesn't messes up UI so much
285 """
285 """
286
286
287 # TODO(marcink): probably we should replace this with whole custom
287 # TODO(marcink): probably we should replace this with whole custom
288 # CSS set that doesn't screw up, but jupyter generated html has some
288 # CSS set that doesn't screw up, but jupyter generated html has some
289 # special markers, so it requires Custom HTML exporter template with
289 # special markers, so it requires Custom HTML exporter template with
290 # _default_template_path_default, to achieve that
290 # _default_template_path_default, to achieve that
291
291
292 # strip the reset CSS
292 # strip the reset CSS
293 resources[0] = resources[0][resources[0].find('/*! Source'):]
293 resources[0] = resources[0][resources[0].find('/*! Source'):]
294 return resources
294 return resources
295
295
296 def as_html(notebook):
296 def as_html(notebook):
297 conf = Config()
297 conf = Config()
298 html_exporter = CustomHTMLExporter(config=conf)
298 html_exporter = CustomHTMLExporter(config=conf)
299
299
300 (body, resources) = html_exporter.from_notebook_node(notebook)
300 (body, resources) = html_exporter.from_notebook_node(notebook)
301 header = '<!-- ## IPYTHON NOTEBOOK RENDERING ## -->'
301 header = '<!-- ## IPYTHON NOTEBOOK RENDERING ## -->'
302 js = MakoTemplate(r'''
302 js = MakoTemplate(r'''
303 <!-- Load mathjax -->
303 <!-- Load mathjax -->
304 <!-- MathJax configuration -->
304 <!-- MathJax configuration -->
305 <script type="text/x-mathjax-config">
305 <script type="text/x-mathjax-config">
306 MathJax.Hub.Config({
306 MathJax.Hub.Config({
307 jax: ["input/TeX","output/HTML-CSS", "output/PreviewHTML"],
307 jax: ["input/TeX","output/HTML-CSS", "output/PreviewHTML"],
308 extensions: ["tex2jax.js","MathMenu.js","MathZoom.js", "fast-preview.js", "AssistiveMML.js", "[Contrib]/a11y/accessibility-menu.js"],
308 extensions: ["tex2jax.js","MathMenu.js","MathZoom.js", "fast-preview.js", "AssistiveMML.js", "[Contrib]/a11y/accessibility-menu.js"],
309 TeX: {
309 TeX: {
310 extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
310 extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
311 },
311 },
312 tex2jax: {
312 tex2jax: {
313 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
313 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
314 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
314 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
315 processEscapes: true,
315 processEscapes: true,
316 processEnvironments: true
316 processEnvironments: true
317 },
317 },
318 // Center justify equations in code and markdown cells. Elsewhere
318 // Center justify equations in code and markdown cells. Elsewhere
319 // we use CSS to left justify single line equations in code cells.
319 // we use CSS to left justify single line equations in code cells.
320 displayAlign: 'center',
320 displayAlign: 'center',
321 "HTML-CSS": {
321 "HTML-CSS": {
322 styles: {'.MathJax_Display': {"margin": 0}},
322 styles: {'.MathJax_Display': {"margin": 0}},
323 linebreaks: { automatic: true }
323 linebreaks: { automatic: true },
324 availableFonts: ["STIX", "TeX"]
324 },
325 },
325 showMathMenu: false
326 showMathMenu: false
326 });
327 });
327 </script>
328 </script>
328 <!-- End of mathjax configuration -->
329 <!-- End of mathjax configuration -->
329 <script src="${h.asset('js/src/math_jax/MathJax.js')}"></script>
330 <script src="${h.asset('js/src/math_jax/MathJax.js')}"></script>
330 ''').render(h=helpers)
331 ''').render(h=helpers)
331
332
332 css = '<style>{}</style>'.format(
333 css = '<style>{}</style>'.format(
333 ''.join(_sanitize_resources(resources['inlining']['css'])))
334 ''.join(_sanitize_resources(resources['inlining']['css'])))
334
335
335 body = '\n'.join([header, css, js, body])
336 body = '\n'.join([header, css, js, body])
336 return body, resources
337 return body, resources
337
338
338 notebook = nbformat.reads(source, as_version=4)
339 notebook = nbformat.reads(source, as_version=4)
339 (body, resources) = as_html(notebook)
340 (body, resources) = as_html(notebook)
340 return body
341 return body
341
342
342
343
343 class RstTemplateRenderer(object):
344 class RstTemplateRenderer(object):
344
345
345 def __init__(self):
346 def __init__(self):
346 base = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
347 base = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
347 rst_template_dirs = [os.path.join(base, 'templates', 'rst_templates')]
348 rst_template_dirs = [os.path.join(base, 'templates', 'rst_templates')]
348 self.template_store = TemplateLookup(
349 self.template_store = TemplateLookup(
349 directories=rst_template_dirs,
350 directories=rst_template_dirs,
350 input_encoding='utf-8',
351 input_encoding='utf-8',
351 imports=['from rhodecode.lib import helpers as h'])
352 imports=['from rhodecode.lib import helpers as h'])
352
353
353 def _get_template(self, templatename):
354 def _get_template(self, templatename):
354 return self.template_store.get_template(templatename)
355 return self.template_store.get_template(templatename)
355
356
356 def render(self, template_name, **kwargs):
357 def render(self, template_name, **kwargs):
357 template = self._get_template(template_name)
358 template = self._get_template(template_name)
358 return template.render(**kwargs)
359 return template.render(**kwargs)
General Comments 0
You need to be logged in to leave comments. Login now