##// END OF EJS Templates
Complete denisopopoving
Bohdan Horbeshko -
r2139:ecf526ad default
parent child Browse files
Show More
@@ -0,0 +1,296 b''
1 # coding=utf-8
2 from xml import etree
3
4 import re
5 import random
6 import bbcode
7
8 from urllib.parse import unquote
9
10 from django.core.exceptions import ObjectDoesNotExist
11 from django.urls import reverse
12 from django.utils.translation import ugettext_lazy as _
13
14 import boards
15 from boards import settings
16 from swineboard.settings import ALLOWED_HOSTS
17
18
19 __author__ = 'neko259'
20
21
22 REFLINK_PATTERN = re.compile(r'^\d+$')
23 GLOBAL_REFLINK_PATTERN = re.compile(r'(\w+)::([^:]+)::(\d+)')
24 MULTI_NEWLINES_PATTERN = re.compile(r'(\r?\n){2,}')
25 ONE_NEWLINE = '\n'
26 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
27 LINE_BREAK_HTML = '<div class="br"></div>'
28 SPOILER_SPACE = '&nbsp;'
29 REGEX_ANY_LINE_BREAK = re.compile(r'(' + LINE_BREAK_HTML + '|\r|\n)')
30 QUOTE_REPLACEMENT = '\g<1>&gt;'
31
32 MAX_SPOILER_MULTIPLIER = 2
33 MAX_SPOILER_SPACE_COUNT = 20
34
35
36 class TextFormatter:
37 """
38 An interface for formatter that can be used in the text format panel
39 """
40
41 def __init__(self):
42 pass
43
44 name = ''
45
46 # Left and right tags for the button preview
47 preview_left = ''
48 preview_right = ''
49
50 # Tag name to enclose the text in the form
51 tag_name = ''
52
53 has_input = 'false'
54 input_prompt = ''
55
56
57 class AutolinkPattern:
58 def handleMatch(self, m):
59 link_element = etree.Element('a')
60 href = m.group(2)
61 link_element.set('href', href)
62 link_element.text = href
63
64 return link_element
65
66
67 class QuotePattern(TextFormatter):
68 name = '>q'
69 preview_left = '<span class="quote">'
70 preview_right = '</span>'
71
72 tag_name = 'quote'
73
74
75 class SpoilerPattern(TextFormatter):
76 name = 'spoiler'
77 preview_left = '<span class="spoiler">'
78 preview_right = '</span>'
79
80 tag_name = 'spoiler'
81
82
83 class CommentPattern(TextFormatter):
84 name = ''
85 preview_left = '<span class="comment">// '
86 preview_right = '</span>'
87
88 tag_name = 'comment'
89
90
91 # TODO Use <s> tag here
92 class StrikeThroughPattern(TextFormatter):
93 name = 's'
94 preview_left = '<span class="strikethrough">'
95 preview_right = '</span>'
96
97 tag_name = 's'
98
99
100 class ItalicPattern(TextFormatter):
101 name = 'i'
102 preview_left = '<i>'
103 preview_right = '</i>'
104
105 tag_name = 'i'
106
107
108 class BoldPattern(TextFormatter):
109 name = 'b'
110 preview_left = '<b>'
111 preview_right = '</b>'
112
113 tag_name = 'b'
114
115
116 class CodePattern(TextFormatter):
117 name = 'code'
118 preview_left = '<code>'
119 preview_right = '</code>'
120
121 tag_name = 'code'
122
123
124 class HintPattern(TextFormatter):
125 name = 'hint'
126 preview_left = '<span class="hint">'
127 preview_right = '</span>'
128
129 tag_name = 'hint'
130
131
132 class ColorPattern(TextFormatter):
133 name = 'color'
134 preview_left = '<span style="color:yellow;">'
135 preview_right = '</span>'
136
137 tag_name = 'color'
138
139 has_input = 'true'
140 input_prompt = _('Enter color (name or #ccc code)')
141
142
143 def render_reflink(tag_name, value, options, parent, context):
144 result = '>>%s' % value
145
146 post = None
147 if REFLINK_PATTERN.match(value):
148 post_id = int(value)
149
150 try:
151 post = boards.models.Post.objects.get(id=post_id)
152
153 except ObjectDoesNotExist:
154 pass
155 elif GLOBAL_REFLINK_PATTERN.match(value):
156 match = GLOBAL_REFLINK_PATTERN.search(value)
157 try:
158 global_id = boards.models.GlobalId.objects.get(
159 key_type=match.group(1), key=match.group(2),
160 local_id=match.group(3))
161 post = global_id.post
162 except ObjectDoesNotExist:
163 pass
164
165 if post is not None:
166 result = post.get_link_view()
167
168 return result
169
170
171 def render_quote(tag_name, value, options, parent, context):
172 source = options.get('quote') or options.get('source')
173
174 if source:
175 result = '<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
176 else:
177 # Insert a ">" at the start of every line
178 result = '<span class="quote">&gt;{}</span>'.format(
179 REGEX_ANY_LINE_BREAK.sub(QUOTE_REPLACEMENT, value))
180
181 return result
182
183
184 def render_hint(tag_name, value, options, parent, context):
185 if 'hint' in options:
186 hint = options['hint']
187 result = '<span class="hint" title="{}">{}</span>'.format(hint, value)
188 else:
189 result = value
190 return result
191
192
193 def render_notification(tag_name, value, options, parent, content):
194 username = value.lower()
195
196 return '<a href="{}" class="user-cast">@{}</a>'.format(
197 reverse('notifications', kwargs={'username': username}), username)
198
199
200 def render_tag(tag_name, value, options, parent, context):
201 tag_name = value.lower()
202
203 tag = boards.models.Tag.objects.get_by_alias(tag_name)
204 if tag:
205 url = tag.get_view()
206 else:
207 url = tag_name
208
209 return url
210
211
212 def render_spoiler(tag_name, value, options, parent, context):
213 if settings.get_bool('Forms', 'AdditionalSpoilerSpaces'):
214 text_len = len(value)
215 space_count = min(random.randint(0, text_len * MAX_SPOILER_MULTIPLIER),
216 MAX_SPOILER_SPACE_COUNT)
217 side_spaces = SPOILER_SPACE * (space_count // 2)
218 else:
219 side_spaces = ''
220 return '<span class="spoiler">{}{}{}</span>'.format(side_spaces,
221 value, side_spaces)
222
223
224 formatters = [
225 QuotePattern,
226 SpoilerPattern,
227 ItalicPattern,
228 BoldPattern,
229 CommentPattern,
230 StrikeThroughPattern,
231 CodePattern,
232 HintPattern,
233 ColorPattern,
234 ]
235
236
237 PREPARSE_PATTERNS = {
238 r'(?<!>)>>(\d+)': r'[post]\1[/post]', # Reflink ">>123"
239 r'^>([^>].+)': r'[quote]\1[/quote]', # Quote ">text"
240 r'^//\s?(.+)': r'[comment]\1[/comment]', # Comment "//text"
241 r'\B@(\w+)': r'[user]\1[/user]', # User notification "@user"
242 }
243
244 for hostname in ALLOWED_HOSTS:
245 if hostname != '*':
246 PREPARSE_PATTERNS[r'https?://{}/thread/\d+/#(\d+)/?'.format(hostname)] = r'[post]\1[/post]'
247 PREPARSE_PATTERNS[r'https?://{}/thread/(\d+)/?'.format(hostname)] = r'[post]\1[/post]'
248
249
250 class Parser:
251 def __init__(self):
252 # The newline hack is added because br's margin does not work in all
253 # browsers except firefox, when the div's does.
254 self.parser = bbcode.Parser(newline=LINE_BREAK_HTML)
255
256 self.parser.add_formatter('post', render_reflink, strip=True)
257 self.parser.add_formatter('quote', render_quote, strip=True)
258 self.parser.add_formatter('hint', render_hint, strip=True)
259 self.parser.add_formatter('user', render_notification, strip=True)
260 self.parser.add_formatter('tag', render_tag, strip=True)
261 self.parser.add_formatter('spoiler', render_spoiler, strip=True)
262 self.parser.add_simple_formatter(
263 'comment', '<span class="comment">// %(value)s</span>', strip=True)
264 self.parser.add_simple_formatter(
265 's', '<span class="strikethrough">%(value)s</span>')
266 self.parser.add_simple_formatter('code',
267 '<pre><code>%(value)s</pre></code>',
268 render_embedded=False,
269 escape_html=True,
270 replace_links=False,
271 replace_cosmetic=False)
272
273 def preparse(self, text):
274 """
275 Performs manual parsing before the bbcode parser is used.
276 Preparsed text is saved as raw and the text before preparsing is lost.
277 """
278 new_text = MULTI_NEWLINES_PATTERN.sub(ONE_NEWLINE, text)
279
280 for key, value in PREPARSE_PATTERNS.items():
281 new_text = re.sub(key, value, new_text, flags=re.MULTILINE)
282
283 for link in REGEX_URL.findall(text):
284 new_text = new_text.replace(link, unquote(link))
285
286 return new_text
287
288 def parse(self, text):
289 return self.parser.format(text)
290
291
292 parser = Parser()
293
294
295 def get_parser():
296 return parser
@@ -0,0 +1,10 b''
1 [Unit]
2 Description=Swineboard imageboard
3 After=network.target
4
5 [Service]
6 ExecStart=/usr/bin/uwsgi_python33 --ini uwsgi.ini
7 WorkingDirectory=<where is your swineboard located>
8
9 [Install]
10 WantedBy=multi-user.target
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now