Show More
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | from __future__ import unicode_literals | |||
|
3 | ||||
|
4 | from django.db import models, migrations | |||
|
5 | ||||
|
6 | ||||
|
7 | class Migration(migrations.Migration): | |||
|
8 | ||||
|
9 | dependencies = [ | |||
|
10 | ('boards', '0021_tag_description'), | |||
|
11 | ] | |||
|
12 | ||||
|
13 | operations = [ | |||
|
14 | migrations.AlterField( | |||
|
15 | model_name='thread', | |||
|
16 | name='tags', | |||
|
17 | field=models.ManyToManyField(related_name='thread_tags', to='boards.Tag'), | |||
|
18 | ), | |||
|
19 | ] |
@@ -0,0 +1,29 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | from __future__ import unicode_literals | |||
|
3 | ||||
|
4 | from django.db import models, migrations | |||
|
5 | import boards.models.attachment | |||
|
6 | ||||
|
7 | ||||
|
8 | class Migration(migrations.Migration): | |||
|
9 | ||||
|
10 | dependencies = [ | |||
|
11 | ('boards', '0022_auto_20150812_1819'), | |||
|
12 | ] | |||
|
13 | ||||
|
14 | operations = [ | |||
|
15 | migrations.CreateModel( | |||
|
16 | name='Attachment', | |||
|
17 | fields=[ | |||
|
18 | ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), | |||
|
19 | ('file', models.FileField(upload_to=boards.models.attachment.Attachment._update_filename)), | |||
|
20 | ('mimetype', models.CharField(max_length=50)), | |||
|
21 | ('hash', models.CharField(max_length=36)), | |||
|
22 | ], | |||
|
23 | ), | |||
|
24 | migrations.AddField( | |||
|
25 | model_name='post', | |||
|
26 | name='attachments', | |||
|
27 | field=models.ManyToManyField(blank=True, null=True, related_name='attachment_posts', to='boards.Attachment'), | |||
|
28 | ), | |||
|
29 | ] |
@@ -0,0 +1,75 b'' | |||||
|
1 | import hashlib | |||
|
2 | import os | |||
|
3 | import time | |||
|
4 | ||||
|
5 | from random import random | |||
|
6 | ||||
|
7 | from django.db import models | |||
|
8 | ||||
|
9 | from boards.models.attachment.viewers import AbstractViewer, WebmViewer | |||
|
10 | ||||
|
11 | ||||
|
12 | FILES_DIRECTORY = 'files/' | |||
|
13 | FILE_EXTENSION_DELIMITER = '.' | |||
|
14 | ||||
|
15 | VIEWERS = ( | |||
|
16 | WebmViewer, | |||
|
17 | ) | |||
|
18 | ||||
|
19 | ||||
|
20 | class AttachmentManager(models.Manager): | |||
|
21 | def create_with_hash(self, file): | |||
|
22 | file_hash = self.get_hash(file) | |||
|
23 | existing = self.filter(hash=file_hash) | |||
|
24 | if len(existing) > 0: | |||
|
25 | attachment = existing[0] | |||
|
26 | else: | |||
|
27 | file_type = file.name.split(FILE_EXTENSION_DELIMITER)[-1].lower() | |||
|
28 | attachment = Attachment.objects.create(file=file, | |||
|
29 | mimetype=file_type, hash=file_hash) | |||
|
30 | ||||
|
31 | return attachment | |||
|
32 | ||||
|
33 | def get_hash(self, file): | |||
|
34 | """ | |||
|
35 | Gets hash of an file. | |||
|
36 | """ | |||
|
37 | md5 = hashlib.md5() | |||
|
38 | for chunk in file.chunks(): | |||
|
39 | md5.update(chunk) | |||
|
40 | return md5.hexdigest() | |||
|
41 | ||||
|
42 | ||||
|
43 | class Attachment(models.Model): | |||
|
44 | objects = AttachmentManager() | |||
|
45 | ||||
|
46 | # TODO Dedup the method | |||
|
47 | def _update_filename(self, filename): | |||
|
48 | """ | |||
|
49 | Gets unique filename | |||
|
50 | """ | |||
|
51 | ||||
|
52 | # TODO Use something other than random number in file name | |||
|
53 | new_name = '{}{}.{}'.format( | |||
|
54 | str(int(time.mktime(time.gmtime()))), | |||
|
55 | str(int(random() * 1000)), | |||
|
56 | filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]) | |||
|
57 | ||||
|
58 | return os.path.join(FILES_DIRECTORY, new_name) | |||
|
59 | ||||
|
60 | file = models.FileField(upload_to=_update_filename) | |||
|
61 | mimetype = models.CharField(max_length=50) | |||
|
62 | hash = models.CharField(max_length=36) | |||
|
63 | ||||
|
64 | def get_view(self): | |||
|
65 | file_viewer = None | |||
|
66 | for viewer in VIEWERS: | |||
|
67 | if viewer.supports(self.mimetype): | |||
|
68 | file_viewer = viewer(self.file, self.mimetype) | |||
|
69 | break | |||
|
70 | if file_viewer is None: | |||
|
71 | file_viewer = AbstractViewer(self.file, self.mimetype) | |||
|
72 | ||||
|
73 | return file_viewer.get_view() | |||
|
74 | ||||
|
75 |
@@ -0,0 +1,39 b'' | |||||
|
1 | from django.template.defaultfilters import filesizeformat | |||
|
2 | ||||
|
3 | ||||
|
4 | class AbstractViewer: | |||
|
5 | def __init__(self, file, file_type): | |||
|
6 | self.file = file | |||
|
7 | self.file_type = file_type | |||
|
8 | ||||
|
9 | @staticmethod | |||
|
10 | def get_viewer(file_type, file): | |||
|
11 | for viewer in VIEWERS: | |||
|
12 | if viewer.supports(file_type): | |||
|
13 | return viewer(file) | |||
|
14 | return AbstractViewer(file) | |||
|
15 | ||||
|
16 | @staticmethod | |||
|
17 | def supports(file_type): | |||
|
18 | return true | |||
|
19 | ||||
|
20 | def get_view(self): | |||
|
21 | return '<div class="image"><a href="{}">'\ | |||
|
22 | '<img src="/static/images/file.png" width="200" height="150"/>'\ | |||
|
23 | '</a>'\ | |||
|
24 | '<div class="image-metadata">{}, {}</div>'\ | |||
|
25 | '</div>'.format(self.file.url, self.file_type, | |||
|
26 | filesizeformat(self.file.size)) | |||
|
27 | ||||
|
28 | ||||
|
29 | class WebmViewer(AbstractViewer): | |||
|
30 | @staticmethod | |||
|
31 | def supports(file_type): | |||
|
32 | return file_type == 'webm' | |||
|
33 | ||||
|
34 | def get_view(self): | |||
|
35 | return '<div class="image">'\ | |||
|
36 | '<video width="200" height="150" controls/>'\ | |||
|
37 | '<source src="{}">'\ | |||
|
38 | '</div>'.format(self.file.url) | |||
|
39 |
@@ -9,7 +9,7 b' CacheTimeout = 600' | |||||
9 | [Forms] |
|
9 | [Forms] | |
10 | # Max post length in characters |
|
10 | # Max post length in characters | |
11 | MaxTextLength = 30000 |
|
11 | MaxTextLength = 30000 | |
12 |
Max |
|
12 | MaxFileSize = 8000000 | |
13 | LimitPostingSpeed = false |
|
13 | LimitPostingSpeed = false | |
14 |
|
14 | |||
15 | [Messages] |
|
15 | [Messages] |
@@ -18,14 +18,6 b' import boards.settings as board_settings' | |||||
18 | HEADER_CONTENT_LENGTH = 'content-length' |
|
18 | HEADER_CONTENT_LENGTH = 'content-length' | |
19 | HEADER_CONTENT_TYPE = 'content-type' |
|
19 | HEADER_CONTENT_TYPE = 'content-type' | |
20 |
|
20 | |||
21 | CONTENT_TYPE_IMAGE = ( |
|
|||
22 | 'image/jpeg', |
|
|||
23 | 'image/jpg', |
|
|||
24 | 'image/png', |
|
|||
25 | 'image/gif', |
|
|||
26 | 'image/bmp', |
|
|||
27 | ) |
|
|||
28 |
|
||||
29 | REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE) |
|
21 | REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE) | |
30 |
|
22 | |||
31 | VETERAN_POSTING_DELAY = 5 |
|
23 | VETERAN_POSTING_DELAY = 5 | |
@@ -47,7 +39,7 b" ERROR_SPEED = _('Please wait %s seconds " | |||||
47 |
|
39 | |||
48 | TAG_MAX_LENGTH = 20 |
|
40 | TAG_MAX_LENGTH = 20 | |
49 |
|
41 | |||
50 |
|
|
42 | FILE_DOWNLOAD_CHUNK_BYTES = 100000 | |
51 |
|
43 | |||
52 | HTTP_RESULT_OK = 200 |
|
44 | HTTP_RESULT_OK = 200 | |
53 |
|
45 | |||
@@ -144,10 +136,10 b' class PostForm(NeboardForm):' | |||||
144 | ATTRIBUTE_ROWS: TEXTAREA_ROWS, |
|
136 | ATTRIBUTE_ROWS: TEXTAREA_ROWS, | |
145 | }), |
|
137 | }), | |
146 | required=False, label=LABEL_TEXT) |
|
138 | required=False, label=LABEL_TEXT) | |
147 |
|
|
139 | file = forms.FileField(required=False, label=_('File'), | |
148 | widget=forms.ClearableFileInput( |
|
140 | widget=forms.ClearableFileInput( | |
149 |
attrs={'accept': ' |
|
141 | attrs={'accept': 'file/*'})) | |
150 |
|
|
142 | file_url = forms.CharField(required=False, label=_('File URL'), | |
151 | widget=forms.TextInput( |
|
143 | widget=forms.TextInput( | |
152 | attrs={ATTRIBUTE_PLACEHOLDER: |
|
144 | attrs={ATTRIBUTE_PLACEHOLDER: | |
153 | 'http://example.com/image.png'})) |
|
145 | 'http://example.com/image.png'})) | |
@@ -181,27 +173,27 b' class PostForm(NeboardForm):' | |||||
181 | 'characters') % str(max_length)) |
|
173 | 'characters') % str(max_length)) | |
182 | return text |
|
174 | return text | |
183 |
|
175 | |||
184 |
def clean_ |
|
176 | def clean_file(self): | |
185 |
|
|
177 | file = self.cleaned_data['file'] | |
186 |
|
178 | |||
187 |
if |
|
179 | if file: | |
188 |
self.validate_ |
|
180 | self.validate_file_size(file.size) | |
189 |
|
181 | |||
190 |
return |
|
182 | return file | |
191 |
|
183 | |||
192 |
def clean_ |
|
184 | def clean_file_url(self): | |
193 |
url = self.cleaned_data[' |
|
185 | url = self.cleaned_data['file_url'] | |
194 |
|
186 | |||
195 |
|
|
187 | file = None | |
196 | if url: |
|
188 | if url: | |
197 |
|
|
189 | file = self._get_file_from_url(url) | |
198 |
|
190 | |||
199 |
if not |
|
191 | if not file: | |
200 | raise forms.ValidationError(_('Invalid URL')) |
|
192 | raise forms.ValidationError(_('Invalid URL')) | |
201 | else: |
|
193 | else: | |
202 |
self.validate_ |
|
194 | self.validate_file_size(file.size) | |
203 |
|
195 | |||
204 |
return |
|
196 | return file | |
205 |
|
197 | |||
206 | def clean_threads(self): |
|
198 | def clean_threads(self): | |
207 | threads_str = self.cleaned_data['threads'] |
|
199 | threads_str = self.cleaned_data['threads'] | |
@@ -230,27 +222,27 b' class PostForm(NeboardForm):' | |||||
230 | raise forms.ValidationError('A human cannot enter a hidden field') |
|
222 | raise forms.ValidationError('A human cannot enter a hidden field') | |
231 |
|
223 | |||
232 | if not self.errors: |
|
224 | if not self.errors: | |
233 |
self._clean_text_ |
|
225 | self._clean_text_file() | |
234 |
|
226 | |||
235 | if not self.errors and self.session: |
|
227 | if not self.errors and self.session: | |
236 | self._validate_posting_speed() |
|
228 | self._validate_posting_speed() | |
237 |
|
229 | |||
238 | return cleaned_data |
|
230 | return cleaned_data | |
239 |
|
231 | |||
240 |
def get_ |
|
232 | def get_file(self): | |
241 | """ |
|
233 | """ | |
242 |
Gets |
|
234 | Gets file from form or URL. | |
243 | """ |
|
235 | """ | |
244 |
|
236 | |||
245 |
|
|
237 | file = self.cleaned_data['file'] | |
246 |
return |
|
238 | return file or self.cleaned_data['file_url'] | |
247 |
|
239 | |||
248 |
def _clean_text_ |
|
240 | def _clean_text_file(self): | |
249 | text = self.cleaned_data.get('text') |
|
241 | text = self.cleaned_data.get('text') | |
250 |
|
|
242 | file = self.get_file() | |
251 |
|
243 | |||
252 |
if (not text) and (not |
|
244 | if (not text) and (not file): | |
253 |
error_message = _('Either text or |
|
245 | error_message = _('Either text or file must be entered.') | |
254 | self._errors['text'] = self.error_class([error_message]) |
|
246 | self._errors['text'] = self.error_class([error_message]) | |
255 |
|
247 | |||
256 | def _validate_posting_speed(self): |
|
248 | def _validate_posting_speed(self): | |
@@ -284,16 +276,16 b' class PostForm(NeboardForm):' | |||||
284 | if can_post: |
|
276 | if can_post: | |
285 | self.session[LAST_POST_TIME] = now |
|
277 | self.session[LAST_POST_TIME] = now | |
286 |
|
278 | |||
287 |
def validate_ |
|
279 | def validate_file_size(self, size: int): | |
288 |
max_size = board_settings.get_int('Forms', 'Max |
|
280 | max_size = board_settings.get_int('Forms', 'MaxFileSize') | |
289 | if size > max_size: |
|
281 | if size > max_size: | |
290 | raise forms.ValidationError( |
|
282 | raise forms.ValidationError( | |
291 |
_(' |
|
283 | _('File must be less than %s bytes') | |
292 | % str(max_size)) |
|
284 | % str(max_size)) | |
293 |
|
285 | |||
294 |
def _get_ |
|
286 | def _get_file_from_url(self, url: str) -> SimpleUploadedFile: | |
295 | """ |
|
287 | """ | |
296 |
Gets an |
|
288 | Gets an file file from URL. | |
297 | """ |
|
289 | """ | |
298 |
|
290 | |||
299 | img_temp = None |
|
291 | img_temp = None | |
@@ -302,30 +294,29 b' class PostForm(NeboardForm):' | |||||
302 | # Verify content headers |
|
294 | # Verify content headers | |
303 | response_head = requests.head(url, verify=False) |
|
295 | response_head = requests.head(url, verify=False) | |
304 | content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0] |
|
296 | content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0] | |
305 | if content_type in CONTENT_TYPE_IMAGE: |
|
297 | length_header = response_head.headers.get(HEADER_CONTENT_LENGTH) | |
306 | length_header = response_head.headers.get(HEADER_CONTENT_LENGTH) |
|
298 | if length_header: | |
307 |
|
|
299 | length = int(length_header) | |
308 | length = int(length_header) |
|
300 | self.validate_file_size(length) | |
309 | self.validate_image_size(length) |
|
301 | # Get the actual content into memory | |
310 | # Get the actual content into memory |
|
302 | response = requests.get(url, verify=False, stream=True) | |
311 | response = requests.get(url, verify=False, stream=True) |
|
|||
312 |
|
303 | |||
313 |
|
|
304 | # Download file, stop if the size exceeds limit | |
314 |
|
|
305 | size = 0 | |
315 |
|
|
306 | content = b'' | |
316 |
|
|
307 | for chunk in response.iter_content(file_DOWNLOAD_CHUNK_BYTES): | |
317 |
|
|
308 | size += len(chunk) | |
318 |
|
|
309 | self.validate_file_size(size) | |
319 |
|
|
310 | content += chunk | |
320 |
|
311 | |||
321 |
|
|
312 | if response.status_code == HTTP_RESULT_OK and content: | |
322 |
|
|
313 | # Set a dummy file name that will be replaced | |
323 |
|
|
314 | # anyway, just keep the valid extension | |
324 |
|
|
315 | filename = 'file.' + content_type.split('/')[1] | |
325 |
|
|
316 | img_temp = SimpleUploadedFile(filename, content, | |
326 |
|
|
317 | content_type) | |
327 | except Exception: |
|
318 | except Exception: | |
328 |
# Just return no |
|
319 | # Just return no file | |
329 | pass |
|
320 | pass | |
330 |
|
321 | |||
331 | return img_temp |
|
322 | return img_temp | |
@@ -369,7 +360,7 b' class ThreadForm(PostForm):' | |||||
369 | class SettingsForm(NeboardForm): |
|
360 | class SettingsForm(NeboardForm): | |
370 |
|
361 | |||
371 | theme = forms.ChoiceField(choices=settings.THEMES, label=_('Theme')) |
|
362 | theme = forms.ChoiceField(choices=settings.THEMES, label=_('Theme')) | |
372 |
image_viewer = forms.ChoiceField(choices=settings.IMAGE_VIEWERS, label=_(' |
|
363 | image_viewer = forms.ChoiceField(choices=settings.IMAGE_VIEWERS, label=_('image view mode')) | |
373 | username = forms.CharField(label=_('User name'), required=False) |
|
364 | username = forms.CharField(label=_('User name'), required=False) | |
374 | timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone')) |
|
365 | timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone')) | |
375 |
|
366 |
@@ -1,6 +1,7 b'' | |||||
1 | __author__ = 'neko259' |
|
1 | __author__ = 'neko259' | |
2 |
|
2 | |||
3 | from boards.models.image import PostImage |
|
3 | from boards.models.image import PostImage | |
|
4 | from boards.models.attachment import Attachment | |||
4 | from boards.models.thread import Thread |
|
5 | from boards.models.thread import Thread | |
5 | from boards.models.post import Post |
|
6 | from boards.models.post import Post | |
6 | from boards.models.tag import Tag |
|
7 | from boards.models.tag import Tag |
@@ -61,15 +61,13 b' class PostImage(models.Model, Viewable):' | |||||
61 | Gets unique image filename |
|
61 | Gets unique image filename | |
62 | """ |
|
62 | """ | |
63 |
|
63 | |||
64 | path = IMAGES_DIRECTORY |
|
|||
65 |
|
||||
66 | # TODO Use something other than random number in file name |
|
64 | # TODO Use something other than random number in file name | |
67 | new_name = '{}{}.{}'.format( |
|
65 | new_name = '{}{}.{}'.format( | |
68 | str(int(time.mktime(time.gmtime()))), |
|
66 | str(int(time.mktime(time.gmtime()))), | |
69 | str(int(random() * 1000)), |
|
67 | str(int(random() * 1000)), | |
70 | filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]) |
|
68 | filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]) | |
71 |
|
69 | |||
72 |
return os.path.join( |
|
70 | return os.path.join(IMAGES_DIRECTORY, new_name) | |
73 |
|
71 | |||
74 | width = models.IntegerField(default=0) |
|
72 | width = models.IntegerField(default=0) | |
75 | height = models.IntegerField(default=0) |
|
73 | height = models.IntegerField(default=0) |
@@ -13,7 +13,7 b' from django.utils import timezone' | |||||
13 |
|
13 | |||
14 | from boards import settings |
|
14 | from boards import settings | |
15 | from boards.mdx_neboard import Parser |
|
15 | from boards.mdx_neboard import Parser | |
16 | from boards.models import PostImage |
|
16 | from boards.models import PostImage, Attachment | |
17 | from boards.models.base import Viewable |
|
17 | from boards.models.base import Viewable | |
18 | from boards import utils |
|
18 | from boards import utils | |
19 | from boards.models.post.export import get_exporter, DIFF_TYPE_JSON |
|
19 | from boards.models.post.export import get_exporter, DIFF_TYPE_JSON | |
@@ -62,10 +62,17 b' POST_VIEW_PARAMS = (' | |||||
62 |
|
62 | |||
63 | REFMAP_STR = '<a href="{}">>>{}</a>' |
|
63 | REFMAP_STR = '<a href="{}">>>{}</a>' | |
64 |
|
64 | |||
|
65 | IMAGE_TYPES = ( | |||
|
66 | 'jpeg', | |||
|
67 | 'jpg', | |||
|
68 | 'png', | |||
|
69 | 'bmp', | |||
|
70 | ) | |||
|
71 | ||||
65 |
|
72 | |||
66 | class PostManager(models.Manager): |
|
73 | class PostManager(models.Manager): | |
67 | @transaction.atomic |
|
74 | @transaction.atomic | |
68 |
def create_post(self, title: str, text: str, |
|
75 | def create_post(self, title: str, text: str, file=None, thread=None, | |
69 | ip=NO_IP, tags: list=None, opening_posts: list=None): |
|
76 | ip=NO_IP, tags: list=None, opening_posts: list=None): | |
70 | """ |
|
77 | """ | |
71 | Creates new post |
|
78 | Creates new post | |
@@ -105,8 +112,13 b' class PostManager(models.Manager):' | |||||
105 |
|
112 | |||
106 | logger.info('Created post {} by {}'.format(post, post.poster_ip)) |
|
113 | logger.info('Created post {} by {}'.format(post, post.poster_ip)) | |
107 |
|
114 | |||
108 | if image: |
|
115 | # TODO Move this to other place | |
109 | post.images.add(PostImage.objects.create_with_hash(image)) |
|
116 | if file: | |
|
117 | file_type = file.name.split('.')[-1].lower() | |||
|
118 | if file_type in IMAGE_TYPES: | |||
|
119 | post.images.add(PostImage.objects.create_with_hash(file)) | |||
|
120 | else: | |||
|
121 | post.attachments.add(Attachment.objects.create_with_hash(file)) | |||
110 |
|
122 | |||
111 | post.build_url() |
|
123 | post.build_url() | |
112 | post.connect_replies() |
|
124 | post.connect_replies() | |
@@ -169,6 +181,8 b' class Post(models.Model, Viewable):' | |||||
169 |
|
181 | |||
170 | images = models.ManyToManyField(PostImage, null=True, blank=True, |
|
182 | images = models.ManyToManyField(PostImage, null=True, blank=True, | |
171 | related_name='post_images', db_index=True) |
|
183 | related_name='post_images', db_index=True) | |
|
184 | attachments = models.ManyToManyField(Attachment, null=True, blank=True, | |||
|
185 | related_name='attachment_posts') | |||
172 |
|
186 | |||
173 | poster_ip = models.GenericIPAddressField() |
|
187 | poster_ip = models.GenericIPAddressField() | |
174 |
|
188 |
@@ -51,12 +51,19 b'' | |||||
51 | supports multiple. |
|
51 | supports multiple. | |
52 | {% endcomment %} |
|
52 | {% endcomment %} | |
53 | {% if post.images.exists %} |
|
53 | {% if post.images.exists %} | |
54 |
{% with post.images. |
|
54 | {% with post.images.first as image %} | |
55 | {% autoescape off %} |
|
55 | {% autoescape off %} | |
56 | {{ image.get_view }} |
|
56 | {{ image.get_view }} | |
57 | {% endautoescape %} |
|
57 | {% endautoescape %} | |
58 | {% endwith %} |
|
58 | {% endwith %} | |
59 | {% endif %} |
|
59 | {% endif %} | |
|
60 | {% if post.attachments.exists %} | |||
|
61 | {% with post.attachments.first as file %} | |||
|
62 | {% autoescape off %} | |||
|
63 | {{ file.get_view }} | |||
|
64 | {% endautoescape %} | |||
|
65 | {% endwith %} | |||
|
66 | {% endif %} | |||
60 | {% comment %} |
|
67 | {% comment %} | |
61 | Post message (text) |
|
68 | Post message (text) | |
62 | {% endcomment %} |
|
69 | {% endcomment %} |
@@ -141,7 +141,7 b' class AllThreadsView(PostMixin, BaseBoar' | |||||
141 |
|
141 | |||
142 | title = data[FORM_TITLE] |
|
142 | title = data[FORM_TITLE] | |
143 | text = data[FORM_TEXT] |
|
143 | text = data[FORM_TEXT] | |
144 |
|
|
144 | file = form.get_file() | |
145 | threads = data[FORM_THREADS] |
|
145 | threads = data[FORM_THREADS] | |
146 |
|
146 | |||
147 | text = self._remove_invalid_links(text) |
|
147 | text = self._remove_invalid_links(text) | |
@@ -150,7 +150,7 b' class AllThreadsView(PostMixin, BaseBoar' | |||||
150 |
|
150 | |||
151 | tags = self.parse_tags_string(tag_strings) |
|
151 | tags = self.parse_tags_string(tag_strings) | |
152 |
|
152 | |||
153 |
post = Post.objects.create_post(title=title, text=text, |
|
153 | post = Post.objects.create_post(title=title, text=text, file=file, | |
154 | ip=ip, tags=tags, opening_posts=threads) |
|
154 | ip=ip, tags=tags, opening_posts=threads) | |
155 |
|
155 | |||
156 | # This is required to update the threads to which posts we have replied |
|
156 | # This is required to update the threads to which posts we have replied |
@@ -102,14 +102,14 b' class ThreadView(BaseBoardView, PostMixi' | |||||
102 |
|
102 | |||
103 | title = data[FORM_TITLE] |
|
103 | title = data[FORM_TITLE] | |
104 | text = data[FORM_TEXT] |
|
104 | text = data[FORM_TEXT] | |
105 |
|
|
105 | file = form.get_file() | |
106 | threads = data[FORM_THREADS] |
|
106 | threads = data[FORM_THREADS] | |
107 |
|
107 | |||
108 | text = self._remove_invalid_links(text) |
|
108 | text = self._remove_invalid_links(text) | |
109 |
|
109 | |||
110 | post_thread = opening_post.get_thread() |
|
110 | post_thread = opening_post.get_thread() | |
111 |
|
111 | |||
112 |
post = Post.objects.create_post(title=title, text=text, |
|
112 | post = Post.objects.create_post(title=title, text=text, file=file, | |
113 | thread=post_thread, ip=ip, |
|
113 | thread=post_thread, ip=ip, | |
114 | opening_posts=threads) |
|
114 | opening_posts=threads) | |
115 | post.notify_clients() |
|
115 | post.notify_clients() |
General Comments 0
You need to be logged in to leave comments.
Login now