##// END OF EJS Templates
Fixed limiting posting speed
neko259 -
r832:e351426f default
parent child Browse files
Show More
@@ -1,298 +1,294 b''
1 1 import re
2 2 import time
3 3 import hashlib
4 4
5 5 from django import forms
6 6 from django.forms.util import ErrorList
7 7 from django.utils.translation import ugettext_lazy as _
8 8
9 9 from boards.mdx_neboard import formatters
10 10 from boards.models.post import TITLE_MAX_LENGTH
11 11 from boards.models import PostImage
12 12 from neboard import settings
13 13 from boards import utils
14 14 import boards.settings as board_settings
15 15
16 16 VETERAN_POSTING_DELAY = 5
17 17
18 18 ATTRIBUTE_PLACEHOLDER = 'placeholder'
19 19
20 20 LAST_POST_TIME = 'last_post_time'
21 21 LAST_LOGIN_TIME = 'last_login_time'
22 22 TEXT_PLACEHOLDER = _('''Type message here. Use formatting panel for more advanced usage.''')
23 23 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
24 24
25 25 ERROR_IMAGE_DUPLICATE = _('Such image was already posted')
26 26
27 27 LABEL_TITLE = _('Title')
28 28 LABEL_TEXT = _('Text')
29 29 LABEL_TAG = _('Tag')
30 30 LABEL_SEARCH = _('Search')
31 31
32 32 TAG_MAX_LENGTH = 20
33 33
34 34 REGEX_TAG = r'^[\w\d]+$'
35 35
36 36
37 37 class FormatPanel(forms.Textarea):
38 38 def render(self, name, value, attrs=None):
39 39 output = '<div id="mark-panel">'
40 40 for formatter in formatters:
41 41 output += '<span class="mark_btn"' + \
42 42 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
43 43 '\', \'' + formatter.format_right + '\')">' + \
44 44 formatter.preview_left + formatter.name + \
45 45 formatter.preview_right + '</span>'
46 46
47 47 output += '</div>'
48 48 output += super(FormatPanel, self).render(name, value, attrs=None)
49 49
50 50 return output
51 51
52 52
53 53 class PlainErrorList(ErrorList):
54 54 def __unicode__(self):
55 55 return self.as_text()
56 56
57 57 def as_text(self):
58 58 return ''.join(['(!) %s ' % e for e in self])
59 59
60 60
61 61 class NeboardForm(forms.Form):
62 62
63 63 def as_div(self):
64 64 """
65 65 Returns this form rendered as HTML <as_div>s.
66 66 """
67 67
68 68 return self._html_output(
69 69 # TODO Do not show hidden rows in the list here
70 70 normal_row='<div class="form-row"><div class="form-label">'
71 71 '%(label)s'
72 72 '</div></div>'
73 73 '<div class="form-row"><div class="form-input">'
74 74 '%(field)s'
75 75 '</div></div>'
76 76 '<div class="form-row">'
77 77 '%(help_text)s'
78 78 '</div>',
79 79 error_row='<div class="form-row">'
80 80 '<div class="form-label"></div>'
81 81 '<div class="form-errors">%s</div>'
82 82 '</div>',
83 83 row_ender='</div>',
84 84 help_text_html='%s',
85 85 errors_on_separate_row=True)
86 86
87 87 def as_json_errors(self):
88 88 errors = []
89 89
90 90 for name, field in list(self.fields.items()):
91 91 if self[name].errors:
92 92 errors.append({
93 93 'field': name,
94 94 'errors': self[name].errors.as_text(),
95 95 })
96 96
97 97 return errors
98 98
99 99
100 100 class PostForm(NeboardForm):
101 101
102 102 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
103 103 label=LABEL_TITLE)
104 104 text = forms.CharField(
105 105 widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}),
106 106 required=False, label=LABEL_TEXT)
107 107 image = forms.ImageField(required=False, label=_('Image'),
108 108 widget=forms.ClearableFileInput(
109 109 attrs={'accept': 'image/*'}))
110 110
111 111 # This field is for spam prevention only
112 112 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
113 113 widget=forms.TextInput(attrs={
114 114 'class': 'form-email'}))
115 115
116 116 session = None
117 117 need_to_ban = False
118 118
119 119 def clean_title(self):
120 120 title = self.cleaned_data['title']
121 121 if title:
122 122 if len(title) > TITLE_MAX_LENGTH:
123 123 raise forms.ValidationError(_('Title must have less than %s '
124 124 'characters') %
125 125 str(TITLE_MAX_LENGTH))
126 126 return title
127 127
128 128 def clean_text(self):
129 129 text = self.cleaned_data['text'].strip()
130 130 if text:
131 131 if len(text) > board_settings.MAX_TEXT_LENGTH:
132 132 raise forms.ValidationError(_('Text must have less than %s '
133 133 'characters') %
134 134 str(board_settings
135 135 .MAX_TEXT_LENGTH))
136 136 return text
137 137
138 138 def clean_image(self):
139 139 image = self.cleaned_data['image']
140 140 if image:
141 141 if image.size > board_settings.MAX_IMAGE_SIZE:
142 142 raise forms.ValidationError(
143 143 _('Image must be less than %s bytes')
144 144 % str(board_settings.MAX_IMAGE_SIZE))
145 145
146 146 md5 = hashlib.md5()
147 147 for chunk in image.chunks():
148 148 md5.update(chunk)
149 149 image_hash = md5.hexdigest()
150 150 if PostImage.objects.filter(hash=image_hash).exists():
151 151 raise forms.ValidationError(ERROR_IMAGE_DUPLICATE)
152 152
153 153 return image
154 154
155 155 def clean(self):
156 156 cleaned_data = super(PostForm, self).clean()
157 157
158 158 if not self.session:
159 159 raise forms.ValidationError('Humans have sessions')
160 160
161 161 if cleaned_data['email']:
162 162 self.need_to_ban = True
163 163 raise forms.ValidationError('A human cannot enter a hidden field')
164 164
165 165 if not self.errors:
166 166 self._clean_text_image()
167 167
168 168 if not self.errors and self.session:
169 169 self._validate_posting_speed()
170 170
171 171 return cleaned_data
172 172
173 173 def _clean_text_image(self):
174 174 text = self.cleaned_data.get('text')
175 175 image = self.cleaned_data.get('image')
176 176
177 177 if (not text) and (not image):
178 178 error_message = _('Either text or image must be entered.')
179 179 self._errors['text'] = self.error_class([error_message])
180 180
181 181 def _validate_posting_speed(self):
182 182 can_post = True
183 183
184 # TODO Remove this, it's only for test
185 if not 'user_id' in self.session:
186 return
187
188 184 posting_delay = settings.POSTING_DELAY
189 185
190 186 if board_settings.LIMIT_POSTING_SPEED and LAST_POST_TIME in \
191 187 self.session:
192 188 now = time.time()
193 189 last_post_time = self.session[LAST_POST_TIME]
194 190
195 191 current_delay = int(now - last_post_time)
196 192
197 193 if current_delay < posting_delay:
198 194 error_message = _('Wait %s seconds after last posting') % str(
199 195 posting_delay - current_delay)
200 196 self._errors['text'] = self.error_class([error_message])
201 197
202 198 can_post = False
203 199
204 200 if can_post:
205 201 self.session[LAST_POST_TIME] = time.time()
206 202
207 203
208 204 class ThreadForm(PostForm):
209 205
210 206 regex_tags = re.compile(r'^[\w\s\d]+$', re.UNICODE)
211 207
212 208 tags = forms.CharField(
213 209 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
214 210 max_length=100, label=_('Tags'), required=True)
215 211
216 212 def clean_tags(self):
217 213 tags = self.cleaned_data['tags'].strip()
218 214
219 215 if not tags or not self.regex_tags.match(tags):
220 216 raise forms.ValidationError(
221 217 _('Inappropriate characters in tags.'))
222 218
223 219 return tags
224 220
225 221 def clean(self):
226 222 cleaned_data = super(ThreadForm, self).clean()
227 223
228 224 return cleaned_data
229 225
230 226
231 227 class SettingsForm(NeboardForm):
232 228
233 229 theme = forms.ChoiceField(choices=settings.THEMES,
234 230 label=_('Theme'))
235 231
236 232
237 233 class AddTagForm(NeboardForm):
238 234
239 235 tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG)
240 236 method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag')
241 237
242 238 def clean_tag(self):
243 239 tag = self.cleaned_data['tag']
244 240
245 241 regex_tag = re.compile(REGEX_TAG, re.UNICODE)
246 242 if not regex_tag.match(tag):
247 243 raise forms.ValidationError(_('Inappropriate characters in tags.'))
248 244
249 245 return tag
250 246
251 247 def clean(self):
252 248 cleaned_data = super(AddTagForm, self).clean()
253 249
254 250 return cleaned_data
255 251
256 252
257 253 class SearchForm(NeboardForm):
258 254 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
259 255
260 256
261 257 class LoginForm(NeboardForm):
262 258
263 259 password = forms.CharField()
264 260
265 261 session = None
266 262
267 263 def clean_password(self):
268 264 password = self.cleaned_data['password']
269 265 if board_settings.MASTER_PASSWORD != password:
270 266 raise forms.ValidationError(_('Invalid master password'))
271 267
272 268 return password
273 269
274 270 def _validate_login_speed(self):
275 271 can_post = True
276 272
277 273 if LAST_LOGIN_TIME in self.session:
278 274 now = time.time()
279 275 last_login_time = self.session[LAST_LOGIN_TIME]
280 276
281 277 current_delay = int(now - last_login_time)
282 278
283 279 if current_delay < board_settings.LOGIN_TIMEOUT:
284 280 error_message = _('Wait %s minutes after last login') % str(
285 281 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
286 282 self._errors['password'] = self.error_class([error_message])
287 283
288 284 can_post = False
289 285
290 286 if can_post:
291 287 self.session[LAST_LOGIN_TIME] = time.time()
292 288
293 289 def clean(self):
294 290 self._validate_login_speed()
295 291
296 292 cleaned_data = super(LoginForm, self).clean()
297 293
298 294 return cleaned_data
General Comments 0
You need to be logged in to leave comments. Login now