##// END OF EJS Templates
Small code cleanups
neko259 -
r721:1814d7a8 default
parent child Browse files
Show More
@@ -1,31 +1,35
1 __author__ = 'neko259'
2
1 from django.utils.translation import ugettext_lazy as _
3 from django.utils.translation import ugettext_lazy as _
2
4
3 __author__ = 'neko259'
5 ATTR_ROLES = 'roles'
6 ATTR_CONTACTS = 'contacts'
7 ATTR_NAME = 'name'
4
8
5 ROLE_AUTHOR = _('author')
9 ROLE_AUTHOR = _('author')
6 ROLE_DEVELOPER = _('developer')
10 ROLE_DEVELOPER = _('developer')
7 ROLE_JS_DEV = _('javascript developer')
11 ROLE_JS_DEV = _('javascript developer')
8 ROLE_DESIGNER = _('designer')
12 ROLE_DESIGNER = _('designer')
9
13
10 authors = {
14 authors = {
11 'neko259': {
15 'neko259': {
12 'name': 'Pavel Ryapolov',
16 ATTR_NAME: 'Pavel Ryapolov',
13 'contacts': ['neko259@gmail.com'],
17 ATTR_CONTACTS: ['neko259@gmail.com'],
14 'roles': [ROLE_AUTHOR, ROLE_DEVELOPER],
18 ATTR_ROLES: [ROLE_AUTHOR, ROLE_DEVELOPER],
15 },
19 },
16 'ilyas': {
20 'ilyas': {
17 'name': 'Ilyas Babayev',
21 ATTR_NAME: 'Ilyas Babayev',
18 'contacts': ['zamesilyasa@gmail.com'],
22 ATTR_CONTACTS: ['zamesilyasa@gmail.com'],
19 'roles': [ROLE_AUTHOR, ROLE_DEVELOPER],
23 ATTR_ROLES: [ROLE_AUTHOR, ROLE_DEVELOPER],
20 },
24 },
21 'ritsufag': {
25 'ritsufag': {
22 'name': 'Aiko Kirino',
26 ATTR_NAME: 'Aiko Kirino',
23 'contacts': ['ritsufag@gmail.com'],
27 ATTR_CONTACTS: ['ritsufag@gmail.com'],
24 'roles': [ROLE_JS_DEV, ROLE_DESIGNER],
28 ATTR_ROLES: [ROLE_JS_DEV, ROLE_DESIGNER],
25 },
29 },
26 'Tenno Seremel': {
30 'Tenno Seremel': {
27 'name': 'anonymous',
31 ATTR_NAME: 'anonymous',
28 'contacts': ['html@serenareem.net'],
32 ATTR_CONTACTS: ['html@serenareem.net'],
29 'roles': [ROLE_JS_DEV, ROLE_DESIGNER],
33 ATTR_ROLES: [ROLE_JS_DEV, ROLE_DESIGNER],
30 },
34 },
31 } No newline at end of file
35 }
@@ -1,30 +1,39
1 __author__ = 'neko259'
2
1 from boards import utils, settings
3 from boards import utils, settings
2 from boards.models import Post
4 from boards.models import Post
3 from boards.models.post import SETTING_MODERATE
5 from boards.models.post import SETTING_MODERATE
4
6
5 __author__ = 'neko259'
7 CONTEXT_SITE_NAME = 'site_name'
8 CONTEXT_VERSION = 'version'
9 CONTEXT_MODERATOR = 'moderator'
10 CONTEXT_THEME_CSS = 'theme_css'
11 CONTEXT_THEME = 'theme'
12 CONTEXT_PPD = 'posts_per_day'
13 CONTEXT_TAGS = 'tags'
14 CONTEXT_USER = 'user'
6
15
7
16
8 def user_and_ui_processor(request):
17 def user_and_ui_processor(request):
9 context = {}
18 context = {}
10
19
11 user = utils.get_user(request)
20 user = utils.get_user(request)
12 context['user'] = user
21 context[CONTEXT_USER] = user
13 context['tags'] = user.fav_tags.all()
22 context[CONTEXT_TAGS] = user.fav_tags.all()
14 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
23 context[CONTEXT_PPD] = float(Post.objects.get_posts_per_day())
15
24
16 theme = utils.get_theme(request, user)
25 theme = utils.get_theme(request, user)
17 context['theme'] = theme
26 context[CONTEXT_THEME] = theme
18 context['theme_css'] = 'css/' + theme + '/base_page.css'
27 context[CONTEXT_THEME_CSS] = 'css/' + theme + '/base_page.css'
19
28
20 # This shows the moderator panel
29 # This shows the moderator panel
21 moderate = user.get_setting(SETTING_MODERATE)
30 moderate = user.get_setting(SETTING_MODERATE)
22 if moderate == 'True':
31 if moderate == 'True':
23 context['moderator'] = user.is_moderator()
32 context[CONTEXT_MODERATOR] = user.is_moderator()
24 else:
33 else:
25 context['moderator'] = False
34 context[CONTEXT_MODERATOR] = False
26
35
27 context['version'] = settings.VERSION
36 context[CONTEXT_VERSION] = settings.VERSION
28 context['site_name'] = settings.SITE_NAME
37 context[CONTEXT_SITE_NAME] = settings.SITE_NAME
29
38
30 return context No newline at end of file
39 return context
@@ -1,355 +1,356
1 import re
1 import re
2 import time
2 import time
3 import hashlib
3 import hashlib
4
4
5 from captcha.fields import CaptchaField
5 from captcha.fields import CaptchaField
6 from django import forms
6 from django import forms
7 from django.forms.util import ErrorList
7 from django.forms.util import ErrorList
8 from django.utils.translation import ugettext_lazy as _
8 from django.utils.translation import ugettext_lazy as _
9
9
10 from boards.mdx_neboard import formatters
10 from boards.mdx_neboard import formatters
11 from boards.models.post import TITLE_MAX_LENGTH
11 from boards.models.post import TITLE_MAX_LENGTH
12 from boards.models import User, PostImage
12 from boards.models import User, PostImage
13 from neboard import settings
13 from neboard import settings
14 from boards import utils
14 from boards import utils
15 import boards.settings as board_settings
15 import boards.settings as board_settings
16
16
17 VETERAN_POSTING_DELAY = 5
17 VETERAN_POSTING_DELAY = 5
18
18
19 ATTRIBUTE_PLACEHOLDER = 'placeholder'
19 ATTRIBUTE_PLACEHOLDER = 'placeholder'
20
20
21 LAST_POST_TIME = 'last_post_time'
21 LAST_POST_TIME = 'last_post_time'
22 LAST_LOGIN_TIME = 'last_login_time'
22 LAST_LOGIN_TIME = 'last_login_time'
23 TEXT_PLACEHOLDER = _('''Type message here. You can reply to message >>123 like
23 TEXT_PLACEHOLDER = _('''Type message here. You can reply to message >>123 like
24 this. 2 new lines are required to start new paragraph.''')
24 this. 2 new lines are required to start new paragraph.''')
25 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
25 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
26
26
27 ERROR_IMAGE_DUPLICATE = _('Such image was already posted')
27 ERROR_IMAGE_DUPLICATE = _('Such image was already posted')
28
28
29 LABEL_TITLE = _('Title')
29 LABEL_TITLE = _('Title')
30 LABEL_TEXT = _('Text')
30 LABEL_TEXT = _('Text')
31 LABEL_TAG = _('Tag')
31 LABEL_TAG = _('Tag')
32 LABEL_SEARCH = _('Search')
32 LABEL_SEARCH = _('Search')
33
33
34 TAG_MAX_LENGTH = 20
34 TAG_MAX_LENGTH = 20
35
35
36 REGEX_TAG = ur'^[\w\d]+$'
36 REGEX_TAG = ur'^[\w\d]+$'
37
37
38
38
39 class FormatPanel(forms.Textarea):
39 class FormatPanel(forms.Textarea):
40 def render(self, name, value, attrs=None):
40 def render(self, name, value, attrs=None):
41 output = '<div id="mark-panel">'
41 output = '<div id="mark-panel">'
42 for formatter in formatters:
42 for formatter in formatters:
43 output += u'<span class="mark_btn"' + \
43 output += u'<span class="mark_btn"' + \
44 u' onClick="addMarkToMsg(\'' + formatter.format_left + \
44 u' onClick="addMarkToMsg(\'' + formatter.format_left + \
45 '\', \'' + formatter.format_right + '\')">' + \
45 '\', \'' + formatter.format_right + '\')">' + \
46 formatter.preview_left + formatter.name + \
46 formatter.preview_left + formatter.name + \
47 formatter.preview_right + u'</span>'
47 formatter.preview_right + u'</span>'
48
48
49 output += '</div>'
49 output += '</div>'
50 output += super(FormatPanel, self).render(name, value, attrs=None)
50 output += super(FormatPanel, self).render(name, value, attrs=None)
51
51
52 return output
52 return output
53
53
54
54
55 class PlainErrorList(ErrorList):
55 class PlainErrorList(ErrorList):
56 def __unicode__(self):
56 def __unicode__(self):
57 return self.as_text()
57 return self.as_text()
58
58
59 def as_text(self):
59 def as_text(self):
60 return ''.join([u'(!) %s ' % e for e in self])
60 return ''.join([u'(!) %s ' % e for e in self])
61
61
62
62
63 class NeboardForm(forms.Form):
63 class NeboardForm(forms.Form):
64
64
65 def as_div(self):
65 def as_div(self):
66 """
66 """
67 Returns this form rendered as HTML <as_div>s.
67 Returns this form rendered as HTML <as_div>s.
68 """
68 """
69
69
70 return self._html_output(
70 return self._html_output(
71 # TODO Do not show hidden rows in the list here
71 # TODO Do not show hidden rows in the list here
72 normal_row='<div class="form-row"><div class="form-label">'
72 normal_row='<div class="form-row"><div class="form-label">'
73 '%(label)s'
73 '%(label)s'
74 '</div></div>'
74 '</div></div>'
75 '<div class="form-row"><div class="form-input">'
75 '<div class="form-row"><div class="form-input">'
76 '%(field)s'
76 '%(field)s'
77 '</div></div>'
77 '</div></div>'
78 '<div class="form-row">'
78 '<div class="form-row">'
79 '%(help_text)s'
79 '%(help_text)s'
80 '</div>',
80 '</div>',
81 error_row='<div class="form-row">'
81 error_row='<div class="form-row">'
82 '<div class="form-label"></div>'
82 '<div class="form-label"></div>'
83 '<div class="form-errors">%s</div>'
83 '<div class="form-errors">%s</div>'
84 '</div>',
84 '</div>',
85 row_ender='</div>',
85 row_ender='</div>',
86 help_text_html='%s',
86 help_text_html='%s',
87 errors_on_separate_row=True)
87 errors_on_separate_row=True)
88
88
89 def as_json_errors(self):
89 def as_json_errors(self):
90 errors = []
90 errors = []
91
91
92 for name, field in self.fields.items():
92 for name, field in self.fields.items():
93 if self[name].errors:
93 if self[name].errors:
94 errors.append({
94 errors.append({
95 'field': name,
95 'field': name,
96 'errors': self[name].errors.as_text(),
96 'errors': self[name].errors.as_text(),
97 })
97 })
98
98
99 return errors
99 return errors
100
100
101
101
102 class PostForm(NeboardForm):
102 class PostForm(NeboardForm):
103
103
104 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
104 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
105 label=LABEL_TITLE)
105 label=LABEL_TITLE)
106 text = forms.CharField(
106 text = forms.CharField(
107 widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}),
107 widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}),
108 required=False, label=LABEL_TEXT)
108 required=False, label=LABEL_TEXT)
109 image = forms.ImageField(required=False, label=_('Image'),
109 image = forms.ImageField(required=False, label=_('Image'),
110 widget=forms.ClearableFileInput(attrs={'accept': 'image/*'}))
110 widget=forms.ClearableFileInput(
111 attrs={'accept': 'image/*'}))
111
112
112 # This field is for spam prevention only
113 # This field is for spam prevention only
113 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
114 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
114 widget=forms.TextInput(attrs={
115 widget=forms.TextInput(attrs={
115 'class': 'form-email'}))
116 'class': 'form-email'}))
116
117
117 session = None
118 session = None
118 need_to_ban = False
119 need_to_ban = False
119
120
120 def clean_title(self):
121 def clean_title(self):
121 title = self.cleaned_data['title']
122 title = self.cleaned_data['title']
122 if title:
123 if title:
123 if len(title) > TITLE_MAX_LENGTH:
124 if len(title) > TITLE_MAX_LENGTH:
124 raise forms.ValidationError(_('Title must have less than %s '
125 raise forms.ValidationError(_('Title must have less than %s '
125 'characters') %
126 'characters') %
126 str(TITLE_MAX_LENGTH))
127 str(TITLE_MAX_LENGTH))
127 return title
128 return title
128
129
129 def clean_text(self):
130 def clean_text(self):
130 text = self.cleaned_data['text'].strip()
131 text = self.cleaned_data['text'].strip()
131 if text:
132 if text:
132 if len(text) > board_settings.MAX_TEXT_LENGTH:
133 if len(text) > board_settings.MAX_TEXT_LENGTH:
133 raise forms.ValidationError(_('Text must have less than %s '
134 raise forms.ValidationError(_('Text must have less than %s '
134 'characters') %
135 'characters') %
135 str(board_settings
136 str(board_settings
136 .MAX_TEXT_LENGTH))
137 .MAX_TEXT_LENGTH))
137 return text
138 return text
138
139
139 def clean_image(self):
140 def clean_image(self):
140 image = self.cleaned_data['image']
141 image = self.cleaned_data['image']
141 if image:
142 if image:
142 if image._size > board_settings.MAX_IMAGE_SIZE:
143 if image.size > board_settings.MAX_IMAGE_SIZE:
143 raise forms.ValidationError(
144 raise forms.ValidationError(
144 _('Image must be less than %s bytes')
145 _('Image must be less than %s bytes')
145 % str(board_settings.MAX_IMAGE_SIZE))
146 % str(board_settings.MAX_IMAGE_SIZE))
146
147
147 md5 = hashlib.md5()
148 md5 = hashlib.md5()
148 for chunk in image.chunks():
149 for chunk in image.chunks():
149 md5.update(chunk)
150 md5.update(chunk)
150 image_hash = md5.hexdigest()
151 image_hash = md5.hexdigest()
151 if PostImage.objects.filter(hash=image_hash).exists():
152 if PostImage.objects.filter(hash=image_hash).exists():
152 raise forms.ValidationError(ERROR_IMAGE_DUPLICATE)
153 raise forms.ValidationError(ERROR_IMAGE_DUPLICATE)
153
154
154 return image
155 return image
155
156
156 def clean(self):
157 def clean(self):
157 cleaned_data = super(PostForm, self).clean()
158 cleaned_data = super(PostForm, self).clean()
158
159
159 if not self.session:
160 if not self.session:
160 raise forms.ValidationError('Humans have sessions')
161 raise forms.ValidationError('Humans have sessions')
161
162
162 if cleaned_data['email']:
163 if cleaned_data['email']:
163 self.need_to_ban = True
164 self.need_to_ban = True
164 raise forms.ValidationError('A human cannot enter a hidden field')
165 raise forms.ValidationError('A human cannot enter a hidden field')
165
166
166 if not self.errors:
167 if not self.errors:
167 self._clean_text_image()
168 self._clean_text_image()
168
169
169 if not self.errors and self.session:
170 if not self.errors and self.session:
170 self._validate_posting_speed()
171 self._validate_posting_speed()
171
172
172 return cleaned_data
173 return cleaned_data
173
174
174 def _clean_text_image(self):
175 def _clean_text_image(self):
175 text = self.cleaned_data.get('text')
176 text = self.cleaned_data.get('text')
176 image = self.cleaned_data.get('image')
177 image = self.cleaned_data.get('image')
177
178
178 if (not text) and (not image):
179 if (not text) and (not image):
179 error_message = _('Either text or image must be entered.')
180 error_message = _('Either text or image must be entered.')
180 self._errors['text'] = self.error_class([error_message])
181 self._errors['text'] = self.error_class([error_message])
181
182
182 def _validate_posting_speed(self):
183 def _validate_posting_speed(self):
183 can_post = True
184 can_post = True
184
185
185 # TODO Remove this, it's only for test
186 # TODO Remove this, it's only for test
186 if not 'user_id' in self.session:
187 if not 'user_id' in self.session:
187 return
188 return
188
189
189 user = User.objects.get(id=self.session['user_id'])
190 user = User.objects.get(id=self.session['user_id'])
190 if user.is_veteran():
191 if user.is_veteran():
191 posting_delay = VETERAN_POSTING_DELAY
192 posting_delay = VETERAN_POSTING_DELAY
192 else:
193 else:
193 posting_delay = settings.POSTING_DELAY
194 posting_delay = settings.POSTING_DELAY
194
195
195 if LAST_POST_TIME in self.session:
196 if LAST_POST_TIME in self.session:
196 now = time.time()
197 now = time.time()
197 last_post_time = self.session[LAST_POST_TIME]
198 last_post_time = self.session[LAST_POST_TIME]
198
199
199 current_delay = int(now - last_post_time)
200 current_delay = int(now - last_post_time)
200
201
201 if current_delay < posting_delay:
202 if current_delay < posting_delay:
202 error_message = _('Wait %s seconds after last posting') % str(
203 error_message = _('Wait %s seconds after last posting') % str(
203 posting_delay - current_delay)
204 posting_delay - current_delay)
204 self._errors['text'] = self.error_class([error_message])
205 self._errors['text'] = self.error_class([error_message])
205
206
206 can_post = False
207 can_post = False
207
208
208 if can_post:
209 if can_post:
209 self.session[LAST_POST_TIME] = time.time()
210 self.session[LAST_POST_TIME] = time.time()
210
211
211
212
212 class ThreadForm(PostForm):
213 class ThreadForm(PostForm):
213
214
214 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
215 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
215
216
216 tags = forms.CharField(
217 tags = forms.CharField(
217 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
218 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
218 max_length=100, label=_('Tags'), required=True)
219 max_length=100, label=_('Tags'), required=True)
219
220
220 def clean_tags(self):
221 def clean_tags(self):
221 tags = self.cleaned_data['tags'].strip()
222 tags = self.cleaned_data['tags'].strip()
222
223
223 if not tags or not self.regex_tags.match(tags):
224 if not tags or not self.regex_tags.match(tags):
224 raise forms.ValidationError(
225 raise forms.ValidationError(
225 _('Inappropriate characters in tags.'))
226 _('Inappropriate characters in tags.'))
226
227
227 return tags
228 return tags
228
229
229 def clean(self):
230 def clean(self):
230 cleaned_data = super(ThreadForm, self).clean()
231 cleaned_data = super(ThreadForm, self).clean()
231
232
232 return cleaned_data
233 return cleaned_data
233
234
234
235
235 class PostCaptchaForm(PostForm):
236 class PostCaptchaForm(PostForm):
236 captcha = CaptchaField()
237 captcha = CaptchaField()
237
238
238 def __init__(self, *args, **kwargs):
239 def __init__(self, *args, **kwargs):
239 self.request = kwargs['request']
240 self.request = kwargs['request']
240 del kwargs['request']
241 del kwargs['request']
241
242
242 super(PostCaptchaForm, self).__init__(*args, **kwargs)
243 super(PostCaptchaForm, self).__init__(*args, **kwargs)
243
244
244 def clean(self):
245 def clean(self):
245 cleaned_data = super(PostCaptchaForm, self).clean()
246 cleaned_data = super(PostCaptchaForm, self).clean()
246
247
247 success = self.is_valid()
248 success = self.is_valid()
248 utils.update_captcha_access(self.request, success)
249 utils.update_captcha_access(self.request, success)
249
250
250 if success:
251 if success:
251 return cleaned_data
252 return cleaned_data
252 else:
253 else:
253 raise forms.ValidationError(_("Captcha validation failed"))
254 raise forms.ValidationError(_("Captcha validation failed"))
254
255
255
256
256 class ThreadCaptchaForm(ThreadForm):
257 class ThreadCaptchaForm(ThreadForm):
257 captcha = CaptchaField()
258 captcha = CaptchaField()
258
259
259 def __init__(self, *args, **kwargs):
260 def __init__(self, *args, **kwargs):
260 self.request = kwargs['request']
261 self.request = kwargs['request']
261 del kwargs['request']
262 del kwargs['request']
262
263
263 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
264 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
264
265
265 def clean(self):
266 def clean(self):
266 cleaned_data = super(ThreadCaptchaForm, self).clean()
267 cleaned_data = super(ThreadCaptchaForm, self).clean()
267
268
268 success = self.is_valid()
269 success = self.is_valid()
269 utils.update_captcha_access(self.request, success)
270 utils.update_captcha_access(self.request, success)
270
271
271 if success:
272 if success:
272 return cleaned_data
273 return cleaned_data
273 else:
274 else:
274 raise forms.ValidationError(_("Captcha validation failed"))
275 raise forms.ValidationError(_("Captcha validation failed"))
275
276
276
277
277 class SettingsForm(NeboardForm):
278 class SettingsForm(NeboardForm):
278
279
279 theme = forms.ChoiceField(choices=settings.THEMES,
280 theme = forms.ChoiceField(choices=settings.THEMES,
280 label=_('Theme'))
281 label=_('Theme'))
281
282
282
283
283 class ModeratorSettingsForm(SettingsForm):
284 class ModeratorSettingsForm(SettingsForm):
284
285
285 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
286 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
286 'panel'))
287 'panel'))
287
288
288
289
289 class LoginForm(NeboardForm):
290 class LoginForm(NeboardForm):
290
291
291 user_id = forms.CharField()
292 user_id = forms.CharField()
292
293
293 session = None
294 session = None
294
295
295 def clean_user_id(self):
296 def clean_user_id(self):
296 user_id = self.cleaned_data['user_id']
297 user_id = self.cleaned_data['user_id']
297 if user_id:
298 if user_id:
298 users = User.objects.filter(user_id=user_id)
299 users = User.objects.filter(user_id=user_id)
299 if len(users) == 0:
300 if len(users) == 0:
300 raise forms.ValidationError(_('No such user found'))
301 raise forms.ValidationError(_('No such user found'))
301
302
302 return user_id
303 return user_id
303
304
304 def _validate_login_speed(self):
305 def _validate_login_speed(self):
305 can_post = True
306 can_post = True
306
307
307 if LAST_LOGIN_TIME in self.session:
308 if LAST_LOGIN_TIME in self.session:
308 now = time.time()
309 now = time.time()
309 last_login_time = self.session[LAST_LOGIN_TIME]
310 last_login_time = self.session[LAST_LOGIN_TIME]
310
311
311 current_delay = int(now - last_login_time)
312 current_delay = int(now - last_login_time)
312
313
313 if current_delay < board_settings.LOGIN_TIMEOUT:
314 if current_delay < board_settings.LOGIN_TIMEOUT:
314 error_message = _('Wait %s minutes after last login') % str(
315 error_message = _('Wait %s minutes after last login') % str(
315 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
316 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
316 self._errors['user_id'] = self.error_class([error_message])
317 self._errors['user_id'] = self.error_class([error_message])
317
318
318 can_post = False
319 can_post = False
319
320
320 if can_post:
321 if can_post:
321 self.session[LAST_LOGIN_TIME] = time.time()
322 self.session[LAST_LOGIN_TIME] = time.time()
322
323
323 def clean(self):
324 def clean(self):
324 if not self.session:
325 if not self.session:
325 raise forms.ValidationError('Humans have sessions')
326 raise forms.ValidationError('Humans have sessions')
326
327
327 self._validate_login_speed()
328 self._validate_login_speed()
328
329
329 cleaned_data = super(LoginForm, self).clean()
330 cleaned_data = super(LoginForm, self).clean()
330
331
331 return cleaned_data
332 return cleaned_data
332
333
333
334
334 class AddTagForm(NeboardForm):
335 class AddTagForm(NeboardForm):
335
336
336 tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG)
337 tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG)
337 method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag')
338 method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag')
338
339
339 def clean_tag(self):
340 def clean_tag(self):
340 tag = self.cleaned_data['tag']
341 tag = self.cleaned_data['tag']
341
342
342 regex_tag = re.compile(REGEX_TAG, re.UNICODE)
343 regex_tag = re.compile(REGEX_TAG, re.UNICODE)
343 if not regex_tag.match(tag):
344 if not regex_tag.match(tag):
344 raise forms.ValidationError(_('Inappropriate characters in tags.'))
345 raise forms.ValidationError(_('Inappropriate characters in tags.'))
345
346
346 return tag
347 return tag
347
348
348 def clean(self):
349 def clean(self):
349 cleaned_data = super(AddTagForm, self).clean()
350 cleaned_data = super(AddTagForm, self).clean()
350
351
351 return cleaned_data
352 return cleaned_data
352
353
353
354
354 class SearchForm(NeboardForm):
355 class SearchForm(NeboardForm):
355 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) No newline at end of file
356 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
@@ -1,202 +1,207
1 # coding=utf-8
1 # coding=utf-8
2
2
3 import markdown
3 import markdown
4 from markdown.inlinepatterns import Pattern, SubstituteTagPattern
4 from markdown.inlinepatterns import Pattern
5 from markdown.util import etree
5 from markdown.util import etree
6
6 import boards
7 import boards
7
8
9
8 __author__ = 'neko259'
10 __author__ = 'neko259'
9
11
10
12
11 AUTOLINK_PATTERN = r'(https?://\S+)'
13 AUTOLINK_PATTERN = r'(https?://\S+)'
12 QUOTE_PATTERN = r'^(?<!>)(>[^>].*)$'
14 QUOTE_PATTERN = r'^(?<!>)(>[^>].*)$'
13 REFLINK_PATTERN = r'((>>)(\d+))'
15 REFLINK_PATTERN = r'((>>)(\d+))'
14 SPOILER_PATTERN = r'%%([^(%%)]+)%%'
16 SPOILER_PATTERN = r'%%([^(%%)]+)%%'
15 COMMENT_PATTERN = r'^(//(.+))'
17 COMMENT_PATTERN = r'^(//(.+))'
16 STRIKETHROUGH_PATTERN = r'~(.+)~'
18 STRIKETHROUGH_PATTERN = r'~(.+)~'
17 DASH_PATTERN = r'--'
19 DASH_PATTERN = r'--'
18
20
19
21
20 class TextFormatter():
22 class TextFormatter():
21 """
23 """
22 An interface for formatter that can be used in the text format panel
24 An interface for formatter that can be used in the text format panel
23 """
25 """
24
26
27 def __init__(self):
28 pass
29
25 name = ''
30 name = ''
26
31
27 # Left and right tags for the button preview
32 # Left and right tags for the button preview
28 preview_left = ''
33 preview_left = ''
29 preview_right = ''
34 preview_right = ''
30
35
31 # Left and right characters for the textarea input
36 # Left and right characters for the textarea input
32 format_left = ''
37 format_left = ''
33 format_right = ''
38 format_right = ''
34
39
35
40
36 class AutolinkPattern(Pattern):
41 class AutolinkPattern(Pattern):
37 def handleMatch(self, m):
42 def handleMatch(self, m):
38 link_element = etree.Element('a')
43 link_element = etree.Element('a')
39 href = m.group(2)
44 href = m.group(2)
40 link_element.set('href', href)
45 link_element.set('href', href)
41 link_element.text = href
46 link_element.text = href
42
47
43 return link_element
48 return link_element
44
49
45
50
46 class QuotePattern(Pattern, TextFormatter):
51 class QuotePattern(Pattern, TextFormatter):
47 name = ''
52 name = ''
48 preview_left = '<span class="quote">&gt; '
53 preview_left = '<span class="quote">&gt; '
49 preview_right = '</span>'
54 preview_right = '</span>'
50
55
51 format_left = '&gt;'
56 format_left = '&gt;'
52
57
53 def handleMatch(self, m):
58 def handleMatch(self, m):
54 quote_element = etree.Element('span')
59 quote_element = etree.Element('span')
55 quote_element.set('class', 'quote')
60 quote_element.set('class', 'quote')
56 quote_element.text = m.group(2)
61 quote_element.text = m.group(2)
57
62
58 return quote_element
63 return quote_element
59
64
60
65
61 class ReflinkPattern(Pattern):
66 class ReflinkPattern(Pattern):
62 def handleMatch(self, m):
67 def handleMatch(self, m):
63 post_id = m.group(4)
68 post_id = m.group(4)
64
69
65 posts = boards.models.Post.objects.filter(id=post_id)
70 posts = boards.models.Post.objects.filter(id=post_id)
66 if posts.count() > 0:
71 if posts.count() > 0:
67 ref_element = etree.Element('a')
72 ref_element = etree.Element('a')
68
73
69 post = posts[0]
74 post = posts[0]
70
75
71 ref_element.set('href', post.get_url())
76 ref_element.set('href', post.get_url())
72 ref_element.text = m.group(2)
77 ref_element.text = m.group(2)
73
78
74 return ref_element
79 return ref_element
75
80
76
81
77 class SpoilerPattern(Pattern, TextFormatter):
82 class SpoilerPattern(Pattern, TextFormatter):
78 name = 's'
83 name = 's'
79 preview_left = '<span class="spoiler">'
84 preview_left = '<span class="spoiler">'
80 preview_right = '</span>'
85 preview_right = '</span>'
81
86
82 format_left = '%%'
87 format_left = '%%'
83 format_right = '%%'
88 format_right = '%%'
84
89
85 def handleMatch(self, m):
90 def handleMatch(self, m):
86 quote_element = etree.Element('span')
91 quote_element = etree.Element('span')
87 quote_element.set('class', 'spoiler')
92 quote_element.set('class', 'spoiler')
88 quote_element.text = m.group(2)
93 quote_element.text = m.group(2)
89
94
90 return quote_element
95 return quote_element
91
96
92
97
93 class CommentPattern(Pattern, TextFormatter):
98 class CommentPattern(Pattern, TextFormatter):
94 name = ''
99 name = ''
95 preview_left = '<span class="comment">// '
100 preview_left = '<span class="comment">// '
96 preview_right = '</span>'
101 preview_right = '</span>'
97
102
98 format_left = '//'
103 format_left = '//'
99
104
100 def handleMatch(self, m):
105 def handleMatch(self, m):
101 quote_element = etree.Element('span')
106 quote_element = etree.Element('span')
102 quote_element.set('class', 'comment')
107 quote_element.set('class', 'comment')
103 quote_element.text = '//' + m.group(3)
108 quote_element.text = '//' + m.group(3)
104
109
105 return quote_element
110 return quote_element
106
111
107
112
108 class StrikeThroughPattern(Pattern, TextFormatter):
113 class StrikeThroughPattern(Pattern, TextFormatter):
109 name = 's'
114 name = 's'
110 preview_left = '<span class="strikethrough">'
115 preview_left = '<span class="strikethrough">'
111 preview_right = '</span>'
116 preview_right = '</span>'
112
117
113 format_left = '~'
118 format_left = '~'
114 format_right = '~'
119 format_right = '~'
115
120
116 def handleMatch(self, m):
121 def handleMatch(self, m):
117 quote_element = etree.Element('span')
122 quote_element = etree.Element('span')
118 quote_element.set('class', 'strikethrough')
123 quote_element.set('class', 'strikethrough')
119 quote_element.text = m.group(2)
124 quote_element.text = m.group(2)
120
125
121 return quote_element
126 return quote_element
122
127
123
128
124 class ItalicPattern(TextFormatter):
129 class ItalicPattern(TextFormatter):
125 name = 'i'
130 name = 'i'
126 preview_left = '<i>'
131 preview_left = '<i>'
127 preview_right = '</i>'
132 preview_right = '</i>'
128
133
129 format_left = '_'
134 format_left = '_'
130 format_right = '_'
135 format_right = '_'
131
136
132
137
133 class BoldPattern(TextFormatter):
138 class BoldPattern(TextFormatter):
134 name = 'b'
139 name = 'b'
135 preview_left = '<b>'
140 preview_left = '<b>'
136 preview_right = '</b>'
141 preview_right = '</b>'
137
142
138 format_left = '__'
143 format_left = '__'
139 format_right = '__'
144 format_right = '__'
140
145
141
146
142 class CodePattern(TextFormatter):
147 class CodePattern(TextFormatter):
143 name = 'code'
148 name = 'code'
144 preview_left = '<code>'
149 preview_left = '<code>'
145 preview_right = '</code>'
150 preview_right = '</code>'
146
151
147 format_left = ' '
152 format_left = ' '
148
153
149
154
150 class DashPattern(Pattern):
155 class DashPattern(Pattern):
151 def handleMatch(self, m):
156 def handleMatch(self, m):
152 return u'—'
157 return u'—'
153
158
154
159
155 class NeboardMarkdown(markdown.Extension):
160 class NeboardMarkdown(markdown.Extension):
156 def extendMarkdown(self, md, md_globals):
161 def extendMarkdown(self, md, md_globals):
157 self._add_neboard_patterns(md)
162 self._add_neboard_patterns(md)
158 self._delete_patterns(md)
163 self._delete_patterns(md)
159
164
160 def _delete_patterns(self, md):
165 def _delete_patterns(self, md):
161 del md.parser.blockprocessors['quote']
166 del md.parser.blockprocessors['quote']
162
167
163 del md.inlinePatterns['image_link']
168 del md.inlinePatterns['image_link']
164 del md.inlinePatterns['image_reference']
169 del md.inlinePatterns['image_reference']
165
170
166 def _add_neboard_patterns(self, md):
171 def _add_neboard_patterns(self, md):
167 autolink = AutolinkPattern(AUTOLINK_PATTERN, md)
172 autolink = AutolinkPattern(AUTOLINK_PATTERN, md)
168 quote = QuotePattern(QUOTE_PATTERN, md)
173 quote = QuotePattern(QUOTE_PATTERN, md)
169 reflink = ReflinkPattern(REFLINK_PATTERN, md)
174 reflink = ReflinkPattern(REFLINK_PATTERN, md)
170 spoiler = SpoilerPattern(SPOILER_PATTERN, md)
175 spoiler = SpoilerPattern(SPOILER_PATTERN, md)
171 comment = CommentPattern(COMMENT_PATTERN, md)
176 comment = CommentPattern(COMMENT_PATTERN, md)
172 strikethrough = StrikeThroughPattern(STRIKETHROUGH_PATTERN, md)
177 strikethrough = StrikeThroughPattern(STRIKETHROUGH_PATTERN, md)
173 dash = DashPattern(DASH_PATTERN, md)
178 dash = DashPattern(DASH_PATTERN, md)
174
179
175 md.inlinePatterns[u'autolink_ext'] = autolink
180 md.inlinePatterns[u'autolink_ext'] = autolink
176 md.inlinePatterns[u'spoiler'] = spoiler
181 md.inlinePatterns[u'spoiler'] = spoiler
177 md.inlinePatterns[u'strikethrough'] = strikethrough
182 md.inlinePatterns[u'strikethrough'] = strikethrough
178 md.inlinePatterns[u'comment'] = comment
183 md.inlinePatterns[u'comment'] = comment
179 md.inlinePatterns[u'reflink'] = reflink
184 md.inlinePatterns[u'reflink'] = reflink
180 md.inlinePatterns[u'quote'] = quote
185 md.inlinePatterns[u'quote'] = quote
181 md.inlinePatterns[u'dash'] = dash
186 md.inlinePatterns[u'dash'] = dash
182
187
183
188
184 def make_extension(configs=None):
189 def make_extension(configs=None):
185 return NeboardMarkdown(configs=configs)
190 return NeboardMarkdown(configs=configs)
186
191
187 neboard_extension = make_extension()
192 neboard_extension = make_extension()
188
193
189
194
190 def markdown_extended(markup):
195 def markdown_extended(markup):
191 return markdown.markdown(markup, [neboard_extension, 'nl2br'],
196 return markdown.markdown(markup, [neboard_extension, 'nl2br'],
192 safe_mode='escape')
197 safe_mode='escape')
193
198
194 formatters = [
199 formatters = [
195 QuotePattern,
200 QuotePattern,
196 SpoilerPattern,
201 SpoilerPattern,
197 ItalicPattern,
202 ItalicPattern,
198 BoldPattern,
203 BoldPattern,
199 CommentPattern,
204 CommentPattern,
200 StrikeThroughPattern,
205 StrikeThroughPattern,
201 CodePattern,
206 CodePattern,
202 ]
207 ]
@@ -1,42 +1,46
1 from django.shortcuts import redirect
1 from django.shortcuts import redirect
2 from boards import utils
2 from boards import utils
3 from boards.models import Ban
3 from boards.models import Ban
4 from django.utils.html import strip_spaces_between_tags
4 from django.utils.html import strip_spaces_between_tags
5 from django.conf import settings
5 from django.conf import settings
6 from boards.views.banned import BannedView
6 from boards.views.banned import BannedView
7
7
8 RESPONSE_CONTENT_TYPE = 'Content-Type'
8 RESPONSE_CONTENT_TYPE = 'Content-Type'
9
9
10 TYPE_HTML = 'text/html'
10 TYPE_HTML = 'text/html'
11
11
12
12
13 class BanMiddleware:
13 class BanMiddleware:
14 """
14 """
15 This is run before showing the thread. Banned users don't need to see
15 This is run before showing the thread. Banned users don't need to see
16 anything
16 anything
17 """
17 """
18
18
19 def __init__(self):
20 pass
21
19 def process_view(self, request, view_func, view_args, view_kwargs):
22 def process_view(self, request, view_func, view_args, view_kwargs):
20
23
21 if view_func != BannedView.as_view:
24 if view_func != BannedView.as_view:
22 ip = utils.get_client_ip(request)
25 ip = utils.get_client_ip(request)
23 bans = Ban.objects.filter(ip=ip)
26 bans = Ban.objects.filter(ip=ip)
24
27
25 if bans.exists():
28 if bans.exists():
26 ban = bans[0]
29 ban = bans[0]
27 if not ban.can_read:
30 if not ban.can_read:
28 return redirect('banned')
31 return redirect('banned')
29
32
30
33
31 class MinifyHTMLMiddleware(object):
34 class MinifyHTMLMiddleware(object):
32 def process_response(self, request, response):
35 def process_response(self, request, response):
33 try:
36 try:
34 compress_html = settings.COMPRESS_HTML
37 compress_html = settings.COMPRESS_HTML
35 except AttributeError:
38 except AttributeError:
36 compress_html = False
39 compress_html = False
37
40
38 if RESPONSE_CONTENT_TYPE in response\
41 if RESPONSE_CONTENT_TYPE in response\
39 and TYPE_HTML in response[RESPONSE_CONTENT_TYPE] and compress_html:
42 and TYPE_HTML in response[RESPONSE_CONTENT_TYPE]\
43 and compress_html:
40 response.content = strip_spaces_between_tags(
44 response.content = strip_spaces_between_tags(
41 response.content.strip())
45 response.content.strip())
42 return response No newline at end of file
46 return response
@@ -1,23 +1,26
1 import sys
1 import sys
2 from cStringIO import StringIO
2 from cStringIO import StringIO
3 from django.conf import settings
3 from django.conf import settings
4 import line_profiler
4 import line_profiler
5
5
6
6
7 class ProfilerMiddleware(object):
7 class ProfilerMiddleware():
8 def __init__(self):
9 self.profiler = None
10
8 def process_view(self, request, callback, callback_args, callback_kwargs):
11 def process_view(self, request, callback, callback_args, callback_kwargs):
9 if settings.DEBUG and 'prof' in request.GET:
12 if settings.DEBUG and 'prof' in request.GET:
10 self.profiler = line_profiler.LineProfiler()
13 self.profiler = line_profiler.LineProfiler()
11 self.profiler.add_function(callback)
14 self.profiler.add_function(callback)
12 self.profiler.enable()
15 self.profiler.enable()
13 args = (request,) + callback_args
16 args = (request,) + callback_args
14 return callback(*args, **callback_kwargs)
17 return callback(*args, **callback_kwargs)
15
18
16 def process_response(self, request, response):
19 def process_response(self, request, response):
17 if settings.DEBUG and 'prof' in request.GET:
20 if settings.DEBUG and 'prof' in request.GET:
18 out = StringIO()
21 out = StringIO()
19 old_stdout, sys.stdout = sys.stdout, out
22 old_stdout, sys.stdout = sys.stdout, out
20 self.profiler.print_stats()
23 self.profiler.print_stats()
21 sys.stdout = old_stdout
24 sys.stdout = old_stdout
22 response.content = '<pre>%s</pre>' % out.getvalue()
25 response.content = '<pre>%s</pre>' % out.getvalue()
23 return response
26 return response
@@ -1,33 +1,41
1 from django.shortcuts import render
1 from django.shortcuts import render
2 from django.template import RequestContext
2 from django.template import RequestContext
3 from django.views.generic import View
3 from django.views.generic import View
4 from haystack.query import SearchQuerySet
4 from haystack.query import SearchQuerySet
5 from boards.abstracts.paginator import get_paginator
5 from boards.abstracts.paginator import get_paginator
6 from boards.forms import SearchForm, PlainErrorList
6 from boards.forms import SearchForm, PlainErrorList
7
7
8 FORM_QUERY = 'query'
9
10 CONTEXT_QUERY = 'query'
11 CONTEXT_FORM = 'form'
12 CONTEXT_PAGE = 'page'
13
14 REQUEST_PAGE = 'page'
15
8 __author__ = 'neko259'
16 __author__ = 'neko259'
9
17
10 TEMPLATE = 'search/search.html'
18 TEMPLATE = 'search/search.html'
11
19
12
20
13 class BoardSearchView(View):
21 class BoardSearchView(View):
14 def get(self, request):
22 def get(self, request):
15 context = RequestContext(request)
23 context = RequestContext(request)
16 form = SearchForm(request.GET, error_class=PlainErrorList)
24 form = SearchForm(request.GET, error_class=PlainErrorList)
17 context['form'] = form
25 context[CONTEXT_FORM] = form
18
26
19 if form.is_valid():
27 if form.is_valid():
20 query = form.cleaned_data['query']
28 query = form.cleaned_data[FORM_QUERY]
21 if len(query) >= 3:
29 if len(query) >= 3:
22 results = SearchQuerySet().auto_query(query).order_by('-id') \
30 results = SearchQuerySet().auto_query(query).order_by('-id') \
23 .highlight()
31 .highlight()
24 paginator = get_paginator(results, 10)
32 paginator = get_paginator(results, 10)
25
33
26 if 'page' in request.GET:
34 if REQUEST_PAGE in request.GET:
27 page = int(request.GET['page'])
35 page = int(request.GET[REQUEST_PAGE])
28 else:
36 else:
29 page = 1
37 page = 1
30 context['page'] = paginator.page(page)
38 context[CONTEXT_PAGE] = paginator.page(page)
31 context['query'] = query
39 context[CONTEXT_QUERY] = query
32
40
33 return render(request, TEMPLATE, context) No newline at end of file
41 return render(request, TEMPLATE, context)
@@ -1,9 +1,10
1 line_profiler
1 haystack
2 haystack
2 pillow
3 pillow
3 django>=1.6
4 django>=1.6
4 django_cleanup
5 django_cleanup
5 django-markupfield
6 django-markupfield
6 markdown
7 markdown
7 python-markdown
8 python-markdown
8 django-simple-captcha
9 django-simple-captcha
9 line-profiler
10 line-profiler
General Comments 0
You need to be logged in to leave comments. Login now