diff --git a/boards/forms.py b/boards/forms.py --- a/boards/forms.py +++ b/boards/forms.py @@ -4,13 +4,14 @@ import pytz from django import forms from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.exceptions import ObjectDoesNotExist from django.forms.util import ErrorList from django.utils.translation import ugettext_lazy as _ import requests from boards.mdx_neboard import formatters from boards.models.post import TITLE_MAX_LENGTH -from boards.models import Tag +from boards.models import Tag, Post from neboard import settings import boards.settings as board_settings @@ -148,6 +149,9 @@ class PostForm(NeboardForm): email = forms.CharField(max_length=100, required=False, label=_('e-mail'), widget=forms.TextInput(attrs={ 'class': 'form-email'})) + threads = forms.CharField(required=False, label=_('Additional threads'), + widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: + '123 456 789'})) session = None need_to_ban = False @@ -193,6 +197,25 @@ class PostForm(NeboardForm): return image + def clean_threads(self): + threads_str = self.cleaned_data['threads'] + + if len(threads_str) > 0: + threads_id_list = threads_str.split(' ') + + threads = list() + + for thread_id in threads_id_list: + try: + thread = Post.objects.get(id=int(thread_id)) + if not thread.is_opening(): + raise ObjectDoesNotExist() + threads.append(thread) + except (ObjectDoesNotExist, ValueError): + raise forms.ValidationError(_('Invalid additional thread list')) + + return threads + def clean(self): cleaned_data = super(PostForm, self).clean() diff --git a/boards/locale/ru/LC_MESSAGES/django.mo b/boards/locale/ru/LC_MESSAGES/django.mo index a2bde82e2a0f6f72f4ac7560905901da4f9e14b7..e37b969700cf0977b990809d84beac1619b36d43 GIT binary patch literal 7000 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 diff --git a/boards/locale/ru/LC_MESSAGES/django.po b/boards/locale/ru/LC_MESSAGES/django.po --- a/boards/locale/ru/LC_MESSAGES/django.po +++ b/boards/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-04-01 18:05+0300\n" +"POT-Creation-Date: 2015-04-07 15:26+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -38,97 +38,106 @@ msgstr "разработчик javascript" msgid "designer" msgstr "дизайнер" -#: forms.py:34 +#: forms.py:35 msgid "Type message here. Use formatting panel for more advanced usage." msgstr "" "Вводите сообщение сюда. Используйте панель для более сложного форматирования." -#: forms.py:35 +#: forms.py:36 msgid "tag1 several_words_tag" msgstr "метка1 метка_из_нескольких_слов" -#: forms.py:37 +#: forms.py:38 msgid "Title" msgstr "Заголовок" -#: forms.py:38 +#: forms.py:39 msgid "Text" msgstr "Текст" -#: forms.py:39 +#: forms.py:40 msgid "Tag" msgstr "Метка" -#: forms.py:40 templates/boards/base.html:36 templates/search/search.html:13 -#: templates/search/search.html.py:17 +#: forms.py:41 templates/boards/base.html:36 templates/search/search.html:7 msgid "Search" msgstr "Поиск" -#: forms.py:139 +#: forms.py:140 msgid "Image" msgstr "Изображение" -#: forms.py:142 +#: forms.py:143 msgid "Image URL" msgstr "URL изображения" -#: forms.py:148 +#: forms.py:149 msgid "e-mail" msgstr "" -#: forms.py:159 +#: forms.py:152 +#| msgid "All threads" +msgid "Additional threads" +msgstr "Дополнительные темы" + +#: forms.py:163 #, python-format msgid "Title must have less than %s characters" msgstr "Заголовок должен иметь меньше %s символов" -#: forms.py:168 +#: forms.py:172 #, python-format msgid "Text must have less than %s characters" msgstr "Текст должен быть короче %s символов" -#: forms.py:190 +#: forms.py:194 msgid "Invalid URL" msgstr "Неверный URL" -#: forms.py:227 +#: forms.py:214 +msgid "Invalid additional thread list" +msgstr "Неверный список дополнительных тем" + +#: forms.py:249 msgid "Either text or image must be entered." msgstr "Текст или картинка должны быть введены." -#: forms.py:243 +#: forms.py:265 #, python-format msgid "Wait %s seconds after last posting" msgstr "Подождите %s секунд после последнего постинга" -#: forms.py:255 +#: forms.py:277 #, python-format msgid "Image must be less than %s bytes" msgstr "Изображение должно быть менее %s байт" -#: forms.py:302 templates/boards/rss/post.html:10 templates/boards/tags.html:7 +#: forms.py:324 templates/boards/posting_general.html:148 +#: templates/boards/rss/post.html:10 templates/boards/tags.html:7 msgid "Tags" msgstr "Метки" -#: forms.py:309 +#: forms.py:331 msgid "Inappropriate characters in tags." msgstr "Недопустимые символы в метках." -#: forms.py:320 +#: forms.py:342 msgid "Need at least 1 required tag." msgstr "Нужна хотя бы 1 обязательная метка." -#: forms.py:332 +#: forms.py:354 msgid "Theme" msgstr "Тема" -#: forms.py:333 +#: forms.py:355 msgid "User name" msgstr "Имя пользователя" -#: forms.py:334 +#: forms.py:356 msgid "Time zone" msgstr "Часовой пояс" -#: forms.py:340 +#: forms.py:362 msgid "Inappropriate characters." msgstr "Недопустимые символы." @@ -265,11 +274,11 @@ msgstr "" msgid "Text syntax" msgstr "Синтаксис текста" -#: templates/boards/posting_general.html:157 +#: templates/boards/posting_general.html:161 msgid "Pages:" msgstr "Страницы: " -#: templates/boards/preview.html:6 templates/boards/staticpages/help.html:21 +#: templates/boards/preview.html:6 templates/boards/staticpages/help.html:20 msgid "Preview" msgstr "Предпросмотр" @@ -323,23 +332,19 @@ msgid "Link to a post" msgstr "Ссылка на сообщение" #: templates/boards/staticpages/help.html:15 -msgid "Add post to this thread" -msgstr "Добавить сообщение в эту тему" - -#: templates/boards/staticpages/help.html:16 msgid "Strikethrough text" msgstr "Зачеркнутый текст" -#: templates/boards/staticpages/help.html:17 +#: templates/boards/staticpages/help.html:16 msgid "Comment" msgstr "Комментарий" +#: templates/boards/staticpages/help.html:17 #: templates/boards/staticpages/help.html:18 -#: templates/boards/staticpages/help.html:19 msgid "Quote" msgstr "Цитата" -#: templates/boards/staticpages/help.html:21 +#: templates/boards/staticpages/help.html:20 msgid "You can try pasting the text and previewing the result here:" msgstr "Вы можете попробовать вставить текст и проверить результат здесь:" @@ -384,3 +389,8 @@ msgstr "Закрыть форму" #: templates/boards/thread_normal.html:68 msgid "Update" msgstr "Обновить" + +#: templates/search/search.html:17 +msgid "Ok" +msgstr "" + diff --git a/boards/mdx_neboard.py b/boards/mdx_neboard.py --- a/boards/mdx_neboard.py +++ b/boards/mdx_neboard.py @@ -137,23 +137,6 @@ def render_reflink(tag_name, value, opti return result -def render_multithread(tag_name, value, options, parent, context): - result = '>>>%s' % value - - if REFLINK_PATTERN.match(value): - post_id = int(value) - - try: - post = boards.models.Post.objects.get(id=post_id) - - if post.is_opening(): - result = '>>>%s' % (post.get_url(), post_id) - except ObjectDoesNotExist: - pass - - return result - - def render_quote(tag_name, value, options, parent, context): source = '' if 'source' in options: @@ -189,7 +172,6 @@ formatters = [ PREPARSE_PATTERNS = { - r'>>>(\d+)': r'[thread]\1[/thread]', # Multi-thread post ">>>123" r'(?)>>(\d+)': r'[post]\1[/post]', # Reflink ">>123" r'^>([^>].+)': r'[quote]\1[/quote]', # Quote ">text" r'^//(.+)': r'[comment]\1[/comment]', # Comment "//text" @@ -204,7 +186,6 @@ class Parser: self.parser = bbcode.Parser(newline=LINE_BREAK_HTML) self.parser.add_formatter('post', render_reflink, strip=True) - self.parser.add_formatter('thread', render_multithread, strip=True) self.parser.add_formatter('quote', render_quote, strip=True) self.parser.add_formatter('user', render_notification, strip=True) self.parser.add_simple_formatter( diff --git a/boards/models/post.py b/boards/models/post.py --- a/boards/models/post.py +++ b/boards/models/post.py @@ -42,7 +42,6 @@ NO_IP = '0.0.0.0' UNKNOWN_UA = '' REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]') -REGEX_MULTI_THREAD = re.compile(r'\[thread\](\d+)\[/thread\]') REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]') PARAMETER_TRUNCATED = 'truncated' @@ -65,13 +64,15 @@ DIFF_TYPE_JSON = 'json' class PostManager(models.Manager): @transaction.atomic def create_post(self, title: str, text: str, image=None, thread=None, - ip=NO_IP, tags: list=None): + ip=NO_IP, tags: list=None, threads: list=None): """ Creates new post """ if not tags: tags = [] + if not threads: + threads = [] posting_time = timezone.now() if not thread: @@ -111,7 +112,7 @@ class PostManager(models.Manager): thread.save() post.connect_replies() - post.connect_threads() + post.connect_threads(threads) post.connect_notifications() return post @@ -424,27 +425,20 @@ class Post(models.Model, Viewable): except ObjectDoesNotExist: pass - def connect_threads(self): + def connect_threads(self, threads): """ If the referenced post is an OP in another thread, make this post multi-thread. """ - for reply_number in re.finditer(REGEX_MULTI_THREAD, self.get_raw_text()): - post_id = reply_number.group(1) - - try: - referenced_post = Post.objects.get(id=post_id) + for referenced_post in threads: + if referenced_post.is_opening(): + referenced_threads = referenced_post.get_threads().all() + for thread in referenced_threads: + if thread.can_bump(): + thread.update_bump_status() - if referenced_post.is_opening(): - referenced_threads = referenced_post.get_threads().all() - for thread in referenced_threads: - if thread.can_bump(): - thread.update_bump_status() + thread.last_edit_time = self.pub_time + thread.save(update_fields=['last_edit_time', 'bumpable']) - thread.last_edit_time = self.pub_time - thread.save(update_fields=['last_edit_time', 'bumpable']) - - self.threads.add(thread) - except ObjectDoesNotExist: - pass + self.threads.add(thread) diff --git a/boards/templates/boards/staticpages/help.html b/boards/templates/boards/staticpages/help.html --- a/boards/templates/boards/staticpages/help.html +++ b/boards/templates/boards/staticpages/help.html @@ -12,7 +12,6 @@

[b]{% trans 'Bold text' %}[/b]

[spoiler]{% trans 'Spoiler' %}[/spoiler]

[post]123[/post] — {% trans 'Link to a post' %}

-

[thread]123[/thread] — {% trans 'Add post to this thread' %}

[s]{% trans 'Strikethrough text' %}[/s]

[comment]{% trans 'Comment' %}[/comment]

[quote]>{% trans 'Quote' %}[/quote]

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 @@ -20,6 +20,7 @@ FORM_TAGS = 'tags' FORM_TEXT = 'text' FORM_TITLE = 'title' FORM_IMAGE = 'image' +FORM_THREADS = 'threads' TAG_DELIMITER = ' ' @@ -119,6 +120,7 @@ class AllThreadsView(PostMixin, BaseBoar title = data[FORM_TITLE] text = data[FORM_TEXT] image = form.get_image() + threads = data[FORM_THREADS] text = self._remove_invalid_links(text) @@ -127,7 +129,7 @@ class AllThreadsView(PostMixin, BaseBoar tags = self.parse_tags_string(tag_strings) post = Post.objects.create_post(title=title, text=text, image=image, - ip=ip, tags=tags) + ip=ip, tags=tags, threads=threads) # This is required to update the threads to which posts we have replied # when creating this one 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 @@ -21,6 +21,7 @@ CONTEXT_WS_PORT = 'ws_port' FORM_TITLE = 'title' FORM_TEXT = 'text' FORM_IMAGE = 'image' +FORM_THREADS = 'threads' class ThreadView(BaseBoardView, PostMixin, FormMixin): @@ -91,13 +92,14 @@ class ThreadView(BaseBoardView, PostMixi title = data[FORM_TITLE] text = data[FORM_TEXT] image = form.get_image() + threads = data[FORM_THREADS] text = self._remove_invalid_links(text) post_thread = opening_post.get_thread() post = Post.objects.create_post(title=title, text=text, image=image, - thread=post_thread, ip=ip) + thread=post_thread, ip=ip, threads=threads) post.send_to_websocket(request) if html_response: