##// END OF EJS Templates
Add a bbcode tag to render tag link
neko259 -
r1202:96809673 default
parent child Browse files
Show More
@@ -1,218 +1,230 b''
1 1 # coding=utf-8
2 2
3 3 import re
4 4 import bbcode
5 5
6 6 from urllib.parse import unquote
7 7
8 8 from django.core.exceptions import ObjectDoesNotExist
9 9 from django.core.urlresolvers import reverse
10 10
11 11 import boards
12 12
13 13
14 14 __author__ = 'neko259'
15 15
16 16
17 17 REFLINK_PATTERN = re.compile(r'^\d+$')
18 18 MULTI_NEWLINES_PATTERN = re.compile(r'(\r?\n){2,}')
19 19 ONE_NEWLINE = '\n'
20 20 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
21 21 LINE_BREAK_HTML = '<div class="br"></div>'
22 22
23 23
24 24 class TextFormatter():
25 25 """
26 26 An interface for formatter that can be used in the text format panel
27 27 """
28 28
29 29 def __init__(self):
30 30 pass
31 31
32 32 name = ''
33 33
34 34 # Left and right tags for the button preview
35 35 preview_left = ''
36 36 preview_right = ''
37 37
38 38 # Left and right characters for the textarea input
39 39 format_left = ''
40 40 format_right = ''
41 41
42 42
43 43 class AutolinkPattern():
44 44 def handleMatch(self, m):
45 45 link_element = etree.Element('a')
46 46 href = m.group(2)
47 47 link_element.set('href', href)
48 48 link_element.text = href
49 49
50 50 return link_element
51 51
52 52
53 53 class QuotePattern(TextFormatter):
54 54 name = '>q'
55 55 preview_left = '<span class="quote">'
56 56 preview_right = '</span>'
57 57
58 58 format_left = '[quote]'
59 59 format_right = '[/quote]'
60 60
61 61
62 62 class SpoilerPattern(TextFormatter):
63 63 name = 'spoiler'
64 64 preview_left = '<span class="spoiler">'
65 65 preview_right = '</span>'
66 66
67 67 format_left = '[spoiler]'
68 68 format_right = '[/spoiler]'
69 69
70 70 def handleMatch(self, m):
71 71 quote_element = etree.Element('span')
72 72 quote_element.set('class', 'spoiler')
73 73 quote_element.text = m.group(2)
74 74
75 75 return quote_element
76 76
77 77
78 78 class CommentPattern(TextFormatter):
79 79 name = ''
80 80 preview_left = '<span class="comment">// '
81 81 preview_right = '</span>'
82 82
83 83 format_left = '[comment]'
84 84 format_right = '[/comment]'
85 85
86 86
87 87 # TODO Use <s> tag here
88 88 class StrikeThroughPattern(TextFormatter):
89 89 name = 's'
90 90 preview_left = '<span class="strikethrough">'
91 91 preview_right = '</span>'
92 92
93 93 format_left = '[s]'
94 94 format_right = '[/s]'
95 95
96 96
97 97 class ItalicPattern(TextFormatter):
98 98 name = 'i'
99 99 preview_left = '<i>'
100 100 preview_right = '</i>'
101 101
102 102 format_left = '[i]'
103 103 format_right = '[/i]'
104 104
105 105
106 106 class BoldPattern(TextFormatter):
107 107 name = 'b'
108 108 preview_left = '<b>'
109 109 preview_right = '</b>'
110 110
111 111 format_left = '[b]'
112 112 format_right = '[/b]'
113 113
114 114
115 115 class CodePattern(TextFormatter):
116 116 name = 'code'
117 117 preview_left = '<code>'
118 118 preview_right = '</code>'
119 119
120 120 format_left = '[code]'
121 121 format_right = '[/code]'
122 122
123 123
124 124 def render_reflink(tag_name, value, options, parent, context):
125 125 result = '>>%s' % value
126 126
127 127 if REFLINK_PATTERN.match(value):
128 128 post_id = int(value)
129 129
130 130 try:
131 131 post = boards.models.Post.objects.get(id=post_id)
132 132
133 133 result = '<a href="%s">&gt;&gt;%s</a>' % (post.get_absolute_url(), post_id)
134 134 except ObjectDoesNotExist:
135 135 pass
136 136
137 137 return result
138 138
139 139
140 140 def render_quote(tag_name, value, options, parent, context):
141 141 source = ''
142 142 if 'source' in options:
143 143 source = options['source']
144 144
145 145 if source:
146 146 result = '<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
147 147 else:
148 148 # Insert a ">" at the start of every line
149 149 result = '<span class="quote">&gt;{}</span>'.format(
150 150 value.replace(LINE_BREAK_HTML,
151 151 '{}&gt;'.format(LINE_BREAK_HTML)))
152 152
153 153 return result
154 154
155 155
156 156 def render_notification(tag_name, value, options, parent, content):
157 157 username = value.lower()
158 158
159 159 return '<a href="{}" class="user-cast">@{}</a>'.format(
160 160 reverse('notifications', kwargs={'username': username}), username)
161 161
162 162
163 def render_tag(tag_name, value, options, parent, context):
164 tag_name = value.lower()
165
166 try:
167 url = boards.models.Tag.objects.get(name=tag_name).get_view()
168 except ObjectDoesNotExist:
169 url = tag_name
170
171 return url
172
173
163 174 formatters = [
164 175 QuotePattern,
165 176 SpoilerPattern,
166 177 ItalicPattern,
167 178 BoldPattern,
168 179 CommentPattern,
169 180 StrikeThroughPattern,
170 181 CodePattern,
171 182 ]
172 183
173 184
174 185 PREPARSE_PATTERNS = {
175 186 r'(?<!>)>>(\d+)': r'[post]\1[/post]', # Reflink ">>123"
176 187 r'^>([^>].+)': r'[quote]\1[/quote]', # Quote ">text"
177 188 r'^//(.+)': r'[comment]\1[/comment]', # Comment "//text"
178 189 r'\B@(\w+)': r'[user]\1[/user]', # User notification "@user"
179 190 }
180 191
181 192
182 193 class Parser:
183 194 def __init__(self):
184 195 # The newline hack is added because br's margin does not work in all
185 196 # browsers except firefox, when the div's does.
186 197 self.parser = bbcode.Parser(newline=LINE_BREAK_HTML)
187 198
188 199 self.parser.add_formatter('post', render_reflink, strip=True)
189 200 self.parser.add_formatter('quote', render_quote, strip=True)
190 201 self.parser.add_formatter('user', render_notification, strip=True)
202 self.parser.add_formatter('tag', render_tag, strip=True)
191 203 self.parser.add_simple_formatter(
192 204 'comment', '<span class="comment">//%(value)s</span>')
193 205 self.parser.add_simple_formatter(
194 206 'spoiler', '<span class="spoiler">%(value)s</span>')
195 207 self.parser.add_simple_formatter(
196 208 's', '<span class="strikethrough">%(value)s</span>')
197 209 # TODO Why not use built-in tag?
198 210 self.parser.add_simple_formatter('code',
199 211 '<pre><code>%(value)s</pre></code>',
200 212 render_embedded=False)
201 213
202 214 def preparse(self, text):
203 215 """
204 216 Performs manual parsing before the bbcode parser is used.
205 217 Preparsed text is saved as raw and the text before preparsing is lost.
206 218 """
207 219 new_text = MULTI_NEWLINES_PATTERN.sub(ONE_NEWLINE, text)
208 220
209 221 for key, value in PREPARSE_PATTERNS.items():
210 222 new_text = re.sub(key, value, new_text, flags=re.MULTILINE)
211 223
212 224 for link in REGEX_URL.findall(text):
213 225 new_text = new_text.replace(link, unquote(link))
214 226
215 227 return new_text
216 228
217 229 def parse(self, text):
218 230 return self.parser.format(text)
General Comments 0
You need to be logged in to leave comments. Login now