diff --git a/boards/abstracts/constants.py b/boards/abstracts/constants.py --- a/boards/abstracts/constants.py +++ b/boards/abstracts/constants.py @@ -2,3 +2,4 @@ import re FILE_DIRECTORY = 'files/' REGEX_TAGS = re.compile(r'^[\w\s\d\']+$', re.UNICODE) +REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]') \ No newline at end of file diff --git a/boards/models/post/__init__.py b/boards/models/post/__init__.py --- a/boards/models/post/__init__.py +++ b/boards/models/post/__init__.py @@ -8,6 +8,7 @@ from django.template.defaultfilters impo from django.template.loader import render_to_string from django.urls import reverse +from boards.abstracts.constants import REGEX_REPLY from boards.abstracts.tripcode import Tripcode from boards.models import Attachment, KeyPair, GlobalId from boards.models.attachment import FILE_TYPES_IMAGE @@ -30,7 +31,6 @@ BAN_REASON_AUTO = 'Auto' TITLE_MAX_LENGTH = 200 -REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]') REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]') REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?') REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]') diff --git a/boards/models/post/manager.py b/boards/models/post/manager.py --- a/boards/models/post/manager.py +++ b/boards/models/post/manager.py @@ -1,16 +1,18 @@ import logging +import re from datetime import datetime, timedelta, date from datetime import time as dtime from django.core.exceptions import PermissionDenied from django.db import models, transaction from django.dispatch import Signal +from django.shortcuts import redirect from django.utils import timezone import boards from boards import utils from boards.abstracts.exceptions import ArchiveException -from boards.abstracts.constants import REGEX_TAGS +from boards.abstracts.constants import REGEX_TAGS, REGEX_REPLY from boards.mdx_neboard import Parser from boards.models import Attachment from boards.models.attachment import StickerPack, AttachmentSticker @@ -24,6 +26,11 @@ NO_IP = '0.0.0.0' post_import_deps = Signal() +FORM_TEXT = 'text' +FORM_TAGS = 'tags' + +REFLINK_PREFIX = '>>' + class PostManager(models.Manager): @transaction.atomic @@ -190,6 +197,48 @@ class PostManager(models.Manager): thread.tags.clear() list(map(thread.tags.add, tags)) + def create_from_form(self, request, form, opening_post, html_response=True): + ip = utils.get_client_ip(request) + + data = form.cleaned_data + + title = form.get_title() + text = data[FORM_TEXT] + files = form.get_files() + file_urls = form.get_file_urls() + images = form.get_images() + + text = self._remove_invalid_links(text) + + if opening_post: + post_thread = opening_post.get_thread() + monochrome = False + stickerpack = False + tags = [] + else: + tags = data[FORM_TAGS] + monochrome = form.is_monochrome() + stickerpack = form.is_stickerpack() + post_thread = None + + post = self.create_post(title=title, text=text, files=files, + thread=post_thread, ip=ip, + tripcode=form.get_tripcode(), + images=images, file_urls=file_urls, + monochrome=monochrome, + stickerpack=stickerpack, tags=tags) + + if form.is_subscribe(): + from boards.abstracts.settingsmanager import get_settings_manager + settings_manager = get_settings_manager(request) + settings_manager.add_or_read_fav_thread( + post_thread.get_opening_post()) + + if html_response: + return redirect(post.get_absolute_url()) + else: + return post + def _add_file_to_post(self, file, post): post.attachments.add(Attachment.objects.create_with_hash(file)) @@ -226,3 +275,17 @@ class PostManager(models.Manager): name=post.get_title(), tripcode=post.tripcode) if created: logger.info('Created stickerpack {}'.format(stickerpack)) + + def _remove_invalid_links(self, text): + """ + Replace invalid links in posts so that they won't be parsed. + Invalid links are links to non-existent posts + """ + + for reply_number in re.finditer(REGEX_REPLY, text): + post_id = reply_number.group(1) + post = self.filter(id=post_id) + if not post.exists(): + text = text.replace(REFLINK_PREFIX + post_id, post_id) + + return text diff --git a/boards/signals.py b/boards/signals.py --- a/boards/signals.py +++ b/boards/signals.py @@ -13,8 +13,8 @@ from boards.mdx_neboard import get_parse from boards.models import Post, GlobalId, Attachment, Thread from boards.models.attachment import StickerPack, AttachmentSticker from boards.models.attachment.viewers import FILE_TYPES_IMAGE -from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY,\ - REGEX_GLOBAL_REPLY +from boards.models.post import REGEX_NOTIFICATION, REGEX_GLOBAL_REPLY +from boards.abstracts.constants import REGEX_REPLY from boards.models.post.manager import post_import_deps from boards.models.user import Notification from neboard.settings import MEDIA_ROOT diff --git a/boards/urls.py b/boards/urls.py --- a/boards/urls.py +++ b/boards/urls.py @@ -4,7 +4,7 @@ from django.views.i18n import JavaScript from boards import views from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed -from boards.views import api, tag_threads, all_threads, settings, feed, stickers +from boards.views import api, tag_threads, all_threads, settings, feed, stickers, thread, banned from boards.views.authors import AuthorsView from boards.views.landing import LandingView from boards.views.notifications import NotificationView diff --git a/boards/views/all_threads.py b/boards/views/all_threads.py --- a/boards/views/all_threads.py +++ b/boards/views/all_threads.py @@ -1,22 +1,19 @@ from django.core.paginator import EmptyPage -from django.db import transaction from django.http import Http404 from django.shortcuts import render, redirect from django.urls import reverse from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_protect -from boards import utils, settings +from boards import settings from boards.abstracts.paginator import get_paginator from boards.abstracts.settingsmanager import get_settings_manager, \ SETTING_ONLY_FAVORITES from boards.forms import ThreadForm, PlainErrorList -from boards.models import Post, Thread, Ban -from boards.views.banned import BannedView +from boards.models import Post, Thread from boards.views.base import BaseBoardView, CONTEXT_FORM from boards.views.mixins import FileUploadMixin, PaginatedMixin, \ DispatcherMixin, PARAMETER_METHOD -from boards.views.posting_mixin import PostMixin FORM_TAGS = 'tags' FORM_TEXT = 'text' @@ -37,10 +34,9 @@ PARAMETER_MAX_FILES = 'max_files' TEMPLATE = 'boards/all_threads.html' DEFAULT_PAGE = 1 -FORM_TAGS = 'tags' - -class AllThreadsView(PostMixin, FileUploadMixin, BaseBoardView, PaginatedMixin, DispatcherMixin): +class AllThreadsView(FileUploadMixin, BaseBoardView, PaginatedMixin, + DispatcherMixin): tag_name = '' @@ -103,7 +99,7 @@ class AllThreadsView(PostMixin, FileUplo form.session = request.session if form.is_valid(): - return self.create_thread(request, form) + return Post.objects.create_from_form(request, form, None) if form.need_to_ban: # Ban user because he is suspected to be a bot self._ban_current_user(request) @@ -113,48 +109,6 @@ class AllThreadsView(PostMixin, FileUplo def get_reverse_url(self): return reverse('index') - @transaction.atomic - def create_thread(self, request, form: ThreadForm, html_response=True): - """ - Creates a new thread with an opening post. - """ - - ip = utils.get_client_ip(request) - is_banned = Ban.objects.filter(ip=ip).exists() - - if is_banned: - if html_response: - return redirect(BannedView().as_view()) - else: - return - - data = form.cleaned_data - - title = form.get_title() - text = data[FORM_TEXT] - files = form.get_files() - file_urls = form.get_file_urls() - images = form.get_images() - - text = self._remove_invalid_links(text) - - tags = data[FORM_TAGS] - monochrome = form.is_monochrome() - stickerpack = form.is_stickerpack() - - post = Post.objects.create_post(title=title, text=text, files=files, - ip=ip, tags=tags, - tripcode=form.get_tripcode(), - monochrome=monochrome, images=images, - file_urls=file_urls, stickerpack=stickerpack) - - if form.is_subscribe(): - settings_manager = get_settings_manager(request) - settings_manager.add_or_read_fav_thread(post) - - if html_response: - return redirect(post.get_absolute_url()) - def get_threads(self): """ Gets list of threads that will be shown on a page. diff --git a/boards/views/api.py b/boards/views/api.py --- a/boards/views/api.py +++ b/boards/views/api.py @@ -16,7 +16,6 @@ from boards.models.attachment import Att from boards.models.thread import STATUS_ARCHIVE from boards.models.user import Notification from boards.utils import datetime_to_epoch -from boards.views.thread import ThreadView __author__ = 'neko259' @@ -104,8 +103,8 @@ def api_add_post(request, opening_post_i # _ban_current_user(request) status = STATUS_ERROR if form.is_valid(): - post = ThreadView().new_post(request, form, opening_post, - html_response=False) + post = Post.objects.create_from_form(request, form, opening_post, + html_response=False) if not post: status = STATUS_ERROR else: diff --git a/boards/views/feed.py b/boards/views/feed.py --- a/boards/views/feed.py +++ b/boards/views/feed.py @@ -6,7 +6,6 @@ from boards.abstracts.paginator import g from boards.abstracts.settingsmanager import get_settings_manager from boards.models import Post from boards.views.base import BaseBoardView -from boards.views.posting_mixin import PostMixin from boards.views.mixins import PaginatedMixin POSTS_PER_PAGE = settings.get_int('View', 'PostsPerPage') @@ -92,7 +91,7 @@ class ImageFilter(FeedFilter): return 'File: {}'.format(image) -class FeedView(PostMixin, PaginatedMixin, BaseBoardView): +class FeedView(PaginatedMixin, BaseBoardView): filters = ( TripcodeFilter, FavoritesFilter, diff --git a/boards/views/mixins.py b/boards/views/mixins.py --- a/boards/views/mixins.py +++ b/boards/views/mixins.py @@ -1,6 +1,5 @@ import boards - PARAM_NEXT = 'next' PARAMETER_METHOD = 'method' @@ -10,6 +9,7 @@ PARAMETER_PAGINATOR = 'paginator' PARAMETER_PREV_LINK = 'prev_page_link' PARAMETER_NEXT_LINK = 'next_page_link' + class DispatcherMixin: """ This class contains a dispather method that can run a method specified by @@ -58,4 +58,3 @@ class PaginatedMixin: current_page.next_page_number()) return params - diff --git a/boards/views/posting_mixin.py b/boards/views/posting_mixin.py deleted file mode 100644 --- a/boards/views/posting_mixin.py +++ /dev/null @@ -1,24 +0,0 @@ -import re - -from boards.models import Post -from boards.models.post import REGEX_REPLY - -REFLINK_PREFIX = '>>' - - -class PostMixin: - - @staticmethod - def _remove_invalid_links(text): - """ - Replace invalid links in posts so that they won't be parsed. - Invalid links are links to non-existent posts - """ - - for reply_number in re.finditer(REGEX_REPLY, text): - post_id = reply_number.group(1) - post = Post.objects.filter(id=post_id) - if not post.exists(): - text = text.replace(REFLINK_PREFIX + post_id, post_id) - - return text diff --git a/boards/views/tag_threads.py b/boards/views/tag_threads.py --- a/boards/views/tag_threads.py +++ b/boards/views/tag_threads.py @@ -3,7 +3,7 @@ from django.urls import reverse from boards.abstracts.settingsmanager import get_settings_manager, \ SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS -from boards.models import Tag, TagAlias +from boards.models import Tag, TagAlias, Post from boards.views.all_threads import AllThreadsView from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD from boards.forms import ThreadForm, PlainErrorList @@ -79,7 +79,7 @@ class TagView(AllThreadsView, Dispatcher form.session = request.session if form.is_valid(): - return self.create_thread(request, form) + return Post.objects.create_from_form(request, form, opening_post=None) if form.need_to_ban: # Ban user because he is suspected to be a bot self._ban_current_user(request) diff --git a/boards/views/thread/thread.py b/boards/views/thread/thread.py --- a/boards/views/thread/thread.py +++ b/boards/views/thread/thread.py @@ -6,13 +6,11 @@ from django.utils.decorators import meth from django.views.decorators.csrf import csrf_protect from django.views.generic.edit import FormMixin -from boards import utils from boards.abstracts.settingsmanager import get_settings_manager from boards.forms import PostForm, PlainErrorList from boards.models import Post from boards.views.base import BaseBoardView, CONTEXT_FORM from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD -from boards.views.posting_mixin import PostMixin REQ_POST_ID = 'post_id' @@ -23,13 +21,8 @@ CONTEXT_OP = 'opening_post' CONTEXT_FAVORITE = 'is_favorite' CONTEXT_RSS_URL = 'rss_url' -FORM_TITLE = 'title' -FORM_TEXT = 'text' -FORM_IMAGE = 'image' -FORM_THREADS = 'threads' - -class ThreadView(BaseBoardView, PostMixin, FormMixin, DispatcherMixin): +class ThreadView(BaseBoardView, FormMixin, DispatcherMixin): @method_decorator(csrf_protect) def get(self, request, post_id, form: PostForm=None): @@ -87,49 +80,13 @@ class ThreadView(BaseBoardView, PostMixi form.session = request.session if form.is_valid(): - return self.new_post(request, form, opening_post) + return Post.objects.create_from_form(request, form, opening_post) if form.need_to_ban: # Ban user because he is suspected to be a bot self._ban_current_user(request) return self.get(request, post_id, form) - def new_post(self, request, form: PostForm, opening_post: Post=None, - html_response=True): - """ - Adds a new post (in thread or as a reply). - """ - - ip = utils.get_client_ip(request) - - data = form.cleaned_data - - title = form.get_title() - text = data[FORM_TEXT] - files = form.get_files() - file_urls = form.get_file_urls() - images = form.get_images() - - text = self._remove_invalid_links(text) - - post_thread = opening_post.get_thread() - - post = Post.objects.create_post(title=title, text=text, files=files, - thread=post_thread, ip=ip, - tripcode=form.get_tripcode(), - images=images, file_urls=file_urls) - - if form.is_subscribe(): - settings_manager = get_settings_manager(request) - settings_manager.add_or_read_fav_thread( - post_thread.get_opening_post()) - - if html_response: - if opening_post: - return redirect(post.get_absolute_url()) - else: - return post - def get_data(self, thread) -> dict: """ Returns context params for the view.