##// END OF EJS Templates
Enable all Markdown Extra in Python markdown library.
xpol -
r3164:01cb7df1 beta
parent child Browse files
Show More
@@ -1,150 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.markup_renderer
3 rhodecode.lib.markup_renderer
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6
6
7 Renderer for markup languages with ability to parse using rst or markdown
7 Renderer for markup languages with ability to parse using rst or markdown
8
8
9 :created_on: Oct 27, 2011
9 :created_on: Oct 27, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import re
27 import re
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from rhodecode.lib.utils2 import safe_unicode, MENTIONS_REGEX
31 from rhodecode.lib.utils2 import safe_unicode, MENTIONS_REGEX
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class MarkupRenderer(object):
36 class MarkupRenderer(object):
37 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
37 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
38
38
39 MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
39 MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
40 RST_PAT = re.compile(r're?st', re.IGNORECASE)
40 RST_PAT = re.compile(r're?st', re.IGNORECASE)
41 PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
41 PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
42
42
43 def __detect_renderer(self, source, filename=None):
43 def __detect_renderer(self, source, filename=None):
44 """
44 """
45 runs detection of what renderer should be used for generating html
45 runs detection of what renderer should be used for generating html
46 from a markup language
46 from a markup language
47
47
48 filename can be also explicitly a renderer name
48 filename can be also explicitly a renderer name
49
49
50 :param source:
50 :param source:
51 :param filename:
51 :param filename:
52 """
52 """
53
53
54 if MarkupRenderer.MARKDOWN_PAT.findall(filename):
54 if MarkupRenderer.MARKDOWN_PAT.findall(filename):
55 detected_renderer = 'markdown'
55 detected_renderer = 'markdown'
56 elif MarkupRenderer.RST_PAT.findall(filename):
56 elif MarkupRenderer.RST_PAT.findall(filename):
57 detected_renderer = 'rst'
57 detected_renderer = 'rst'
58 elif MarkupRenderer.PLAIN_PAT.findall(filename):
58 elif MarkupRenderer.PLAIN_PAT.findall(filename):
59 detected_renderer = 'rst'
59 detected_renderer = 'rst'
60 else:
60 else:
61 detected_renderer = 'plain'
61 detected_renderer = 'plain'
62
62
63 return getattr(MarkupRenderer, detected_renderer)
63 return getattr(MarkupRenderer, detected_renderer)
64
64
65 def render(self, source, filename=None):
65 def render(self, source, filename=None):
66 """
66 """
67 Renders a given filename using detected renderer
67 Renders a given filename using detected renderer
68 it detects renderers based on file extension or mimetype.
68 it detects renderers based on file extension or mimetype.
69 At last it will just do a simple html replacing new lines with <br/>
69 At last it will just do a simple html replacing new lines with <br/>
70
70
71 :param file_name:
71 :param file_name:
72 :param source:
72 :param source:
73 """
73 """
74
74
75 renderer = self.__detect_renderer(source, filename)
75 renderer = self.__detect_renderer(source, filename)
76 readme_data = renderer(source)
76 readme_data = renderer(source)
77 return readme_data
77 return readme_data
78
78
79 @classmethod
79 @classmethod
80 def plain(cls, source):
80 def plain(cls, source):
81 source = safe_unicode(source)
81 source = safe_unicode(source)
82
82
83 def urlify_text(text):
83 def urlify_text(text):
84 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
84 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
85 '|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
85 '|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
86
86
87 def url_func(match_obj):
87 def url_func(match_obj):
88 url_full = match_obj.groups()[0]
88 url_full = match_obj.groups()[0]
89 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
89 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
90
90
91 return url_pat.sub(url_func, text)
91 return url_pat.sub(url_func, text)
92
92
93 source = urlify_text(source)
93 source = urlify_text(source)
94 return '<br />' + source.replace("\n", '<br />')
94 return '<br />' + source.replace("\n", '<br />')
95
95
96 @classmethod
96 @classmethod
97 def markdown(cls, source, safe=True):
97 def markdown(cls, source, safe=True):
98 source = safe_unicode(source)
98 source = safe_unicode(source)
99 try:
99 try:
100 import markdown as __markdown
100 import markdown as __markdown
101 return __markdown.markdown(source, ['codehilite', 'tables'])
101 return __markdown.markdown(source, ['codehilite', 'extra'])
102 except ImportError:
102 except ImportError:
103 log.warning('Install markdown to use this function')
103 log.warning('Install markdown to use this function')
104 return cls.plain(source)
104 return cls.plain(source)
105 except Exception:
105 except Exception:
106 log.error(traceback.format_exc())
106 log.error(traceback.format_exc())
107 if safe:
107 if safe:
108 return source
108 return source
109 else:
109 else:
110 raise
110 raise
111
111
112 @classmethod
112 @classmethod
113 def rst(cls, source, safe=True):
113 def rst(cls, source, safe=True):
114 source = safe_unicode(source)
114 source = safe_unicode(source)
115 try:
115 try:
116 from docutils.core import publish_parts
116 from docutils.core import publish_parts
117 from docutils.parsers.rst import directives
117 from docutils.parsers.rst import directives
118 docutils_settings = dict([(alias, None) for alias in
118 docutils_settings = dict([(alias, None) for alias in
119 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
119 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
120
120
121 docutils_settings.update({'input_encoding': 'unicode',
121 docutils_settings.update({'input_encoding': 'unicode',
122 'report_level': 4})
122 'report_level': 4})
123
123
124 for k, v in docutils_settings.iteritems():
124 for k, v in docutils_settings.iteritems():
125 directives.register_directive(k, v)
125 directives.register_directive(k, v)
126
126
127 parts = publish_parts(source=source,
127 parts = publish_parts(source=source,
128 writer_name="html4css1",
128 writer_name="html4css1",
129 settings_overrides=docutils_settings)
129 settings_overrides=docutils_settings)
130
130
131 return parts['html_title'] + parts["fragment"]
131 return parts['html_title'] + parts["fragment"]
132 except ImportError:
132 except ImportError:
133 log.warning('Install docutils to use this function')
133 log.warning('Install docutils to use this function')
134 return cls.plain(source)
134 return cls.plain(source)
135 except Exception:
135 except Exception:
136 log.error(traceback.format_exc())
136 log.error(traceback.format_exc())
137 if safe:
137 if safe:
138 return source
138 return source
139 else:
139 else:
140 raise
140 raise
141
141
142 @classmethod
142 @classmethod
143 def rst_with_mentions(cls, source):
143 def rst_with_mentions(cls, source):
144 mention_pat = re.compile(MENTIONS_REGEX)
144 mention_pat = re.compile(MENTIONS_REGEX)
145
145
146 def wrapp(match_obj):
146 def wrapp(match_obj):
147 uname = match_obj.groups()[0]
147 uname = match_obj.groups()[0]
148 return ' **@%(uname)s** ' % {'uname': uname}
148 return ' **@%(uname)s** ' % {'uname': uname}
149 mention_hl = mention_pat.sub(wrapp, source).strip()
149 mention_hl = mention_pat.sub(wrapp, source).strip()
150 return cls.rst(mention_hl)
150 return cls.rst(mention_hl)
General Comments 0
You need to be logged in to leave comments. Login now