##// 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 38 from rhodecode.lib.auth import (
39 39 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, XHRRequired)
40 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 42 from rhodecode.lib.ext_json import json
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44 from rhodecode.lib.vcs.exceptions import (
@@ -70,7 +70,12 b' class SummaryController(BaseRepoControll'
70 70 log.debug("Searching for a README file.")
71 71 readme_node = ReadmeFinder(default_renderer).search(commit)
72 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 79 readme_filename = readme_node.path
75 80 return readme_data, readme_filename
76 81
@@ -95,13 +100,16 b' class SummaryController(BaseRepoControll'
95 100 log.exception(
96 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 104 log.debug(
100 105 'Found README file `%s` rendering...', readme_node.path)
101 106 renderer = MarkupRenderer()
102 107 try:
103 return renderer.render(
108 html_source = renderer.render(
104 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 113 except Exception:
106 114 log.exception(
107 115 "Exception while trying to render the README")
@@ -75,7 +75,7 b' from rhodecode.lib.utils import repo_nam'
75 75 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
76 76 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
77 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 79 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
80 80 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
81 81 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
@@ -1828,19 +1828,29 b' def renderer_from_filename(filename, exc'
1828 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 1838 if renderer == 'rst':
1833 1839 return literal(
1834 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 1843 elif renderer == 'markdown':
1837 1844 return literal(
1838 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 1849 elif renderer == 'jupyter':
1841 1850 return literal(
1842 1851 '<div class="ipynb">%s</div>' %
1843 MarkupRenderer.jupyter(source))
1852 maybe_convert_relative_links(
1853 MarkupRenderer.jupyter(source)))
1844 1854
1845 1855 # None means just show the file-source
1846 1856 return None
@@ -25,8 +25,10 b' Renderer for markup languages with abili'
25 25
26 26 import re
27 27 import os
28 import lxml
28 29 import logging
29 import itertools
30 import urlparse
31 import urllib
30 32
31 33 from mako.lookup import TemplateLookup
32 34 from mako.template import Template as MakoTemplate
@@ -35,9 +37,9 b' from docutils.core import publish_parts'
35 37 from docutils.parsers.rst import directives
36 38 import markdown
37 39
38 from rhodecode.lib.markdown_ext import (
39 UrlizeExtension, GithubFlavoredMarkdownExtension)
40 from rhodecode.lib.utils2 import safe_unicode, md5_safe, MENTIONS_REGEX
40 from rhodecode.lib.markdown_ext import GithubFlavoredMarkdownExtension
41 from rhodecode.lib.utils2 import (
42 safe_str, safe_unicode, md5_safe, MENTIONS_REGEX)
41 43
42 44 log = logging.getLogger(__name__)
43 45
@@ -45,6 +47,84 b' log = logging.getLogger(__name__)'
45 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 128 class MarkupRenderer(object):
49 129 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
50 130
@@ -53,7 +53,7 b''
53 53 %else:
54 54 % if c.file.size < c.cut_off_limit:
55 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 57 %else:
58 58 <table class="cb codehilite">
59 59 %if c.annotate:
@@ -20,7 +20,8 b''
20 20
21 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 27 @pytest.mark.parametrize(
@@ -177,3 +178,78 b' Auto status change to |new_status|'
177 178 renderer = RstTemplateRenderer()
178 179 rendered = renderer.render('auto_status_change.mako', **params)
179 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