##// END OF EJS Templates
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
neko259 -
r1997:be673d04 default
parent child Browse files
Show More
@@ -2,3 +2,4 b' import re'
2
2
3 FILE_DIRECTORY = 'files/'
3 FILE_DIRECTORY = 'files/'
4 REGEX_TAGS = re.compile(r'^[\w\s\d\']+$', re.UNICODE)
4 REGEX_TAGS = re.compile(r'^[\w\s\d\']+$', re.UNICODE)
5 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]') No newline at end of file
@@ -8,6 +8,7 b' from django.template.defaultfilters impo'
8 from django.template.loader import render_to_string
8 from django.template.loader import render_to_string
9 from django.urls import reverse
9 from django.urls import reverse
10
10
11 from boards.abstracts.constants import REGEX_REPLY
11 from boards.abstracts.tripcode import Tripcode
12 from boards.abstracts.tripcode import Tripcode
12 from boards.models import Attachment, KeyPair, GlobalId
13 from boards.models import Attachment, KeyPair, GlobalId
13 from boards.models.attachment import FILE_TYPES_IMAGE
14 from boards.models.attachment import FILE_TYPES_IMAGE
@@ -30,7 +31,6 b" BAN_REASON_AUTO = 'Auto'"
30
31
31 TITLE_MAX_LENGTH = 200
32 TITLE_MAX_LENGTH = 200
32
33
33 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
34 REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]')
34 REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]')
35 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
35 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
36 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
36 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
@@ -1,16 +1,18 b''
1 import logging
1 import logging
2 import re
2 from datetime import datetime, timedelta, date
3 from datetime import datetime, timedelta, date
3 from datetime import time as dtime
4 from datetime import time as dtime
4
5
5 from django.core.exceptions import PermissionDenied
6 from django.core.exceptions import PermissionDenied
6 from django.db import models, transaction
7 from django.db import models, transaction
7 from django.dispatch import Signal
8 from django.dispatch import Signal
9 from django.shortcuts import redirect
8 from django.utils import timezone
10 from django.utils import timezone
9
11
10 import boards
12 import boards
11 from boards import utils
13 from boards import utils
12 from boards.abstracts.exceptions import ArchiveException
14 from boards.abstracts.exceptions import ArchiveException
13 from boards.abstracts.constants import REGEX_TAGS
15 from boards.abstracts.constants import REGEX_TAGS, REGEX_REPLY
14 from boards.mdx_neboard import Parser
16 from boards.mdx_neboard import Parser
15 from boards.models import Attachment
17 from boards.models import Attachment
16 from boards.models.attachment import StickerPack, AttachmentSticker
18 from boards.models.attachment import StickerPack, AttachmentSticker
@@ -24,6 +26,11 b" NO_IP = '0.0.0.0'"
24
26
25 post_import_deps = Signal()
27 post_import_deps = Signal()
26
28
29 FORM_TEXT = 'text'
30 FORM_TAGS = 'tags'
31
32 REFLINK_PREFIX = '>>'
33
27
34
28 class PostManager(models.Manager):
35 class PostManager(models.Manager):
29 @transaction.atomic
36 @transaction.atomic
@@ -190,6 +197,48 b' class PostManager(models.Manager):'
190 thread.tags.clear()
197 thread.tags.clear()
191 list(map(thread.tags.add, tags))
198 list(map(thread.tags.add, tags))
192
199
200 def create_from_form(self, request, form, opening_post, html_response=True):
201 ip = utils.get_client_ip(request)
202
203 data = form.cleaned_data
204
205 title = form.get_title()
206 text = data[FORM_TEXT]
207 files = form.get_files()
208 file_urls = form.get_file_urls()
209 images = form.get_images()
210
211 text = self._remove_invalid_links(text)
212
213 if opening_post:
214 post_thread = opening_post.get_thread()
215 monochrome = False
216 stickerpack = False
217 tags = []
218 else:
219 tags = data[FORM_TAGS]
220 monochrome = form.is_monochrome()
221 stickerpack = form.is_stickerpack()
222 post_thread = None
223
224 post = self.create_post(title=title, text=text, files=files,
225 thread=post_thread, ip=ip,
226 tripcode=form.get_tripcode(),
227 images=images, file_urls=file_urls,
228 monochrome=monochrome,
229 stickerpack=stickerpack, tags=tags)
230
231 if form.is_subscribe():
232 from boards.abstracts.settingsmanager import get_settings_manager
233 settings_manager = get_settings_manager(request)
234 settings_manager.add_or_read_fav_thread(
235 post_thread.get_opening_post())
236
237 if html_response:
238 return redirect(post.get_absolute_url())
239 else:
240 return post
241
193 def _add_file_to_post(self, file, post):
242 def _add_file_to_post(self, file, post):
194 post.attachments.add(Attachment.objects.create_with_hash(file))
243 post.attachments.add(Attachment.objects.create_with_hash(file))
195
244
@@ -226,3 +275,17 b' class PostManager(models.Manager):'
226 name=post.get_title(), tripcode=post.tripcode)
275 name=post.get_title(), tripcode=post.tripcode)
227 if created:
276 if created:
228 logger.info('Created stickerpack {}'.format(stickerpack))
277 logger.info('Created stickerpack {}'.format(stickerpack))
278
279 def _remove_invalid_links(self, text):
280 """
281 Replace invalid links in posts so that they won't be parsed.
282 Invalid links are links to non-existent posts
283 """
284
285 for reply_number in re.finditer(REGEX_REPLY, text):
286 post_id = reply_number.group(1)
287 post = self.filter(id=post_id)
288 if not post.exists():
289 text = text.replace(REFLINK_PREFIX + post_id, post_id)
290
291 return text
@@ -13,8 +13,8 b' from boards.mdx_neboard import get_parse'
13 from boards.models import Post, GlobalId, Attachment, Thread
13 from boards.models import Post, GlobalId, Attachment, Thread
14 from boards.models.attachment import StickerPack, AttachmentSticker
14 from boards.models.attachment import StickerPack, AttachmentSticker
15 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
15 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
16 from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY,\
16 from boards.models.post import REGEX_NOTIFICATION, REGEX_GLOBAL_REPLY
17 REGEX_GLOBAL_REPLY
17 from boards.abstracts.constants import REGEX_REPLY
18 from boards.models.post.manager import post_import_deps
18 from boards.models.post.manager import post_import_deps
19 from boards.models.user import Notification
19 from boards.models.user import Notification
20 from neboard.settings import MEDIA_ROOT
20 from neboard.settings import MEDIA_ROOT
@@ -4,7 +4,7 b' from django.views.i18n import JavaScript'
4
4
5 from boards import views
5 from boards import views
6 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
6 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
7 from boards.views import api, tag_threads, all_threads, settings, feed, stickers
7 from boards.views import api, tag_threads, all_threads, settings, feed, stickers, thread, banned
8 from boards.views.authors import AuthorsView
8 from boards.views.authors import AuthorsView
9 from boards.views.landing import LandingView
9 from boards.views.landing import LandingView
10 from boards.views.notifications import NotificationView
10 from boards.views.notifications import NotificationView
@@ -1,22 +1,19 b''
1 from django.core.paginator import EmptyPage
1 from django.core.paginator import EmptyPage
2 from django.db import transaction
3 from django.http import Http404
2 from django.http import Http404
4 from django.shortcuts import render, redirect
3 from django.shortcuts import render, redirect
5 from django.urls import reverse
4 from django.urls import reverse
6 from django.utils.decorators import method_decorator
5 from django.utils.decorators import method_decorator
7 from django.views.decorators.csrf import csrf_protect
6 from django.views.decorators.csrf import csrf_protect
8
7
9 from boards import utils, settings
8 from boards import settings
10 from boards.abstracts.paginator import get_paginator
9 from boards.abstracts.paginator import get_paginator
11 from boards.abstracts.settingsmanager import get_settings_manager, \
10 from boards.abstracts.settingsmanager import get_settings_manager, \
12 SETTING_ONLY_FAVORITES
11 SETTING_ONLY_FAVORITES
13 from boards.forms import ThreadForm, PlainErrorList
12 from boards.forms import ThreadForm, PlainErrorList
14 from boards.models import Post, Thread, Ban
13 from boards.models import Post, Thread
15 from boards.views.banned import BannedView
16 from boards.views.base import BaseBoardView, CONTEXT_FORM
14 from boards.views.base import BaseBoardView, CONTEXT_FORM
17 from boards.views.mixins import FileUploadMixin, PaginatedMixin, \
15 from boards.views.mixins import FileUploadMixin, PaginatedMixin, \
18 DispatcherMixin, PARAMETER_METHOD
16 DispatcherMixin, PARAMETER_METHOD
19 from boards.views.posting_mixin import PostMixin
20
17
21 FORM_TAGS = 'tags'
18 FORM_TAGS = 'tags'
22 FORM_TEXT = 'text'
19 FORM_TEXT = 'text'
@@ -37,10 +34,9 b" PARAMETER_MAX_FILES = 'max_files'"
37 TEMPLATE = 'boards/all_threads.html'
34 TEMPLATE = 'boards/all_threads.html'
38 DEFAULT_PAGE = 1
35 DEFAULT_PAGE = 1
39
36
40 FORM_TAGS = 'tags'
41
37
42
38 class AllThreadsView(FileUploadMixin, BaseBoardView, PaginatedMixin,
43 class AllThreadsView(PostMixin, FileUploadMixin, BaseBoardView, PaginatedMixin, DispatcherMixin):
39 DispatcherMixin):
44
40
45 tag_name = ''
41 tag_name = ''
46
42
@@ -103,7 +99,7 b' class AllThreadsView(PostMixin, FileUplo'
103 form.session = request.session
99 form.session = request.session
104
100
105 if form.is_valid():
101 if form.is_valid():
106 return self.create_thread(request, form)
102 return Post.objects.create_from_form(request, form, None)
107 if form.need_to_ban:
103 if form.need_to_ban:
108 # Ban user because he is suspected to be a bot
104 # Ban user because he is suspected to be a bot
109 self._ban_current_user(request)
105 self._ban_current_user(request)
@@ -113,48 +109,6 b' class AllThreadsView(PostMixin, FileUplo'
113 def get_reverse_url(self):
109 def get_reverse_url(self):
114 return reverse('index')
110 return reverse('index')
115
111
116 @transaction.atomic
117 def create_thread(self, request, form: ThreadForm, html_response=True):
118 """
119 Creates a new thread with an opening post.
120 """
121
122 ip = utils.get_client_ip(request)
123 is_banned = Ban.objects.filter(ip=ip).exists()
124
125 if is_banned:
126 if html_response:
127 return redirect(BannedView().as_view())
128 else:
129 return
130
131 data = form.cleaned_data
132
133 title = form.get_title()
134 text = data[FORM_TEXT]
135 files = form.get_files()
136 file_urls = form.get_file_urls()
137 images = form.get_images()
138
139 text = self._remove_invalid_links(text)
140
141 tags = data[FORM_TAGS]
142 monochrome = form.is_monochrome()
143 stickerpack = form.is_stickerpack()
144
145 post = Post.objects.create_post(title=title, text=text, files=files,
146 ip=ip, tags=tags,
147 tripcode=form.get_tripcode(),
148 monochrome=monochrome, images=images,
149 file_urls=file_urls, stickerpack=stickerpack)
150
151 if form.is_subscribe():
152 settings_manager = get_settings_manager(request)
153 settings_manager.add_or_read_fav_thread(post)
154
155 if html_response:
156 return redirect(post.get_absolute_url())
157
158 def get_threads(self):
112 def get_threads(self):
159 """
113 """
160 Gets list of threads that will be shown on a page.
114 Gets list of threads that will be shown on a page.
@@ -16,7 +16,6 b' from boards.models.attachment import Att'
16 from boards.models.thread import STATUS_ARCHIVE
16 from boards.models.thread import STATUS_ARCHIVE
17 from boards.models.user import Notification
17 from boards.models.user import Notification
18 from boards.utils import datetime_to_epoch
18 from boards.utils import datetime_to_epoch
19 from boards.views.thread import ThreadView
20
19
21 __author__ = 'neko259'
20 __author__ = 'neko259'
22
21
@@ -104,8 +103,8 b' def api_add_post(request, opening_post_i'
104 # _ban_current_user(request)
103 # _ban_current_user(request)
105 status = STATUS_ERROR
104 status = STATUS_ERROR
106 if form.is_valid():
105 if form.is_valid():
107 post = ThreadView().new_post(request, form, opening_post,
106 post = Post.objects.create_from_form(request, form, opening_post,
108 html_response=False)
107 html_response=False)
109 if not post:
108 if not post:
110 status = STATUS_ERROR
109 status = STATUS_ERROR
111 else:
110 else:
@@ -6,7 +6,6 b' from boards.abstracts.paginator import g'
6 from boards.abstracts.settingsmanager import get_settings_manager
6 from boards.abstracts.settingsmanager import get_settings_manager
7 from boards.models import Post
7 from boards.models import Post
8 from boards.views.base import BaseBoardView
8 from boards.views.base import BaseBoardView
9 from boards.views.posting_mixin import PostMixin
10 from boards.views.mixins import PaginatedMixin
9 from boards.views.mixins import PaginatedMixin
11
10
12 POSTS_PER_PAGE = settings.get_int('View', 'PostsPerPage')
11 POSTS_PER_PAGE = settings.get_int('View', 'PostsPerPage')
@@ -92,7 +91,7 b' class ImageFilter(FeedFilter):'
92 return 'File: {}'.format(image)
91 return 'File: {}'.format(image)
93
92
94
93
95 class FeedView(PostMixin, PaginatedMixin, BaseBoardView):
94 class FeedView(PaginatedMixin, BaseBoardView):
96 filters = (
95 filters = (
97 TripcodeFilter,
96 TripcodeFilter,
98 FavoritesFilter,
97 FavoritesFilter,
@@ -1,6 +1,5 b''
1 import boards
1 import boards
2
2
3
4 PARAM_NEXT = 'next'
3 PARAM_NEXT = 'next'
5 PARAMETER_METHOD = 'method'
4 PARAMETER_METHOD = 'method'
6
5
@@ -10,6 +9,7 b" PARAMETER_PAGINATOR = 'paginator'"
10 PARAMETER_PREV_LINK = 'prev_page_link'
9 PARAMETER_PREV_LINK = 'prev_page_link'
11 PARAMETER_NEXT_LINK = 'next_page_link'
10 PARAMETER_NEXT_LINK = 'next_page_link'
12
11
12
13 class DispatcherMixin:
13 class DispatcherMixin:
14 """
14 """
15 This class contains a dispather method that can run a method specified by
15 This class contains a dispather method that can run a method specified by
@@ -58,4 +58,3 b' class PaginatedMixin:'
58 current_page.next_page_number())
58 current_page.next_page_number())
59
59
60 return params
60 return params
61
@@ -3,7 +3,7 b' from django.urls import reverse'
3
3
4 from boards.abstracts.settingsmanager import get_settings_manager, \
4 from boards.abstracts.settingsmanager import get_settings_manager, \
5 SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS
5 SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS
6 from boards.models import Tag, TagAlias
6 from boards.models import Tag, TagAlias, Post
7 from boards.views.all_threads import AllThreadsView
7 from boards.views.all_threads import AllThreadsView
8 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
8 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
9 from boards.forms import ThreadForm, PlainErrorList
9 from boards.forms import ThreadForm, PlainErrorList
@@ -79,7 +79,7 b' class TagView(AllThreadsView, Dispatcher'
79 form.session = request.session
79 form.session = request.session
80
80
81 if form.is_valid():
81 if form.is_valid():
82 return self.create_thread(request, form)
82 return Post.objects.create_from_form(request, form, opening_post=None)
83 if form.need_to_ban:
83 if form.need_to_ban:
84 # Ban user because he is suspected to be a bot
84 # Ban user because he is suspected to be a bot
85 self._ban_current_user(request)
85 self._ban_current_user(request)
@@ -6,13 +6,11 b' from django.utils.decorators import meth'
6 from django.views.decorators.csrf import csrf_protect
6 from django.views.decorators.csrf import csrf_protect
7 from django.views.generic.edit import FormMixin
7 from django.views.generic.edit import FormMixin
8
8
9 from boards import utils
10 from boards.abstracts.settingsmanager import get_settings_manager
9 from boards.abstracts.settingsmanager import get_settings_manager
11 from boards.forms import PostForm, PlainErrorList
10 from boards.forms import PostForm, PlainErrorList
12 from boards.models import Post
11 from boards.models import Post
13 from boards.views.base import BaseBoardView, CONTEXT_FORM
12 from boards.views.base import BaseBoardView, CONTEXT_FORM
14 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
13 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
15 from boards.views.posting_mixin import PostMixin
16
14
17 REQ_POST_ID = 'post_id'
15 REQ_POST_ID = 'post_id'
18
16
@@ -23,13 +21,8 b" CONTEXT_OP = 'opening_post'"
23 CONTEXT_FAVORITE = 'is_favorite'
21 CONTEXT_FAVORITE = 'is_favorite'
24 CONTEXT_RSS_URL = 'rss_url'
22 CONTEXT_RSS_URL = 'rss_url'
25
23
26 FORM_TITLE = 'title'
27 FORM_TEXT = 'text'
28 FORM_IMAGE = 'image'
29 FORM_THREADS = 'threads'
30
24
31
25 class ThreadView(BaseBoardView, FormMixin, DispatcherMixin):
32 class ThreadView(BaseBoardView, PostMixin, FormMixin, DispatcherMixin):
33
26
34 @method_decorator(csrf_protect)
27 @method_decorator(csrf_protect)
35 def get(self, request, post_id, form: PostForm=None):
28 def get(self, request, post_id, form: PostForm=None):
@@ -87,49 +80,13 b' class ThreadView(BaseBoardView, PostMixi'
87 form.session = request.session
80 form.session = request.session
88
81
89 if form.is_valid():
82 if form.is_valid():
90 return self.new_post(request, form, opening_post)
83 return Post.objects.create_from_form(request, form, opening_post)
91 if form.need_to_ban:
84 if form.need_to_ban:
92 # Ban user because he is suspected to be a bot
85 # Ban user because he is suspected to be a bot
93 self._ban_current_user(request)
86 self._ban_current_user(request)
94
87
95 return self.get(request, post_id, form)
88 return self.get(request, post_id, form)
96
89
97 def new_post(self, request, form: PostForm, opening_post: Post=None,
98 html_response=True):
99 """
100 Adds a new post (in thread or as a reply).
101 """
102
103 ip = utils.get_client_ip(request)
104
105 data = form.cleaned_data
106
107 title = form.get_title()
108 text = data[FORM_TEXT]
109 files = form.get_files()
110 file_urls = form.get_file_urls()
111 images = form.get_images()
112
113 text = self._remove_invalid_links(text)
114
115 post_thread = opening_post.get_thread()
116
117 post = Post.objects.create_post(title=title, text=text, files=files,
118 thread=post_thread, ip=ip,
119 tripcode=form.get_tripcode(),
120 images=images, file_urls=file_urls)
121
122 if form.is_subscribe():
123 settings_manager = get_settings_manager(request)
124 settings_manager.add_or_read_fav_thread(
125 post_thread.get_opening_post())
126
127 if html_response:
128 if opening_post:
129 return redirect(post.get_absolute_url())
130 else:
131 return post
132
133 def get_data(self, thread) -> dict:
90 def get_data(self, thread) -> dict:
134 """
91 """
135 Returns context params for the view.
92 Returns context params for the view.
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now