Show More
@@ -2,3 +2,4 b' import re' | |||
|
2 | 2 | |
|
3 | 3 | FILE_DIRECTORY = 'files/' |
|
4 | 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 | 8 | from django.template.loader import render_to_string |
|
9 | 9 | from django.urls import reverse |
|
10 | 10 | |
|
11 | from boards.abstracts.constants import REGEX_REPLY | |
|
11 | 12 | from boards.abstracts.tripcode import Tripcode |
|
12 | 13 | from boards.models import Attachment, KeyPair, GlobalId |
|
13 | 14 | from boards.models.attachment import FILE_TYPES_IMAGE |
@@ -30,7 +31,6 b" BAN_REASON_AUTO = 'Auto'" | |||
|
30 | 31 | |
|
31 | 32 | TITLE_MAX_LENGTH = 200 |
|
32 | 33 | |
|
33 | REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]') | |
|
34 | 34 | REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]') |
|
35 | 35 | REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?') |
|
36 | 36 | REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]') |
@@ -1,16 +1,18 b'' | |||
|
1 | 1 | import logging |
|
2 | import re | |
|
2 | 3 | from datetime import datetime, timedelta, date |
|
3 | 4 | from datetime import time as dtime |
|
4 | 5 | |
|
5 | 6 | from django.core.exceptions import PermissionDenied |
|
6 | 7 | from django.db import models, transaction |
|
7 | 8 | from django.dispatch import Signal |
|
9 | from django.shortcuts import redirect | |
|
8 | 10 | from django.utils import timezone |
|
9 | 11 | |
|
10 | 12 | import boards |
|
11 | 13 | from boards import utils |
|
12 | 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 | 16 | from boards.mdx_neboard import Parser |
|
15 | 17 | from boards.models import Attachment |
|
16 | 18 | from boards.models.attachment import StickerPack, AttachmentSticker |
@@ -24,6 +26,11 b" NO_IP = '0.0.0.0'" | |||
|
24 | 26 | |
|
25 | 27 | post_import_deps = Signal() |
|
26 | 28 | |
|
29 | FORM_TEXT = 'text' | |
|
30 | FORM_TAGS = 'tags' | |
|
31 | ||
|
32 | REFLINK_PREFIX = '>>' | |
|
33 | ||
|
27 | 34 | |
|
28 | 35 | class PostManager(models.Manager): |
|
29 | 36 | @transaction.atomic |
@@ -190,6 +197,48 b' class PostManager(models.Manager):' | |||
|
190 | 197 | thread.tags.clear() |
|
191 | 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 | 242 | def _add_file_to_post(self, file, post): |
|
194 | 243 | post.attachments.add(Attachment.objects.create_with_hash(file)) |
|
195 | 244 | |
@@ -226,3 +275,17 b' class PostManager(models.Manager):' | |||
|
226 | 275 | name=post.get_title(), tripcode=post.tripcode) |
|
227 | 276 | if created: |
|
228 | 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 | 13 | from boards.models import Post, GlobalId, Attachment, Thread |
|
14 | 14 | from boards.models.attachment import StickerPack, AttachmentSticker |
|
15 | 15 | from boards.models.attachment.viewers import FILE_TYPES_IMAGE |
|
16 |
from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY |
|
|
17 | REGEX_GLOBAL_REPLY | |
|
16 | from boards.models.post import REGEX_NOTIFICATION, REGEX_GLOBAL_REPLY | |
|
17 | from boards.abstracts.constants import REGEX_REPLY | |
|
18 | 18 | from boards.models.post.manager import post_import_deps |
|
19 | 19 | from boards.models.user import Notification |
|
20 | 20 | from neboard.settings import MEDIA_ROOT |
@@ -4,7 +4,7 b' from django.views.i18n import JavaScript' | |||
|
4 | 4 | |
|
5 | 5 | from boards import views |
|
6 | 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 | 8 | from boards.views.authors import AuthorsView |
|
9 | 9 | from boards.views.landing import LandingView |
|
10 | 10 | from boards.views.notifications import NotificationView |
@@ -1,22 +1,19 b'' | |||
|
1 | 1 | from django.core.paginator import EmptyPage |
|
2 | from django.db import transaction | |
|
3 | 2 | from django.http import Http404 |
|
4 | 3 | from django.shortcuts import render, redirect |
|
5 | 4 | from django.urls import reverse |
|
6 | 5 | from django.utils.decorators import method_decorator |
|
7 | 6 | from django.views.decorators.csrf import csrf_protect |
|
8 | 7 | |
|
9 |
from boards import |
|
|
8 | from boards import settings | |
|
10 | 9 | from boards.abstracts.paginator import get_paginator |
|
11 | 10 | from boards.abstracts.settingsmanager import get_settings_manager, \ |
|
12 | 11 | SETTING_ONLY_FAVORITES |
|
13 | 12 | from boards.forms import ThreadForm, PlainErrorList |
|
14 |
from boards.models import Post, Thread |
|
|
15 | from boards.views.banned import BannedView | |
|
13 | from boards.models import Post, Thread | |
|
16 | 14 | from boards.views.base import BaseBoardView, CONTEXT_FORM |
|
17 | 15 | from boards.views.mixins import FileUploadMixin, PaginatedMixin, \ |
|
18 | 16 | DispatcherMixin, PARAMETER_METHOD |
|
19 | from boards.views.posting_mixin import PostMixin | |
|
20 | 17 | |
|
21 | 18 | FORM_TAGS = 'tags' |
|
22 | 19 | FORM_TEXT = 'text' |
@@ -37,10 +34,9 b" PARAMETER_MAX_FILES = 'max_files'" | |||
|
37 | 34 | TEMPLATE = 'boards/all_threads.html' |
|
38 | 35 | DEFAULT_PAGE = 1 |
|
39 | 36 | |
|
40 | FORM_TAGS = 'tags' | |
|
41 | 37 | |
|
42 | ||
|
43 | class AllThreadsView(PostMixin, FileUploadMixin, BaseBoardView, PaginatedMixin, DispatcherMixin): | |
|
38 | class AllThreadsView(FileUploadMixin, BaseBoardView, PaginatedMixin, | |
|
39 | DispatcherMixin): | |
|
44 | 40 | |
|
45 | 41 | tag_name = '' |
|
46 | 42 | |
@@ -103,7 +99,7 b' class AllThreadsView(PostMixin, FileUplo' | |||
|
103 | 99 | form.session = request.session |
|
104 | 100 | |
|
105 | 101 | if form.is_valid(): |
|
106 |
return |
|
|
102 | return Post.objects.create_from_form(request, form, None) | |
|
107 | 103 | if form.need_to_ban: |
|
108 | 104 | # Ban user because he is suspected to be a bot |
|
109 | 105 | self._ban_current_user(request) |
@@ -113,48 +109,6 b' class AllThreadsView(PostMixin, FileUplo' | |||
|
113 | 109 | def get_reverse_url(self): |
|
114 | 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 | 112 | def get_threads(self): |
|
159 | 113 | """ |
|
160 | 114 | Gets list of threads that will be shown on a page. |
@@ -16,7 +16,6 b' from boards.models.attachment import Att' | |||
|
16 | 16 | from boards.models.thread import STATUS_ARCHIVE |
|
17 | 17 | from boards.models.user import Notification |
|
18 | 18 | from boards.utils import datetime_to_epoch |
|
19 | from boards.views.thread import ThreadView | |
|
20 | 19 | |
|
21 | 20 | __author__ = 'neko259' |
|
22 | 21 | |
@@ -104,7 +103,7 b' def api_add_post(request, opening_post_i' | |||
|
104 | 103 | # _ban_current_user(request) |
|
105 | 104 | status = STATUS_ERROR |
|
106 | 105 | if form.is_valid(): |
|
107 |
post = |
|
|
106 | post = Post.objects.create_from_form(request, form, opening_post, | |
|
108 | 107 | html_response=False) |
|
109 | 108 | if not post: |
|
110 | 109 | status = STATUS_ERROR |
@@ -6,7 +6,6 b' from boards.abstracts.paginator import g' | |||
|
6 | 6 | from boards.abstracts.settingsmanager import get_settings_manager |
|
7 | 7 | from boards.models import Post |
|
8 | 8 | from boards.views.base import BaseBoardView |
|
9 | from boards.views.posting_mixin import PostMixin | |
|
10 | 9 | from boards.views.mixins import PaginatedMixin |
|
11 | 10 | |
|
12 | 11 | POSTS_PER_PAGE = settings.get_int('View', 'PostsPerPage') |
@@ -92,7 +91,7 b' class ImageFilter(FeedFilter):' | |||
|
92 | 91 | return 'File: {}'.format(image) |
|
93 | 92 | |
|
94 | 93 | |
|
95 |
class FeedView( |
|
|
94 | class FeedView(PaginatedMixin, BaseBoardView): | |
|
96 | 95 | filters = ( |
|
97 | 96 | TripcodeFilter, |
|
98 | 97 | FavoritesFilter, |
@@ -1,6 +1,5 b'' | |||
|
1 | 1 | import boards |
|
2 | 2 | |
|
3 | ||
|
4 | 3 | PARAM_NEXT = 'next' |
|
5 | 4 | PARAMETER_METHOD = 'method' |
|
6 | 5 | |
@@ -10,6 +9,7 b" PARAMETER_PAGINATOR = 'paginator'" | |||
|
10 | 9 | PARAMETER_PREV_LINK = 'prev_page_link' |
|
11 | 10 | PARAMETER_NEXT_LINK = 'next_page_link' |
|
12 | 11 | |
|
12 | ||
|
13 | 13 | class DispatcherMixin: |
|
14 | 14 | """ |
|
15 | 15 | This class contains a dispather method that can run a method specified by |
@@ -58,4 +58,3 b' class PaginatedMixin:' | |||
|
58 | 58 | current_page.next_page_number()) |
|
59 | 59 | |
|
60 | 60 | return params |
|
61 |
@@ -3,7 +3,7 b' from django.urls import reverse' | |||
|
3 | 3 | |
|
4 | 4 | from boards.abstracts.settingsmanager import get_settings_manager, \ |
|
5 | 5 | SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS |
|
6 | from boards.models import Tag, TagAlias | |
|
6 | from boards.models import Tag, TagAlias, Post | |
|
7 | 7 | from boards.views.all_threads import AllThreadsView |
|
8 | 8 | from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD |
|
9 | 9 | from boards.forms import ThreadForm, PlainErrorList |
@@ -79,7 +79,7 b' class TagView(AllThreadsView, Dispatcher' | |||
|
79 | 79 | form.session = request.session |
|
80 | 80 | |
|
81 | 81 | if form.is_valid(): |
|
82 |
return |
|
|
82 | return Post.objects.create_from_form(request, form, opening_post=None) | |
|
83 | 83 | if form.need_to_ban: |
|
84 | 84 | # Ban user because he is suspected to be a bot |
|
85 | 85 | self._ban_current_user(request) |
@@ -6,13 +6,11 b' from django.utils.decorators import meth' | |||
|
6 | 6 | from django.views.decorators.csrf import csrf_protect |
|
7 | 7 | from django.views.generic.edit import FormMixin |
|
8 | 8 | |
|
9 | from boards import utils | |
|
10 | 9 | from boards.abstracts.settingsmanager import get_settings_manager |
|
11 | 10 | from boards.forms import PostForm, PlainErrorList |
|
12 | 11 | from boards.models import Post |
|
13 | 12 | from boards.views.base import BaseBoardView, CONTEXT_FORM |
|
14 | 13 | from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD |
|
15 | from boards.views.posting_mixin import PostMixin | |
|
16 | 14 | |
|
17 | 15 | REQ_POST_ID = 'post_id' |
|
18 | 16 | |
@@ -23,13 +21,8 b" CONTEXT_OP = 'opening_post'" | |||
|
23 | 21 | CONTEXT_FAVORITE = 'is_favorite' |
|
24 | 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 | ||
|
32 | class ThreadView(BaseBoardView, PostMixin, FormMixin, DispatcherMixin): | |
|
25 | class ThreadView(BaseBoardView, FormMixin, DispatcherMixin): | |
|
33 | 26 | |
|
34 | 27 | @method_decorator(csrf_protect) |
|
35 | 28 | def get(self, request, post_id, form: PostForm=None): |
@@ -87,49 +80,13 b' class ThreadView(BaseBoardView, PostMixi' | |||
|
87 | 80 | form.session = request.session |
|
88 | 81 | |
|
89 | 82 | if form.is_valid(): |
|
90 |
return |
|
|
83 | return Post.objects.create_from_form(request, form, opening_post) | |
|
91 | 84 | if form.need_to_ban: |
|
92 | 85 | # Ban user because he is suspected to be a bot |
|
93 | 86 | self._ban_current_user(request) |
|
94 | 87 | |
|
95 | 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 | 90 | def get_data(self, thread) -> dict: |
|
134 | 91 | """ |
|
135 | 92 | Returns context params for the view. |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now