##// END OF EJS Templates
Use one input for multiple files and multiple URLs
neko259 -
r1761:268fa495 default
parent child Browse files
Show More
@@ -17,6 +17,7 b' PowDifficulty = 0'
17 PostingDelay = 30
17 PostingDelay = 30
18 Autoban = false
18 Autoban = false
19 DefaultTag = test
19 DefaultTag = test
20 MaxFileCount = 2
20
21
21 [Messages]
22 [Messages]
22 # Thread bumplimit
23 # Thread bumplimit
@@ -5,10 +5,8 b' import time'
5
5
6 import pytz
6 import pytz
7 from django import forms
7 from django import forms
8 from django.core.files.uploadedfile import SimpleUploadedFile
8 from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
9 from django.core.files.uploadedfile import UploadedFile
10 from django.forms.utils import ErrorList
9 from django.forms.utils import ErrorList
11 from django.utils import timezone
12 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
13
11
14 import boards.settings as board_settings
12 import boards.settings as board_settings
@@ -24,6 +22,8 b' from boards.utils import validate_file_s'
24 FILE_EXTENSION_DELIMITER
22 FILE_EXTENSION_DELIMITER
25 from neboard import settings
23 from neboard import settings
26
24
25 SECTION_FORMS = 'Forms'
26
27 POW_HASH_LENGTH = 16
27 POW_HASH_LENGTH = 16
28 POW_LIFE_MINUTES = 5
28 POW_LIFE_MINUTES = 5
29
29
@@ -45,11 +45,11 b" LABEL_TITLE = _('Title')"
45 LABEL_TEXT = _('Text')
45 LABEL_TEXT = _('Text')
46 LABEL_TAG = _('Tag')
46 LABEL_TAG = _('Tag')
47 LABEL_SEARCH = _('Search')
47 LABEL_SEARCH = _('Search')
48 LABEL_FILE_1 = _('File 1')
48 LABEL_FILE = _('File')
49 LABEL_FILE_2 = _('File 2')
50
49
51 ERROR_SPEED = 'Please wait %(delay)d second before sending message'
50 ERROR_SPEED = 'Please wait %(delay)d second before sending message'
52 ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message'
51 ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message'
52 ERROR_MANY_FILES = _('Too many files.')
53
53
54 TAG_MAX_LENGTH = 20
54 TAG_MAX_LENGTH = 20
55
55
@@ -161,16 +161,14 b' class PostForm(NeboardForm):'
161 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
161 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
162 label=LABEL_TITLE,
162 label=LABEL_TITLE,
163 widget=forms.TextInput(
163 widget=forms.TextInput(
164 attrs={ATTRIBUTE_PLACEHOLDER:
164 attrs={ATTRIBUTE_PLACEHOLDER: 'title#tripcode'}))
165 'test#tripcode'}))
166 text = forms.CharField(
165 text = forms.CharField(
167 widget=FormatPanel(attrs={
166 widget=FormatPanel(attrs={
168 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
167 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
169 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
168 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
170 }),
169 }),
171 required=False, label=LABEL_TEXT)
170 required=False, label=LABEL_TEXT)
172 file_1 = UrlFileField(required=False, label=LABEL_FILE_1)
171 file = UrlFileField(required=False, label=LABEL_FILE)
173 file_2 = UrlFileField(required=False, label=LABEL_FILE_2)
174
172
175 # This field is for spam prevention only
173 # This field is for spam prevention only
176 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
174 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
@@ -198,35 +196,32 b' class PostForm(NeboardForm):'
198 def clean_text(self):
196 def clean_text(self):
199 text = self.cleaned_data['text'].strip()
197 text = self.cleaned_data['text'].strip()
200 if text:
198 if text:
201 max_length = board_settings.get_int('Forms', 'MaxTextLength')
199 max_length = board_settings.get_int(SECTION_FORMS, 'MaxTextLength')
202 if len(text) > max_length:
200 if len(text) > max_length:
203 raise forms.ValidationError(_('Text must have less than %s '
201 raise forms.ValidationError(_('Text must have less than %s '
204 'characters') % str(max_length))
202 'characters') % str(max_length))
205 return text
203 return text
206
204
207 def clean_file_1(self):
205 def clean_file(self):
208 return self._clean_file(self.cleaned_data['file_1'])
206 return self._clean_files(self.cleaned_data['file'])
209
210 def clean_file_2(self):
211 return self._clean_file(self.cleaned_data['file_2'])
212
207
213 def clean(self):
208 def clean(self):
214 cleaned_data = super(PostForm, self).clean()
209 cleaned_data = super(PostForm, self).clean()
215
210
216 if cleaned_data['email']:
211 if cleaned_data['email']:
217 if board_settings.get_bool('Forms', 'Autoban'):
212 if board_settings.get_bool(SECTION_FORMS, 'Autoban'):
218 self.need_to_ban = True
213 self.need_to_ban = True
219 raise forms.ValidationError('A human cannot enter a hidden field')
214 raise forms.ValidationError('A human cannot enter a hidden field')
220
215
221 if not self.errors:
216 if not self.errors:
222 self._clean_text_file()
217 self._clean_text_file()
223
218
224 limit_speed = board_settings.get_bool('Forms', 'LimitPostingSpeed')
219 limit_speed = board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed')
225 limit_first = board_settings.get_bool('Forms', 'LimitFirstPosting')
220 limit_first = board_settings.get_bool(SECTION_FORMS, 'LimitFirstPosting')
226
221
227 settings_manager = get_settings_manager(self)
222 settings_manager = get_settings_manager(self)
228 if not self.errors and limit_speed or (limit_first and not settings_manager.get_setting('confirmed_user')):
223 if not self.errors and limit_speed or (limit_first and not settings_manager.get_setting('confirmed_user')):
229 pow_difficulty = board_settings.get_int('Forms', 'PowDifficulty')
224 pow_difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
230 if pow_difficulty > 0:
225 if pow_difficulty > 0:
231 # PoW-based
226 # PoW-based
232 if cleaned_data['timestamp'] \
227 if cleaned_data['timestamp'] \
@@ -245,26 +240,16 b' class PostForm(NeboardForm):'
245 Gets file from form or URL.
240 Gets file from form or URL.
246 """
241 """
247
242
248 cleaned_files = [
249 self.cleaned_data['file_1'],
250 self.cleaned_data['file_2'],
251 ]
252
253 files = []
243 files = []
254 for file in cleaned_files:
244 for file in self.cleaned_data['file']:
255 if isinstance(file, UploadedFile):
245 if isinstance(file, UploadedFile):
256 files.append(file)
246 files.append(file)
257
247
258 return files
248 return files
259
249
260 def get_file_urls(self):
250 def get_file_urls(self):
261 cleaned_files = [
262 self.cleaned_data['file_1'],
263 self.cleaned_data['file_2'],
264 ]
265
266 files = []
251 files = []
267 for file in cleaned_files:
252 for file in self.cleaned_data['file']:
268 if type(file) == str:
253 if type(file) == str:
269 files.append(file)
254 files.append(file)
270
255
@@ -307,13 +292,24 b' class PostForm(NeboardForm):'
307 else:
292 else:
308 logger.info('Unrecognized file mimetype: {}'.format(mimetype))
293 logger.info('Unrecognized file mimetype: {}'.format(mimetype))
309
294
310 def _clean_file(self, file):
295 def _clean_files(self, inputs):
311 if isinstance(file, UploadedFile):
296 files = []
312 file = self._clean_file_file(file)
313 else:
314 file = self._clean_file_url(file)
315
297
316 return file
298 max_file_count = board_settings.get_int(SECTION_FORMS, 'MaxFileCount')
299 if type(inputs) == list:
300 if len(inputs) > max_file_count:
301 raise forms.ValidationError(ERROR_MANY_FILES)
302 for file in inputs:
303 files.append(self._clean_file_file(file))
304 elif inputs:
305 inputs = inputs.replace('\r\n', '\n')
306 url_list = inputs.split('\n')
307 if len(url_list) > max_file_count:
308 raise forms.ValidationError(ERROR_MANY_FILES)
309 for url in url_list:
310 files.append(self._clean_file_url(url))
311
312 return files
317
313
318 def _clean_file_file(self, file):
314 def _clean_file_file(self, file):
319 validate_file_size(file.size)
315 validate_file_size(file.size)
@@ -362,9 +358,9 b' class PostForm(NeboardForm):'
362 def _validate_posting_speed(self):
358 def _validate_posting_speed(self):
363 can_post = True
359 can_post = True
364
360
365 posting_delay = board_settings.get_int('Forms', 'PostingDelay')
361 posting_delay = board_settings.get_int(SECTION_FORMS, 'PostingDelay')
366
362
367 if board_settings.get_bool('Forms', 'LimitPostingSpeed'):
363 if board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed'):
368 now = time.time()
364 now = time.time()
369
365
370 current_delay = 0
366 current_delay = 0
@@ -404,7 +400,7 b' class PostForm(NeboardForm):'
404
400
405 def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str):
401 def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str):
406 payload = timestamp + message.replace('\r\n', '\n')
402 payload = timestamp + message.replace('\r\n', '\n')
407 difficulty = board_settings.get_int('Forms', 'PowDifficulty')
403 difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
408 target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty))
404 target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty))
409 if len(target) < POW_HASH_LENGTH:
405 if len(target) < POW_HASH_LENGTH:
410 target = '0' * (POW_HASH_LENGTH - len(target)) + target
406 target = '0' * (POW_HASH_LENGTH - len(target)) + target
@@ -430,7 +426,7 b' class ThreadForm(PostForm):'
430 raise forms.ValidationError(
426 raise forms.ValidationError(
431 _('Inappropriate characters in tags.'))
427 _('Inappropriate characters in tags.'))
432
428
433 default_tag_name = board_settings.get('Forms', 'DefaultTag')\
429 default_tag_name = board_settings.get(SECTION_FORMS, 'DefaultTag')\
434 .strip().lower()
430 .strip().lower()
435
431
436 required_tag_exists = False
432 required_tag_exists = False
@@ -3,14 +3,56 b' from django.utils.translation import uge'
3
3
4
4
5 ATTRIBUTE_PLACEHOLDER = 'placeholder'
5 ATTRIBUTE_PLACEHOLDER = 'placeholder'
6 ATTRIBUTE_ROWS = 'rows'
7
8 URL_ROWS = 2
9
10
11 class MultipleFileInput(forms.FileInput):
12 """
13 Input that allows to enter many files at once, utilizing the html5 "multiple"
14 attribute of a file input.
15 """
16 def value_from_datadict(self, data, files, name):
17 if len(files) == 0:
18 return None
19 else:
20 return files.getlist(name)
21
22 def render(self, name, value, attrs=None):
23 if not attrs:
24 attrs = dict()
25 attrs['multiple'] = ''
26
27 return super().render(name, None, attrs=attrs)
28
29
30 class MultipleFileField(forms.FileField):
31 """
32 Field that allows to enter multiple files from one input.
33 """
34 def to_python(self, data):
35 if not data or len(data) == 0:
36 return None
37
38 for file in data:
39 super().to_python(file)
40
41 return data
6
42
7
43
8 class UrlFileWidget(forms.MultiWidget):
44 class UrlFileWidget(forms.MultiWidget):
45 """
46 Widget with a file input and a text input that allows to enter either a
47 file from a local system, or a URL from which the file would be downloaded.
48 """
9 def __init__(self, *args, **kwargs):
49 def __init__(self, *args, **kwargs):
10 widgets = (
50 widgets = (
11 forms.ClearableFileInput(attrs={'accept': 'file/*'}),
51 MultipleFileInput(attrs={'accept': 'file/*'}),
12 forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER:
52 forms.Textarea(attrs={
13 'http://example.com/image.png'}),
53 ATTRIBUTE_PLACEHOLDER: 'http://example.com/image.png',
54 ATTRIBUTE_ROWS: URL_ROWS,
55 }),
14 )
56 )
15 super().__init__(widgets, *args, **kwargs)
57 super().__init__(widgets, *args, **kwargs)
16
58
@@ -19,17 +61,16 b' class UrlFileWidget(forms.MultiWidget):'
19
61
20
62
21 class UrlFileField(forms.MultiValueField):
63 class UrlFileField(forms.MultiValueField):
64 """
65 Field with a file input and a text input that allows to enter either a
66 file from a local system, or a URL from which the file would be downloaded.
67 """
22 widget = UrlFileWidget
68 widget = UrlFileWidget
23
69
24 def __init__(self, *args, **kwargs):
70 def __init__(self, *args, **kwargs):
25 fields = (
71 fields = (
26 forms.FileField(required=False, label=_('File'),
72 MultipleFileField(required=False, label=_('File')),
27 widget=forms.ClearableFileInput(
73 forms.CharField(required=False, label=_('File URL')),
28 attrs={'accept': 'file/*'})),
29 forms.CharField(required=False, label=_('File URL'),
30 widget=forms.TextInput(
31 attrs={ATTRIBUTE_PLACEHOLDER:
32 'http://example.com/image.png'})),
33 )
74 )
34
75
35 super().__init__(
76 super().__init__(
@@ -38,5 +79,5 b' class UrlFileField(forms.MultiValueField'
38
79
39 def compress(self, data_list):
80 def compress(self, data_list):
40 if data_list and len(data_list) >= 2:
81 if data_list and len(data_list) >= 2:
41 return data_list[0] or data_list[1]
82 return data_list[0] or data_list[1]
42
83
@@ -68,7 +68,7 b' function addQuickReply(postId) {'
68 resetFormPosition();
68 resetFormPosition();
69 } else {
69 } else {
70 var blockToInsert = null;
70 var blockToInsert = null;
71 var textAreaJq = $('textarea');
71 var textAreaJq = $('textarea#id_text');
72 var postLinkRaw = '[post]' + postId + '[/post]'
72 var postLinkRaw = '[post]' + postId + '[/post]'
73 var textToAdd = '';
73 var textToAdd = '';
74
74
@@ -101,7 +101,7 b' function addQuickReply(postId) {'
101 }
101 }
102
102
103 function addQuickQuote() {
103 function addQuickQuote() {
104 var textAreaJq = $('textarea');
104 var textAreaJq = $('textarea#id_text');
105
105
106 var quoteButton = $("#quote-button");
106 var quoteButton = $("#quote-button");
107 var postId = quoteButton.attr('data-post-id');
107 var postId = quoteButton.attr('data-post-id');
General Comments 0
You need to be logged in to leave comments. Login now