Show More
@@ -1,6 +1,5 b'' | |||||
1 | import re |
|
1 | import re | |
2 | import time |
|
2 | import time | |
3 | import hashlib |
|
|||
4 |
|
3 | |||
5 | from django import forms |
|
4 | from django import forms | |
6 | from django.forms.util import ErrorList |
|
5 | from django.forms.util import ErrorList | |
@@ -10,16 +9,17 b' from boards.mdx_neboard import formatter' | |||||
10 | from boards.models.post import TITLE_MAX_LENGTH |
|
9 | from boards.models.post import TITLE_MAX_LENGTH | |
11 | from boards.models import PostImage, Tag |
|
10 | from boards.models import PostImage, Tag | |
12 | from neboard import settings |
|
11 | from neboard import settings | |
13 | from boards import utils |
|
|||
14 | import boards.settings as board_settings |
|
12 | import boards.settings as board_settings | |
15 |
|
13 | |||
|
14 | REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE) | |||
|
15 | ||||
16 | VETERAN_POSTING_DELAY = 5 |
|
16 | VETERAN_POSTING_DELAY = 5 | |
17 |
|
17 | |||
18 | ATTRIBUTE_PLACEHOLDER = 'placeholder' |
|
18 | ATTRIBUTE_PLACEHOLDER = 'placeholder' | |
19 |
|
19 | |||
20 | LAST_POST_TIME = 'last_post_time' |
|
20 | LAST_POST_TIME = 'last_post_time' | |
21 | LAST_LOGIN_TIME = 'last_login_time' |
|
21 | LAST_LOGIN_TIME = 'last_login_time' | |
22 |
TEXT_PLACEHOLDER = _(' |
|
22 | TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.') | |
23 | TAGS_PLACEHOLDER = _('tag1 several_words_tag') |
|
23 | TAGS_PLACEHOLDER = _('tag1 several_words_tag') | |
24 |
|
24 | |||
25 | ERROR_IMAGE_DUPLICATE = _('Such image was already posted') |
|
25 | ERROR_IMAGE_DUPLICATE = _('Such image was already posted') | |
@@ -31,10 +31,13 b" LABEL_SEARCH = _('Search')" | |||||
31 |
|
31 | |||
32 | TAG_MAX_LENGTH = 20 |
|
32 | TAG_MAX_LENGTH = 20 | |
33 |
|
33 | |||
34 | REGEX_TAG = r'^[\w\d]+$' |
|
|||
35 |
|
||||
36 |
|
34 | |||
37 | class FormatPanel(forms.Textarea): |
|
35 | class FormatPanel(forms.Textarea): | |
|
36 | """ | |||
|
37 | Panel for text formatting. Consists of buttons to add different tags to the | |||
|
38 | form text area. | |||
|
39 | """ | |||
|
40 | ||||
38 | def render(self, name, value, attrs=None): |
|
41 | def render(self, name, value, attrs=None): | |
39 | output = '<div id="mark-panel">' |
|
42 | output = '<div id="mark-panel">' | |
40 | for formatter in formatters: |
|
43 | for formatter in formatters: | |
@@ -59,6 +62,9 b' class PlainErrorList(ErrorList):' | |||||
59 |
|
62 | |||
60 |
|
63 | |||
61 | class NeboardForm(forms.Form): |
|
64 | class NeboardForm(forms.Form): | |
|
65 | """ | |||
|
66 | Form with neboard-specific formatting. | |||
|
67 | """ | |||
62 |
|
68 | |||
63 | def as_div(self): |
|
69 | def as_div(self): | |
64 | """ |
|
70 | """ | |
@@ -143,10 +149,7 b' class PostForm(NeboardForm):' | |||||
143 | _('Image must be less than %s bytes') |
|
149 | _('Image must be less than %s bytes') | |
144 | % str(board_settings.MAX_IMAGE_SIZE)) |
|
150 | % str(board_settings.MAX_IMAGE_SIZE)) | |
145 |
|
151 | |||
146 | md5 = hashlib.md5() |
|
152 | image_hash = PostImage.get_hash(image) | |
147 | for chunk in image.chunks(): |
|
|||
148 | md5.update(chunk) |
|
|||
149 | image_hash = md5.hexdigest() |
|
|||
150 | if PostImage.objects.filter(hash=image_hash).exists(): |
|
153 | if PostImage.objects.filter(hash=image_hash).exists(): | |
151 | raise forms.ValidationError(ERROR_IMAGE_DUPLICATE) |
|
154 | raise forms.ValidationError(ERROR_IMAGE_DUPLICATE) | |
152 |
|
155 | |||
@@ -203,8 +206,6 b' class PostForm(NeboardForm):' | |||||
203 |
|
206 | |||
204 | class ThreadForm(PostForm): |
|
207 | class ThreadForm(PostForm): | |
205 |
|
208 | |||
206 | regex_tags = re.compile(r'^[\w\s\d]+$', re.UNICODE) |
|
|||
207 |
|
||||
208 | tags = forms.CharField( |
|
209 | tags = forms.CharField( | |
209 | widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}), |
|
210 | widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}), | |
210 | max_length=100, label=_('Tags'), required=True) |
|
211 | max_length=100, label=_('Tags'), required=True) | |
@@ -212,17 +213,17 b' class ThreadForm(PostForm):' | |||||
212 | def clean_tags(self): |
|
213 | def clean_tags(self): | |
213 | tags = self.cleaned_data['tags'].strip() |
|
214 | tags = self.cleaned_data['tags'].strip() | |
214 |
|
215 | |||
215 |
if not tags or not |
|
216 | if not tags or not REGEX_TAGS.match(tags): | |
216 | raise forms.ValidationError( |
|
217 | raise forms.ValidationError( | |
217 | _('Inappropriate characters in tags.')) |
|
218 | _('Inappropriate characters in tags.')) | |
218 |
|
219 | |||
219 | tag_models = [] |
|
|||
220 | required_tag_exists = False |
|
220 | required_tag_exists = False | |
221 | for tag in tags.split(): |
|
221 | for tag in tags.split(): | |
222 | tag_model = Tag.objects.filter(name=tag.strip().lower(), |
|
222 | tag_model = Tag.objects.filter(name=tag.strip().lower(), | |
223 | required=True) |
|
223 | required=True) | |
224 | if tag_model.exists(): |
|
224 | if tag_model.exists(): | |
225 | required_tag_exists = True |
|
225 | required_tag_exists = True | |
|
226 | break | |||
226 |
|
227 | |||
227 | if not required_tag_exists: |
|
228 | if not required_tag_exists: | |
228 | raise forms.ValidationError(_('Need at least 1 required tag.')) |
|
229 | raise forms.ValidationError(_('Need at least 1 required tag.')) | |
@@ -241,65 +242,5 b' class SettingsForm(NeboardForm):' | |||||
241 | label=_('Theme')) |
|
242 | label=_('Theme')) | |
242 |
|
243 | |||
243 |
|
244 | |||
244 | class AddTagForm(NeboardForm): |
|
|||
245 |
|
||||
246 | tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG) |
|
|||
247 | method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag') |
|
|||
248 |
|
||||
249 | def clean_tag(self): |
|
|||
250 | tag = self.cleaned_data['tag'] |
|
|||
251 |
|
||||
252 | regex_tag = re.compile(REGEX_TAG, re.UNICODE) |
|
|||
253 | if not regex_tag.match(tag): |
|
|||
254 | raise forms.ValidationError(_('Inappropriate characters in tags.')) |
|
|||
255 |
|
||||
256 | return tag |
|
|||
257 |
|
||||
258 | def clean(self): |
|
|||
259 | cleaned_data = super(AddTagForm, self).clean() |
|
|||
260 |
|
||||
261 | return cleaned_data |
|
|||
262 |
|
||||
263 |
|
||||
264 | class SearchForm(NeboardForm): |
|
245 | class SearchForm(NeboardForm): | |
265 | query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) |
|
246 | query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) | |
266 |
|
||||
267 |
|
||||
268 | class LoginForm(NeboardForm): |
|
|||
269 |
|
||||
270 | password = forms.CharField() |
|
|||
271 |
|
||||
272 | session = None |
|
|||
273 |
|
||||
274 | def clean_password(self): |
|
|||
275 | password = self.cleaned_data['password'] |
|
|||
276 | if board_settings.MASTER_PASSWORD != password: |
|
|||
277 | raise forms.ValidationError(_('Invalid master password')) |
|
|||
278 |
|
||||
279 | return password |
|
|||
280 |
|
||||
281 | def _validate_login_speed(self): |
|
|||
282 | can_post = True |
|
|||
283 |
|
||||
284 | if LAST_LOGIN_TIME in self.session: |
|
|||
285 | now = time.time() |
|
|||
286 | last_login_time = self.session[LAST_LOGIN_TIME] |
|
|||
287 |
|
||||
288 | current_delay = int(now - last_login_time) |
|
|||
289 |
|
||||
290 | if current_delay < board_settings.LOGIN_TIMEOUT: |
|
|||
291 | error_message = _('Wait %s minutes after last login') % str( |
|
|||
292 | (board_settings.LOGIN_TIMEOUT - current_delay) / 60) |
|
|||
293 | self._errors['password'] = self.error_class([error_message]) |
|
|||
294 |
|
||||
295 | can_post = False |
|
|||
296 |
|
||||
297 | if can_post: |
|
|||
298 | self.session[LAST_LOGIN_TIME] = time.time() |
|
|||
299 |
|
||||
300 | def clean(self): |
|
|||
301 | self._validate_login_speed() |
|
|||
302 |
|
||||
303 | cleaned_data = super(LoginForm, self).clean() |
|
|||
304 |
|
||||
305 | return cleaned_data |
|
@@ -56,10 +56,7 b' class PostImage(models.Model, Viewable):' | |||||
56 | """ |
|
56 | """ | |
57 |
|
57 | |||
58 | if not self.pk and self.image: |
|
58 | if not self.pk and self.image: | |
59 | md5 = hashlib.md5() |
|
59 | self.hash = PostImage.get_hash(self.image) | |
60 | for chunk in self.image.chunks(): |
|
|||
61 | md5.update(chunk) |
|
|||
62 | self.hash = md5.hexdigest() |
|
|||
63 | super(PostImage, self).save(*args, **kwargs) |
|
60 | super(PostImage, self).save(*args, **kwargs) | |
64 |
|
61 | |||
65 | def __str__(self): |
|
62 | def __str__(self): | |
@@ -81,3 +78,13 b' class PostImage(models.Model, Viewable):' | |||||
81 | self.image.url_200x150, |
|
78 | self.image.url_200x150, | |
82 | str(self.hash), str(self.pre_width), |
|
79 | str(self.hash), str(self.pre_width), | |
83 | str(self.pre_height), str(self.width), str(self.height)) |
|
80 | str(self.pre_height), str(self.width), str(self.height)) | |
|
81 | ||||
|
82 | @staticmethod | |||
|
83 | def get_hash(image): | |||
|
84 | """ | |||
|
85 | Gets hash of an image. | |||
|
86 | """ | |||
|
87 | md5 = hashlib.md5() | |||
|
88 | for chunk in image.chunks(): | |||
|
89 | md5.update(chunk) | |||
|
90 | return md5.hexdigest() |
General Comments 0
You need to be logged in to leave comments.
Login now