##// END OF EJS Templates
jupyter-rendering: added a custom preprocessor to implement Javascript object...
marcink -
r1495:4a485783 default
parent child Browse files
Show More
@@ -1,359 +1,375 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, safe=True):
272 from rhodecode.lib import helpers
272 from rhodecode.lib import helpers
273
274 from traitlets.config import Config
273 import nbformat
275 import nbformat
274 from nbconvert import HTMLExporter
276 from nbconvert import HTMLExporter
275 from traitlets.config import Config
277 from nbconvert.preprocessors import Preprocessor
276
278
277 class CustomHTMLExporter(HTMLExporter):
279 class CustomHTMLExporter(HTMLExporter):
278 def _template_file_default(self):
280 def _template_file_default(self):
279 return 'basic'
281 return 'basic'
280
282
283 class Sandbox(Preprocessor):
284
285 def preprocess(self, nb, resources):
286 sandbox_text = 'SandBoxed(IPython.core.display.Javascript object)'
287 for cell in nb['cells']:
288 if safe and 'outputs' in cell:
289 for cell_output in cell['outputs']:
290 if 'data' in cell_output:
291 if 'application/javascript' in cell_output['data']:
292 cell_output['data']['text/plain'] = sandbox_text
293 cell_output['data'].pop('application/javascript', None)
294 return nb, resources
295
281 def _sanitize_resources(resources):
296 def _sanitize_resources(resources):
282 """
297 """
283 Skip/sanitize some of the CSS generated and included in jupyter
298 Skip/sanitize some of the CSS generated and included in jupyter
284 so it doesn't messes up UI so much
299 so it doesn't messes up UI so much
285 """
300 """
286
301
287 # TODO(marcink): probably we should replace this with whole custom
302 # TODO(marcink): probably we should replace this with whole custom
288 # CSS set that doesn't screw up, but jupyter generated html has some
303 # CSS set that doesn't screw up, but jupyter generated html has some
289 # special markers, so it requires Custom HTML exporter template with
304 # special markers, so it requires Custom HTML exporter template with
290 # _default_template_path_default, to achieve that
305 # _default_template_path_default, to achieve that
291
306
292 # strip the reset CSS
307 # strip the reset CSS
293 resources[0] = resources[0][resources[0].find('/*! Source'):]
308 resources[0] = resources[0][resources[0].find('/*! Source'):]
294 return resources
309 return resources
295
310
296 def as_html(notebook):
311 def as_html(notebook):
297 conf = Config()
312 conf = Config()
313 conf.CustomHTMLExporter.preprocessors = [Sandbox]
298 html_exporter = CustomHTMLExporter(config=conf)
314 html_exporter = CustomHTMLExporter(config=conf)
299
315
300 (body, resources) = html_exporter.from_notebook_node(notebook)
316 (body, resources) = html_exporter.from_notebook_node(notebook)
301 header = '<!-- ## IPYTHON NOTEBOOK RENDERING ## -->'
317 header = '<!-- ## IPYTHON NOTEBOOK RENDERING ## -->'
302 js = MakoTemplate(r'''
318 js = MakoTemplate(r'''
303 <!-- Load mathjax -->
319 <!-- Load mathjax -->
304 <!-- MathJax configuration -->
320 <!-- MathJax configuration -->
305 <script type="text/x-mathjax-config">
321 <script type="text/x-mathjax-config">
306 MathJax.Hub.Config({
322 MathJax.Hub.Config({
307 jax: ["input/TeX","output/HTML-CSS", "output/PreviewHTML"],
323 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"],
324 extensions: ["tex2jax.js","MathMenu.js","MathZoom.js", "fast-preview.js", "AssistiveMML.js", "[Contrib]/a11y/accessibility-menu.js"],
309 TeX: {
325 TeX: {
310 extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
326 extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
311 },
327 },
312 tex2jax: {
328 tex2jax: {
313 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
329 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
314 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
330 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
315 processEscapes: true,
331 processEscapes: true,
316 processEnvironments: true
332 processEnvironments: true
317 },
333 },
318 // Center justify equations in code and markdown cells. Elsewhere
334 // Center justify equations in code and markdown cells. Elsewhere
319 // we use CSS to left justify single line equations in code cells.
335 // we use CSS to left justify single line equations in code cells.
320 displayAlign: 'center',
336 displayAlign: 'center',
321 "HTML-CSS": {
337 "HTML-CSS": {
322 styles: {'.MathJax_Display': {"margin": 0}},
338 styles: {'.MathJax_Display': {"margin": 0}},
323 linebreaks: { automatic: true },
339 linebreaks: { automatic: true },
324 availableFonts: ["STIX", "TeX"]
340 availableFonts: ["STIX", "TeX"]
325 },
341 },
326 showMathMenu: false
342 showMathMenu: false
327 });
343 });
328 </script>
344 </script>
329 <!-- End of mathjax configuration -->
345 <!-- End of mathjax configuration -->
330 <script src="${h.asset('js/src/math_jax/MathJax.js')}"></script>
346 <script src="${h.asset('js/src/math_jax/MathJax.js')}"></script>
331 ''').render(h=helpers)
347 ''').render(h=helpers)
332
348
333 css = '<style>{}</style>'.format(
349 css = '<style>{}</style>'.format(
334 ''.join(_sanitize_resources(resources['inlining']['css'])))
350 ''.join(_sanitize_resources(resources['inlining']['css'])))
335
351
336 body = '\n'.join([header, css, js, body])
352 body = '\n'.join([header, css, js, body])
337 return body, resources
353 return body, resources
338
354
339 notebook = nbformat.reads(source, as_version=4)
355 notebook = nbformat.reads(source, as_version=4)
340 (body, resources) = as_html(notebook)
356 (body, resources) = as_html(notebook)
341 return body
357 return body
342
358
343
359
344 class RstTemplateRenderer(object):
360 class RstTemplateRenderer(object):
345
361
346 def __init__(self):
362 def __init__(self):
347 base = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
363 base = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
348 rst_template_dirs = [os.path.join(base, 'templates', 'rst_templates')]
364 rst_template_dirs = [os.path.join(base, 'templates', 'rst_templates')]
349 self.template_store = TemplateLookup(
365 self.template_store = TemplateLookup(
350 directories=rst_template_dirs,
366 directories=rst_template_dirs,
351 input_encoding='utf-8',
367 input_encoding='utf-8',
352 imports=['from rhodecode.lib import helpers as h'])
368 imports=['from rhodecode.lib import helpers as h'])
353
369
354 def _get_template(self, templatename):
370 def _get_template(self, templatename):
355 return self.template_store.get_template(templatename)
371 return self.template_store.get_template(templatename)
356
372
357 def render(self, template_name, **kwargs):
373 def render(self, template_name, **kwargs):
358 template = self._get_template(template_name)
374 template = self._get_template(template_name)
359 return template.render(**kwargs)
375 return template.render(**kwargs)
General Comments 0
You need to be logged in to leave comments. Login now