##// END OF EJS Templates
markup-rendering: added relative image support....
marcink -
r1527:4089d52f default
parent child Browse files
Show More
@@ -38,7 +38,7 b' from rhodecode.lib.utils2 import safe_st'
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, XHRRequired)
39 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, XHRRequired)
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.markup_renderer import MarkupRenderer
41 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib.vcs.exceptions import (
44 from rhodecode.lib.vcs.exceptions import (
@@ -70,7 +70,12 b' class SummaryController(BaseRepoControll'
70 log.debug("Searching for a README file.")
70 log.debug("Searching for a README file.")
71 readme_node = ReadmeFinder(default_renderer).search(commit)
71 readme_node = ReadmeFinder(default_renderer).search(commit)
72 if readme_node:
72 if readme_node:
73 readme_data = self._render_readme_or_none(commit, readme_node)
73 relative_url = h.url('files_raw_home',
74 repo_name=repo_name,
75 revision=commit.raw_id,
76 f_path=readme_node.path)
77 readme_data = self._render_readme_or_none(
78 commit, readme_node, relative_url)
74 readme_filename = readme_node.path
79 readme_filename = readme_node.path
75 return readme_data, readme_filename
80 return readme_data, readme_filename
76
81
@@ -95,13 +100,16 b' class SummaryController(BaseRepoControll'
95 log.exception(
100 log.exception(
96 "Problem getting commit when trying to render the README.")
101 "Problem getting commit when trying to render the README.")
97
102
98 def _render_readme_or_none(self, commit, readme_node):
103 def _render_readme_or_none(self, commit, readme_node, relative_url):
99 log.debug(
104 log.debug(
100 'Found README file `%s` rendering...', readme_node.path)
105 'Found README file `%s` rendering...', readme_node.path)
101 renderer = MarkupRenderer()
106 renderer = MarkupRenderer()
102 try:
107 try:
103 return renderer.render(
108 html_source = renderer.render(
104 readme_node.content, filename=readme_node.path)
109 readme_node.content, filename=readme_node.path)
110 if relative_url:
111 return relative_links(html_source, relative_url)
112 return html_source
105 except Exception:
113 except Exception:
106 log.exception(
114 log.exception(
107 "Exception while trying to render the README")
115 "Exception while trying to render the README")
@@ -75,7 +75,7 b' from rhodecode.lib.utils import repo_nam'
75 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
75 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
76 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
76 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
77 AttributeDict, safe_int, md5, md5_safe
77 AttributeDict, safe_int, md5, md5_safe
78 from rhodecode.lib.markup_renderer import MarkupRenderer
78 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
79 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
79 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
80 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
80 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
81 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
81 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
@@ -1828,19 +1828,29 b' def renderer_from_filename(filename, exc'
1828 return None
1828 return None
1829
1829
1830
1830
1831 def render(source, renderer='rst', mentions=False):
1831 def render(source, renderer='rst', mentions=False, relative_url=None):
1832
1833 def maybe_convert_relative_links(html_source):
1834 if relative_url:
1835 return relative_links(html_source, relative_url)
1836 return html_source
1837
1832 if renderer == 'rst':
1838 if renderer == 'rst':
1833 return literal(
1839 return literal(
1834 '<div class="rst-block">%s</div>' %
1840 '<div class="rst-block">%s</div>' %
1835 MarkupRenderer.rst(source, mentions=mentions))
1841 maybe_convert_relative_links(
1842 MarkupRenderer.rst(source, mentions=mentions)))
1836 elif renderer == 'markdown':
1843 elif renderer == 'markdown':
1837 return literal(
1844 return literal(
1838 '<div class="markdown-block">%s</div>' %
1845 '<div class="markdown-block">%s</div>' %
1839 MarkupRenderer.markdown(source, flavored=True, mentions=mentions))
1846 maybe_convert_relative_links(
1847 MarkupRenderer.markdown(source, flavored=True,
1848 mentions=mentions)))
1840 elif renderer == 'jupyter':
1849 elif renderer == 'jupyter':
1841 return literal(
1850 return literal(
1842 '<div class="ipynb">%s</div>' %
1851 '<div class="ipynb">%s</div>' %
1843 MarkupRenderer.jupyter(source))
1852 maybe_convert_relative_links(
1853 MarkupRenderer.jupyter(source)))
1844
1854
1845 # None means just show the file-source
1855 # None means just show the file-source
1846 return None
1856 return None
@@ -25,8 +25,10 b' Renderer for markup languages with abili'
25
25
26 import re
26 import re
27 import os
27 import os
28 import lxml
28 import logging
29 import logging
29 import itertools
30 import urlparse
31 import urllib
30
32
31 from mako.lookup import TemplateLookup
33 from mako.lookup import TemplateLookup
32 from mako.template import Template as MakoTemplate
34 from mako.template import Template as MakoTemplate
@@ -35,9 +37,9 b' from docutils.core import publish_parts'
35 from docutils.parsers.rst import directives
37 from docutils.parsers.rst import directives
36 import markdown
38 import markdown
37
39
38 from rhodecode.lib.markdown_ext import (
40 from rhodecode.lib.markdown_ext import GithubFlavoredMarkdownExtension
39 UrlizeExtension, GithubFlavoredMarkdownExtension)
41 from rhodecode.lib.utils2 import (
40 from rhodecode.lib.utils2 import safe_unicode, md5_safe, MENTIONS_REGEX
42 safe_str, safe_unicode, md5_safe, MENTIONS_REGEX)
41
43
42 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
43
45
@@ -45,6 +47,84 b' log = logging.getLogger(__name__)'
45 DEFAULT_COMMENTS_RENDERER = 'rst'
47 DEFAULT_COMMENTS_RENDERER = 'rst'
46
48
47
49
50 def relative_links(html_source, server_path):
51 doc = lxml.html.fromstring(html_source)
52 for el in doc.cssselect('img, video'):
53 src = el.attrib['src']
54 if src:
55 el.attrib['src'] = relative_path(src, server_path)
56
57 for el in doc.cssselect('a:not(.gfm)'):
58 src = el.attrib['href']
59 if src:
60 el.attrib['href'] = relative_path(src, server_path)
61
62 return lxml.html.tostring(doc)
63
64
65 def relative_path(path, request_path, is_repo_file=None):
66 """
67 relative link support, path is a rel path, and request_path is current
68 server path (not absolute)
69
70 e.g.
71
72 path = '../logo.png'
73 request_path= '/repo/files/path/file.md'
74 produces: '/repo/files/logo.png'
75 """
76 # TODO(marcink): unicode/str support ?
77 # maybe=> safe_unicode(urllib.quote(safe_str(final_path), '/:'))
78
79 def dummy_check(p):
80 return True # assume default is a valid file path
81
82 is_repo_file = is_repo_file or dummy_check
83 if not path:
84 return request_path
85
86 path = safe_unicode(path)
87 request_path = safe_unicode(request_path)
88
89 if path.startswith((u'data:', u'#', u':')):
90 # skip data, anchor, invalid links
91 return path
92
93 is_absolute = bool(urlparse.urlparse(path).netloc)
94 if is_absolute:
95 return path
96
97 if not request_path:
98 return path
99
100 if path.startswith(u'/'):
101 path = path[1:]
102
103 if path.startswith(u'./'):
104 path = path[2:]
105
106 parts = request_path.split('/')
107 # compute how deep we need to traverse the request_path
108 depth = 0
109
110 if is_repo_file(request_path):
111 # if request path is a VALID file, we use a relative path with
112 # one level up
113 depth += 1
114
115 while path.startswith(u'../'):
116 depth += 1
117 path = path[3:]
118
119 if depth > 0:
120 parts = parts[:-depth]
121
122 parts.append(path)
123 final_path = u'/'.join(parts).lstrip(u'/')
124
125 return u'/' + final_path
126
127
48 class MarkupRenderer(object):
128 class MarkupRenderer(object):
49 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
129 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
50
130
@@ -53,7 +53,7 b''
53 %else:
53 %else:
54 % if c.file.size < c.cut_off_limit:
54 % if c.file.size < c.cut_off_limit:
55 %if c.renderer and not c.annotate:
55 %if c.renderer and not c.annotate:
56 ${h.render(c.file.content, renderer=c.renderer)}
56 ${h.render(c.file.content, renderer=c.renderer, relative_url=h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
57 %else:
57 %else:
58 <table class="cb codehilite">
58 <table class="cb codehilite">
59 %if c.annotate:
59 %if c.annotate:
@@ -20,7 +20,8 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.markup_renderer import MarkupRenderer, RstTemplateRenderer
23 from rhodecode.lib.markup_renderer import (
24 MarkupRenderer, RstTemplateRenderer, relative_path, relative_links)
24
25
25
26
26 @pytest.mark.parametrize(
27 @pytest.mark.parametrize(
@@ -177,3 +178,78 b' Auto status change to |new_status|'
177 renderer = RstTemplateRenderer()
178 renderer = RstTemplateRenderer()
178 rendered = renderer.render('auto_status_change.mako', **params)
179 rendered = renderer.render('auto_status_change.mako', **params)
179 assert expected == rendered
180 assert expected == rendered
181
182
183 @pytest.mark.parametrize(
184 "src_path, server_path, is_path, expected",
185 [
186 ('source.png', '/repo/files/path', lambda p: False,
187 '/repo/files/path/source.png'),
188
189 ('source.png', 'mk/git/blob/master/README.md', lambda p: True,
190 '/mk/git/blob/master/source.png'),
191
192 ('./source.png', 'mk/git/blob/master/README.md', lambda p: True,
193 '/mk/git/blob/master/source.png'),
194
195 ('/source.png', 'mk/git/blob/master/README.md', lambda p: True,
196 '/mk/git/blob/master/source.png'),
197
198 ('./source.png', 'repo/files/path/source.md', lambda p: True,
199 '/repo/files/path/source.png'),
200
201 ('./source.png', '/repo/files/path/file.md', lambda p: True,
202 '/repo/files/path/source.png'),
203
204 ('../source.png', '/repo/files/path/file.md', lambda p: True,
205 '/repo/files/source.png'),
206
207 ('./../source.png', '/repo/files/path/file.md', lambda p: True,
208 '/repo/files/source.png'),
209
210 ('./source.png', '/repo/files/path/file.md', lambda p: True,
211 '/repo/files/path/source.png'),
212
213 ('../../../source.png', 'path/file.md', lambda p: True,
214 '/source.png'),
215
216 ('../../../../../source.png', '/path/file.md', None,
217 '/source.png'),
218
219 ('../../../../../source.png', 'files/path/file.md', None,
220 '/source.png'),
221
222 ('../../../../../https://google.com/image.png', 'files/path/file.md', None,
223 '/https://google.com/image.png'),
224
225 ('https://google.com/image.png', 'files/path/file.md', None,
226 'https://google.com/image.png'),
227
228 ('://foo', '/files/path/file.md', None,
229 '://foo'),
230
231 (u'한글.png', '/files/path/file.md', None,
232 u'/files/path/한글.png'),
233
234 ('my custom image.png', '/files/path/file.md', None,
235 '/files/path/my custom image.png'),
236 ])
237 def test_relative_path(src_path, server_path, is_path, expected):
238 path = relative_path(src_path, server_path, is_path)
239 assert path == expected
240
241
242 @pytest.mark.parametrize(
243 "src_html, expected_html",
244 [
245 ('<div></div>', '<div></div>'),
246 ('<img src="/file.png"></img>', '<img src="/path/raw/file.png">'),
247 ('<img src="data:abcd"/>', '<img src="data:abcd">'),
248 ('<a href="/file.png"></a>', '<a href="/path/raw/file.png"></a>'),
249 ('<a href="#anchor"></a>', '<a href="#anchor"></a>'),
250 ('<a href="./README.md"></a>', '<a href="/path/raw/README.md"></a>'),
251 ('<a href="../README.md"></a>', '<a href="/path/README.md"></a>'),
252
253 ])
254 def test_relative_links(src_html, expected_html):
255 assert relative_links(src_html, '/path/raw/file.md') == expected_html
General Comments 0
You need to be logged in to leave comments. Login now