##// END OF EJS Templates
Added a setting to enable additional spaces inside spoiler tag
neko259 -
r1832:10b12be1 default
parent child Browse files
Show More
@@ -1,48 +1,49 b''
1 1 [Version]
2 2 Version = 4.1.0 2017
3 3 SiteName = Neboard DEV
4 4
5 5 [Cache]
6 6 # Timeout for caching, if cache is used
7 7 CacheTimeout = 600
8 8
9 9 [Forms]
10 10 # Max post length in characters
11 11 MaxTextLength = 30000
12 12 MaxFileSize = 8000000
13 13 LimitFirstPosting = true
14 14 LimitPostingSpeed = false
15 15 PowDifficulty = 0
16 16 # Delay in seconds
17 17 PostingDelay = 30
18 18 Autoban = false
19 19 DefaultTag = test
20 20 MaxFileCount = 1
21 AdditionalSpoilerSpaces = false
21 22
22 23 [Messages]
23 24 # Thread bumplimit
24 25 MaxPostsPerThread = 10
25 26 ThreadArchiveDays = 300
26 27 AnonymousMode = false
27 28
28 29 [View]
29 30 DefaultTheme = md
30 31 DefaultImageViewer = simple
31 32 LastRepliesCount = 3
32 33 ThreadsPerPage = 3
33 34 PostsPerPage = 10
34 35 ImagesPerPageGallery = 20
35 36 MaxFavoriteThreads = 20
36 37 MaxLandingThreads = 20
37 38 Themes=md:Mystic Dark,md_centered:Mystic Dark (centered),sw:Snow White,pg:Photon Grey,ad:Amanita Dark,iw:Inocibe White
38 39 ImageViewers=simple:Simple,popup:Popup
39 40
40 41 [Storage]
41 42 # Enable archiving threads instead of deletion when the thread limit is reached
42 43 ArchiveThreads = true
43 44
44 45 [RSS]
45 46 MaxItems = 20
46 47
47 48 [External]
48 49 ImageSearchHost=
@@ -1,276 +1,282 b''
1 1 # coding=utf-8
2 2
3 3 import re
4 4 import random
5 5 import bbcode
6 6
7 7 from urllib.parse import unquote
8 8
9 9 from django.core.exceptions import ObjectDoesNotExist
10 10 from django.core.urlresolvers import reverse
11 11
12 12 import boards
13 from boards import settings
13 14
14 15
15 16 __author__ = 'neko259'
16 17
17 18
18 19 REFLINK_PATTERN = re.compile(r'^\d+$')
19 20 GLOBAL_REFLINK_PATTERN = re.compile(r'(\w+)::([^:]+)::(\d+)')
20 21 MULTI_NEWLINES_PATTERN = re.compile(r'(\r?\n){2,}')
21 22 ONE_NEWLINE = '\n'
22 23 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
23 24 LINE_BREAK_HTML = '<div class="br"></div>'
24 25 SPOILER_SPACE = '&nbsp;'
25 26
26 27 MAX_SPOILER_MULTIPLIER = 2
28 MAX_SPOILER_SPACE_COUNT = 20
27 29
28 30
29 31 class TextFormatter():
30 32 """
31 33 An interface for formatter that can be used in the text format panel
32 34 """
33 35
34 36 def __init__(self):
35 37 pass
36 38
37 39 name = ''
38 40
39 41 # Left and right tags for the button preview
40 42 preview_left = ''
41 43 preview_right = ''
42 44
43 45 # Left and right characters for the textarea input
44 46 format_left = ''
45 47 format_right = ''
46 48
47 49
48 50 class AutolinkPattern():
49 51 def handleMatch(self, m):
50 52 link_element = etree.Element('a')
51 53 href = m.group(2)
52 54 link_element.set('href', href)
53 55 link_element.text = href
54 56
55 57 return link_element
56 58
57 59
58 60 class QuotePattern(TextFormatter):
59 61 name = '>q'
60 62 preview_left = '<span class="quote">'
61 63 preview_right = '</span>'
62 64
63 65 format_left = '[quote]'
64 66 format_right = '[/quote]'
65 67
66 68
67 69 class SpoilerPattern(TextFormatter):
68 70 name = 'spoiler'
69 71 preview_left = '<span class="spoiler">'
70 72 preview_right = '</span>'
71 73
72 74 format_left = '[spoiler]'
73 75 format_right = '[/spoiler]'
74 76
75 77
76 78 class CommentPattern(TextFormatter):
77 79 name = ''
78 80 preview_left = '<span class="comment">// '
79 81 preview_right = '</span>'
80 82
81 83 format_left = '[comment]'
82 84 format_right = '[/comment]'
83 85
84 86
85 87 # TODO Use <s> tag here
86 88 class StrikeThroughPattern(TextFormatter):
87 89 name = 's'
88 90 preview_left = '<span class="strikethrough">'
89 91 preview_right = '</span>'
90 92
91 93 format_left = '[s]'
92 94 format_right = '[/s]'
93 95
94 96
95 97 class ItalicPattern(TextFormatter):
96 98 name = 'i'
97 99 preview_left = '<i>'
98 100 preview_right = '</i>'
99 101
100 102 format_left = '[i]'
101 103 format_right = '[/i]'
102 104
103 105
104 106 class BoldPattern(TextFormatter):
105 107 name = 'b'
106 108 preview_left = '<b>'
107 109 preview_right = '</b>'
108 110
109 111 format_left = '[b]'
110 112 format_right = '[/b]'
111 113
112 114
113 115 class CodePattern(TextFormatter):
114 116 name = 'code'
115 117 preview_left = '<code>'
116 118 preview_right = '</code>'
117 119
118 120 format_left = '[code]'
119 121 format_right = '[/code]'
120 122
121 123
122 124 class HintPattern(TextFormatter):
123 125 name = 'hint'
124 126 preview_left = '<span class="hint">'
125 127 preview_right = '</span>'
126 128
127 129 format_left = '[hint]'
128 130 format_right = '[/hint]'
129 131
130 132
131 133 def render_reflink(tag_name, value, options, parent, context):
132 134 result = '>>%s' % value
133 135
134 136 post = None
135 137 if REFLINK_PATTERN.match(value):
136 138 post_id = int(value)
137 139
138 140 try:
139 141 post = boards.models.Post.objects.get(id=post_id)
140 142
141 143 except ObjectDoesNotExist:
142 144 pass
143 145 elif GLOBAL_REFLINK_PATTERN.match(value):
144 146 match = GLOBAL_REFLINK_PATTERN.search(value)
145 147 try:
146 148 global_id = boards.models.GlobalId.objects.get(
147 149 key_type=match.group(1), key=match.group(2),
148 150 local_id=match.group(3))
149 151 post = global_id.post
150 152 except ObjectDoesNotExist:
151 153 pass
152 154
153 155 if post is not None:
154 156 result = post.get_link_view()
155 157
156 158 return result
157 159
158 160
159 161 def render_quote(tag_name, value, options, parent, context):
160 162 source = ''
161 163 if 'source' in options:
162 164 source = options['source']
163 165 elif 'quote' in options:
164 166 source = options['quote']
165 167
166 168 if source:
167 169 result = '<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
168 170 else:
169 171 # Insert a ">" at the start of every line
170 172 result = '<span class="quote">&gt;{}</span>'.format(
171 173 value.replace(LINE_BREAK_HTML,
172 174 '{}&gt;'.format(LINE_BREAK_HTML)))
173 175
174 176 return result
175 177
176 178
177 179 def render_hint(tag_name, value, options, parent, context):
178 180 if 'hint' in options:
179 181 hint = options['hint']
180 182 result = '<span class="hint" title="{}">{}</span>'.format(hint, value)
181 183 else:
182 184 result = value
183 185 return result
184 186
185 187
186 188 def render_notification(tag_name, value, options, parent, content):
187 189 username = value.lower()
188 190
189 191 return '<a href="{}" class="user-cast">@{}</a>'.format(
190 192 reverse('notifications', kwargs={'username': username}), username)
191 193
192 194
193 195 def render_tag(tag_name, value, options, parent, context):
194 196 tag_name = value.lower()
195 197
196 198 try:
197 199 url = boards.models.Tag.objects.get(name=tag_name).get_view()
198 200 except ObjectDoesNotExist:
199 201 url = tag_name
200 202
201 203 return url
202 204
203 205
204 206 def render_spoiler(tag_name, value, options, parent, context):
205 text_len = len(value)
206 space_count = random.randint(0, text_len * MAX_SPOILER_MULTIPLIER)
207 side_spaces = SPOILER_SPACE * (space_count // 2)
208 return '<span class="spoiler">{}{}{}</span>'.format(side_spaces, value,
209 side_spaces)
207 if settings.get_bool('Forms', 'AdditionalSpoilerSpaces'):
208 text_len = len(value)
209 space_count = min(random.randint(0, text_len * MAX_SPOILER_MULTIPLIER),
210 MAX_SPOILER_SPACE_COUNT)
211 side_spaces = SPOILER_SPACE * (space_count // 2)
212 else:
213 side_spaces = ''
214 return '<span class="spoiler">{}{}{}</span>'.format(side_spaces,
215 value, side_spaces)
210 216
211 217
212 218 formatters = [
213 219 QuotePattern,
214 220 SpoilerPattern,
215 221 ItalicPattern,
216 222 BoldPattern,
217 223 CommentPattern,
218 224 StrikeThroughPattern,
219 225 CodePattern,
220 226 HintPattern,
221 227 ]
222 228
223 229
224 230 PREPARSE_PATTERNS = {
225 231 r'(?<!>)>>(\d+)': r'[post]\1[/post]', # Reflink ">>123"
226 232 r'^>([^>].+)': r'[quote]\1[/quote]', # Quote ">text"
227 233 r'^//\s?(.+)': r'[comment]\1[/comment]', # Comment "//text"
228 234 r'\B@(\w+)': r'[user]\1[/user]', # User notification "@user"
229 235 }
230 236
231 237
232 238 class Parser:
233 239 def __init__(self):
234 240 # The newline hack is added because br's margin does not work in all
235 241 # browsers except firefox, when the div's does.
236 242 self.parser = bbcode.Parser(newline=LINE_BREAK_HTML)
237 243
238 244 self.parser.add_formatter('post', render_reflink, strip=True)
239 245 self.parser.add_formatter('quote', render_quote, strip=True)
240 246 self.parser.add_formatter('hint', render_hint, strip=True)
241 247 self.parser.add_formatter('user', render_notification, strip=True)
242 248 self.parser.add_formatter('tag', render_tag, strip=True)
243 249 self.parser.add_formatter('spoiler', render_spoiler, strip=True)
244 250 self.parser.add_simple_formatter(
245 251 'comment', '<span class="comment">// %(value)s</span>', strip=True)
246 252 self.parser.add_simple_formatter(
247 253 's', '<span class="strikethrough">%(value)s</span>')
248 254 self.parser.add_simple_formatter('code',
249 255 '<pre><code>%(value)s</pre></code>',
250 256 render_embedded=False,
251 257 escape_html=True,
252 258 replace_links=False,
253 259 replace_cosmetic=False)
254 260
255 261 def preparse(self, text):
256 262 """
257 263 Performs manual parsing before the bbcode parser is used.
258 264 Preparsed text is saved as raw and the text before preparsing is lost.
259 265 """
260 266 new_text = MULTI_NEWLINES_PATTERN.sub(ONE_NEWLINE, text)
261 267
262 268 for key, value in PREPARSE_PATTERNS.items():
263 269 new_text = re.sub(key, value, new_text, flags=re.MULTILINE)
264 270
265 271 for link in REGEX_URL.findall(text):
266 272 new_text = new_text.replace(link, unquote(link))
267 273
268 274 return new_text
269 275
270 276 def parse(self, text):
271 277 return self.parser.format(text)
272 278
273 279
274 280 parser = Parser()
275 281 def get_parser():
276 282 return parser
General Comments 0
You need to be logged in to leave comments. Login now