diff --git a/boards/forms.py b/boards/forms.py --- a/boards/forms.py +++ b/boards/forms.py @@ -8,7 +8,7 @@ from django.utils.translation import uge from boards.mdx_neboard import formatters from boards.models.post import TITLE_MAX_LENGTH -from boards.models import PostImage +from boards.models import PostImage, Tag from neboard import settings from boards import utils import boards.settings as board_settings @@ -216,6 +216,17 @@ class ThreadForm(PostForm): raise forms.ValidationError( _('Inappropriate characters in tags.')) + tag_models = [] + required_tag_exists = False + for tag in tags.split(): + tag_model = Tag.objects.filter(name=tag.strip().lower(), + required=True) + if tag_model.exists(): + required_tag_exists = True + + if not required_tag_exists: + raise forms.ValidationError(_('Need at least 1 required tag.')) + return tags def clean(self): diff --git a/boards/locale/ru/LC_MESSAGES/django.mo b/boards/locale/ru/LC_MESSAGES/django.mo index 4e9742c29b537bb1ddbce0a846ab3896da8266cc..036d4fd1b32decb9e0a867271ff8dc58f282948e GIT binary patch literal 6702 zc$|$_Yit}>6~2@fV#hd%okv5Vy=h6CG~MkweWjZ?i4!}iiC?juLP5wKdnfiZ>zVD$ ztQ|uuoVXzYTS*mBT9rOXDxvZNa5i@0S8VuIgj8wv4@jtjN+859KnMvW1mZjA+ca1H_)XoR)QiBDyA)q*Kdlu0t97`o25vUvt+;&( z7z0)SA2;)Raa#vG1l$1pGVo5I3tR;ZjGmW(TY=}y`cHsWz^ev-X5>Evk}1CeYT!q} zEx>;RHv{j3&?ey1t3>Zn;Aene16Bi1tdjj-2Ywznx=QSQ2e=0KzLA>&J_P*0==qD0 z``0S5@2(2*x3WUU>wuNOjTPeemI^s{dxf0$l$ozLXU3-Es6;A*k&)zxy|d#lBdk5`L* z_f(00_g6`LcT~yz6TtPrJyqi8Q6u+UmFVjQZUkn5*FT}u1n?e=8>=O*&sPg?GSy=D z%fS19->a5=*Q&+7539wGzXI0+?_MK%wyzOBH36Ri_NI5P0zXUpjr^b1h~7J) zozr6vcL+27Uqf#dYGJ2iyld34DNfxK8-}(K@mJKfoC9 z-WnOV0BeEi8p-cyjl}1I!OJyr&UB5$;deDsXaB5`_}p77xT9A5+f^(4I#?_IJYOq% zhRynS%=oohIsbj&5bzhZ;_troV#mq#V)t2KBk)IN{O9$uf9(d*_rM16Gj7J6z;jfu z8-#yHN%Wd8L{(%XK&N#gMo zZX>`B;0wTC-YxP^Y!*K3-z;%BYH(n)`0>rna^59_Q@}@n9{`^Q{v8Nf7B8~#A@h91 zU>v{4mH0u|PW%$)nR3*sBo`e@YKHi>-mFtCk^K*wG3A)fr?c0Toy*)(5<80LlS=A~ za@0h5$FI%EQXYt_ht%@xfI;fx#7#}{P|{cEI*i}FO8N)Kj^e7MAJ9d0RWEM-5Vc?=;U;i&TG<8_LUWCH;wVNBx92*{p;!g^T*zHYIgL z+}y9E$8?$Xc7s$`sv(uqq1CS%(rFzI__mb_R6{l#@ccj>uw2(psRLdbGWN+(9rOkV zZ5If+(AKU!Z1$)|CkTC~Hyhe1opn>Tj|E#brJRs&e1trRe3OH9pS5bw*Uq5TZ|lKq z5b9oA;|SZgQ*m|3#_@-(blUbu^q`lr)iX{iWxJ#*$

ogg&lKN)T0j-0`8?bL}mV^9QZ8 zgs#mCWxHz6*skipF^FLY*+w_Xf$H$>A;%sT^s<4Nb2RIPw(7JqX$Ns69FaRYXi|!? z7dWBkkEkvS5$m!ozi$8#hK}1GsICETn6uS&!pUUd^44vAFels|APTHFuE{c^k2D9i zt&#oWy6Vaxeo5@iWK!5ifyEv9NLe^F<2hi8>H-r_*wEr-`v)X5UD>_?L$YBjfKEDL zg!hACUBga@b&gWkh#OibRX6O!59xNXiAj%Pz_%jBQg+%Ibcpi3BRa@fedI#-GD@;F63=Vi48^%Q&G0(s&*)dzz^)I7?JjUifdudv*dℑd^S&Kq z(;+*Nq|Lr=?tr-|pIje6wtH=ms+W5i>cm4Z`$FDnrC>TWE;?9Q-1St-4xE11riURr z?SWj1QyHiitRXAt^PNnnOKT{88%;x*SO!Ws3G()42Q$nM1=80c2t)8N-9ud?=%G63 z@Vyu8zA)CDiam>*IG&r-9SsMMG#qZy&o*^-HMh4_Qc?L~tTpI&Qn3Ts{vg)vC3R&- zdv}a09-Cr~+;4U!>Yj)t>SBpqy6(y3V^8i#>`Wve80$o|f|A^GO%0u~*7jIqgMOwx z+1kA0EA4GfmDIXo-M-}pX$nSCA3E03qT3o;oAjQh)`sSm{SA$colRX`dm-xfXW?qB z+qMRix}~A*@G&xZPfN@Gv=jII{*p{m``OCY=GLZ?hjsBpB_#nR6(bfVIkAss(w5`y zW{L)OSbwbhP;6I8jto0)`>`gs&r2ckNxiGr2`f9&S%frp2&@f~+RgAVsDEs?mdE;S z?$)|QeSMvN@Ig(Fi7oYYb$Xvp=p>K#*6%DV?Wup99(hM%PhCR4^pb3WOkz9s)M3xg z?fAnY`fjusU5O^6Tr`tEouAOrLNuK}9nIy(RCF~qXjr@2tD>~-#r}C#&^d{C9=oqY>jwT^96V0mV z5~i<5QWH81TbhLy`Q!07`idet+bj!pt zCvRRW9e254vxbH9(Ts+tFc$(dkjAryx$qU9!b=@p$&ZWn9R5vXjXWHWtLXa$U&)1O z3QV-1qpPrf9(IgkYKHuuqrgJLID%fVFq%mgmngt1nvV=}EUt+g(e!Pm%j}I7%j^~R zry+m_$84CTM7_;UEF$1z6a})HZ9@+52x}4`X15=BVKFCc1{*3VJZDu!%VFoe0z`;aS@sD)LiP3s7LUGC`+eGY!ep)Wk87%x35#?=?Co_ty$e$@_LCHbOTLp%Y#nWtdG+V|x zqk-uh*QNTRGOnsY1cSIEwXXaRD|`3~v{~AJU1Oh)FRd$BkmVY*6-C=nT1K z{bri!dYv^bmUXK9nc`XO6!p9rN)`ob(%1sVQ+i|-nUAjDriR~T;U)HkLtFMaNj@73 z!CK_e1f8@?ah;ZEJfibwS%{bjf+FCQDdQD)@{%ql2-*reCCzlA^eK7^`DeMvg<6zo zj>@cbFMbo`&oY_jus+5eYcx3m2s52m;< zZ|xpp)p_c6$iN%)(NK8nz`kPto8-5`JRLYC@1QGu!nCNQqxe-xR_pvLg$~KhqWHl> zy(%sSfuH#^Qtnf{w_qQ=Ru?$hW0bF@!h5Og(Y&JQBm|LPG!*(3bx|=>=fIAs|E+CF z3Yp?Q+n_;$O_UbGn(n\n" "Language-Team: LANGUAGE \n" @@ -41,7 +41,7 @@ msgstr "" #: forms.py:23 msgid "tag1 several_words_tag" -msgstr "тег1 тег_из_нескольких_слов" +msgstr "метка1 метка_из_нескольких_слов" #: forms.py:25 msgid "Such image was already posted" @@ -57,9 +57,9 @@ msgstr "Текст" #: forms.py:29 msgid "Tag" -msgstr "Тег" +msgstr "Метка" -#: forms.py:30 templates/boards/base.html:40 templates/search/search.html:9 +#: forms.py:30 templates/boards/base.html:38 templates/search/search.html:9 #: templates/search/search.html.py:13 msgid "Search" msgstr "Поиск" @@ -98,21 +98,25 @@ msgstr "Подождите %s секунд после последнего постинга" #: forms.py:210 templates/boards/rss/post.html:10 templates/boards/tags.html:7 msgid "Tags" -msgstr "Теги" +msgstr "Метки" -#: forms.py:217 forms.py:243 +#: forms.py:217 forms.py:254 msgid "Inappropriate characters in tags." -msgstr "Недопустимые символы в тегах." +msgstr "Недопустимые символы в метках." -#: forms.py:230 +#: forms.py:228 +msgid "Need at least 1 required tag." +msgstr "Нужна хотя бы 1 обязательная метка." + +#: forms.py:241 msgid "Theme" msgstr "Тема" -#: forms.py:266 +#: forms.py:277 msgid "Invalid master password" msgstr "Неверный мастер-пароль" -#: forms.py:280 +#: forms.py:291 #, python-format msgid "Wait %s minutes after last login" msgstr "Подождите %s минут после последнего входа" @@ -141,32 +145,32 @@ msgstr "лицензией" msgid "Repository" msgstr "Репозиторий" -#: templates/boards/base.html:16 +#: templates/boards/base.html:13 msgid "Feed" msgstr "Лента" -#: templates/boards/base.html:33 +#: templates/boards/base.html:30 msgid "All threads" msgstr "Все темы" -#: templates/boards/base.html:38 +#: templates/boards/base.html:36 msgid "Tag management" -msgstr "Управление тегами" +msgstr "Управление метками" -#: templates/boards/base.html:41 templates/boards/settings.html:7 +#: templates/boards/base.html:39 templates/boards/settings.html:7 msgid "Settings" msgstr "Настройки" -#: templates/boards/base.html:56 +#: templates/boards/base.html:52 msgid "Admin" msgstr "" -#: templates/boards/base.html:58 +#: templates/boards/base.html:54 #, python-format msgid "Speed: %(ppd)s posts per day" msgstr "Скорость: %(ppd)s сообщений в день" -#: templates/boards/base.html:60 +#: templates/boards/base.html:56 msgid "Up" msgstr "Вверх" @@ -178,92 +182,96 @@ msgstr "Вход" msgid "Insert your user id above" msgstr "Вставьте свой ID пользователя выше" -#: templates/boards/post.html:21 templates/boards/staticpages/help.html:17 +#: templates/boards/post.html:19 templates/boards/staticpages/help.html:17 msgid "Quote" msgstr "Цитата" -#: templates/boards/post.html:31 +#: templates/boards/post.html:27 msgid "Open" msgstr "Открыть" -#: templates/boards/post.html:33 +#: templates/boards/post.html:29 msgid "Reply" msgstr "Ответ" -#: templates/boards/post.html:40 +#: templates/boards/post.html:36 msgid "Edit" msgstr "Изменить" -#: templates/boards/post.html:42 +#: templates/boards/post.html:39 msgid "Edit thread" msgstr "Изменить тему" -#: templates/boards/post.html:73 +#: templates/boards/post.html:71 msgid "Replies" msgstr "Ответы" -#: templates/boards/post.html:83 templates/boards/thread.html:100 +#: templates/boards/post.html:79 templates/boards/thread.html:89 #: templates/boards/thread_gallery.html:59 msgid "messages" msgstr "сообщений" -#: templates/boards/post.html:84 templates/boards/thread.html:101 +#: templates/boards/post.html:80 templates/boards/thread.html:90 #: templates/boards/thread_gallery.html:60 msgid "images" msgstr "изображений" #: templates/boards/post_admin.html:19 msgid "Tags:" -msgstr "Теги:" +msgstr "Метки:" #: templates/boards/post_admin.html:30 msgid "Add tag" -msgstr "Добавить тег" +msgstr "Добавить метку" #: templates/boards/posting_general.html:56 msgid "Show tag" -msgstr "Показывать тег" +msgstr "Показывать метку" #: templates/boards/posting_general.html:60 msgid "Hide tag" -msgstr "Скрывать тег" +msgstr "Скрывать метку" -#: templates/boards/posting_general.html:79 templates/search/search.html:22 +#: templates/boards/posting_general.html:66 +msgid "Edit tag" +msgstr "Изменить метку" + +#: templates/boards/posting_general.html:82 templates/search/search.html:22 msgid "Previous page" msgstr "Предыдущая страница" -#: templates/boards/posting_general.html:94 +#: templates/boards/posting_general.html:97 #, python-format msgid "Skipped %(count)s replies. Open thread to see all replies." msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы." -#: templates/boards/posting_general.html:121 templates/search/search.html:33 +#: templates/boards/posting_general.html:124 templates/search/search.html:33 msgid "Next page" msgstr "Следующая страница" -#: templates/boards/posting_general.html:126 +#: templates/boards/posting_general.html:129 msgid "No threads exist. Create the first one!" msgstr "Нет тем. Создайте первую!" -#: templates/boards/posting_general.html:132 +#: templates/boards/posting_general.html:135 msgid "Create new thread" msgstr "Создать новую тему" -#: templates/boards/posting_general.html:137 templates/boards/preview.html:16 -#: templates/boards/thread.html:59 +#: templates/boards/posting_general.html:140 templates/boards/preview.html:16 +#: templates/boards/thread.html:54 msgid "Post" msgstr "Отправить" -#: templates/boards/posting_general.html:142 +#: templates/boards/posting_general.html:145 msgid "Tags must be delimited by spaces. Text or image is required." msgstr "" -"Теги должны быть разделены пробелами. Текст или изображение обязательны." +"Метки должны быть разделены пробелами. Текст или изображение обязательны." -#: templates/boards/posting_general.html:145 templates/boards/thread.html:67 +#: templates/boards/posting_general.html:148 templates/boards/thread.html:62 msgid "Text syntax" msgstr "Синтаксис текста" -#: templates/boards/posting_general.html:157 +#: templates/boards/posting_general.html:160 msgid "Pages:" msgstr "Страницы: " @@ -281,11 +289,11 @@ msgstr "Вы модератор." #: templates/boards/settings.html:19 msgid "Hidden tags:" -msgstr "Скрытые теги:" +msgstr "Скрытые метки:" #: templates/boards/settings.html:26 msgid "No hidden tags." -msgstr "Нет скрытых тегов." +msgstr "Нет скрытых меток." #: templates/boards/settings.html:35 msgid "Save" @@ -332,36 +340,31 @@ msgstr "Комментарий" msgid "You can try pasting the text and previewing the result here:" msgstr "Вы можете попробовать вставить текст и проверить результат здесь:" -#: templates/boards/tags.html:22 +#: templates/boards/tags.html:23 msgid "No tags found." -msgstr "Теги не найдены." +msgstr "Метки не найдены." -#: templates/boards/thread.html:21 templates/boards/thread_gallery.html:19 +#: templates/boards/thread.html:19 templates/boards/thread_gallery.html:19 msgid "Normal mode" msgstr "Нормальный режим" -#: templates/boards/thread.html:22 templates/boards/thread_gallery.html:20 +#: templates/boards/thread.html:20 templates/boards/thread_gallery.html:20 msgid "Gallery mode" msgstr "Режим галереи" -#: templates/boards/thread.html:30 +#: templates/boards/thread.html:28 msgid "posts to bumplimit" msgstr "сообщений до бамплимита" -#: templates/boards/thread.html:51 +#: templates/boards/thread.html:46 msgid "Reply to thread" msgstr "Ответить в тему" -#: templates/boards/thread.html:64 +#: templates/boards/thread.html:59 msgid "Switch mode" msgstr "Переключить режим" -#: templates/boards/thread.html:102 templates/boards/thread_gallery.html:61 +#: templates/boards/thread.html:91 templates/boards/thread_gallery.html:61 msgid "Last update: " msgstr "Последнее обновление: " -#~ msgid "Delete" -#~ msgstr "Удалить" - -#~ msgid "Ban IP" -#~ msgstr "Заблокировать IP" diff --git a/boards/migrations/0004_tag_required.py b/boards/migrations/0004_tag_required.py new file mode 100644 --- /dev/null +++ b/boards/migrations/0004_tag_required.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('boards', '0003_remove_tag_threads'), + ] + + operations = [ + migrations.AddField( + model_name='tag', + name='required', + field=models.BooleanField(default=False), + preserve_default=True, + ), + ] diff --git a/boards/models/base.py b/boards/models/base.py --- a/boards/models/base.py +++ b/boards/models/base.py @@ -6,5 +6,13 @@ class Viewable(): pass def get_view(self, *args, **kwargs): - """Get an HTML view for a model""" - pass \ No newline at end of file + """ + Gets an HTML view for a model + """ + pass + + def get_search_view(self, *args, **kwargs): + """ + Gets an HTML view for search. + """ + pass diff --git a/boards/models/post.py b/boards/models/post.py --- a/boards/models/post.py +++ b/boards/models/post.py @@ -340,6 +340,9 @@ class Post(models.Model, Viewable): PARAMETER_OP_ID: opening_post_id, }) + def get_search_view(self, *args, **kwargs): + return self.get_view(args, kwargs) + def get_first_image(self) -> PostImage: return self.images.earliest('id') diff --git a/boards/models/tag.py b/boards/models/tag.py --- a/boards/models/tag.py +++ b/boards/models/tag.py @@ -18,7 +18,7 @@ class TagManager(models.Manager): """ not_empty_tags = list() - tags = self.order_by('name') + tags = self.order_by('-required', 'name') for tag in tags: if tag.get_thread_count() > 0: not_empty_tags.append(tag) @@ -39,6 +39,7 @@ class Tag(models.Model, Viewable): ordering = ('name',) name = models.CharField(max_length=100, db_index=True) + required = models.BooleanField(default=False) def __str__(self): return self.name @@ -73,10 +74,21 @@ class Tag(models.Model, Viewable): def get_url(self): return reverse('tag', kwargs={'tag_name': self.name}) - def get_view(self, *args, **kwargs): - return render_to_string('boards/tag.html', { - 'tag': self, - }) - def get_threads(self): return Thread.objects.filter(tags__in=[self]).order_by('-bump_time') + + def is_required(self): + return self.required + + def get_view(self): + #prefix = '##' if self.is_required() else '#' + link = '{}'.format( + self.get_url(), self.name) + if self.is_required(): + link = '{}'.format(link) + return link + + def get_search_view(self, *args, **kwargs): + return render_to_string('boards/tag.html', { + 'tag': self, + }) diff --git a/boards/static/js/thread_update.js b/boards/static/js/thread_update.js --- a/boards/static/js/thread_update.js +++ b/boards/static/js/thread_update.js @@ -312,24 +312,22 @@ function processNewPost(post) { } $(document).ready(function(){ - if ('WebSocket' in window) { - if (initAutoupdate()) { - // Post form data over AJAX - var threadId = $('div.thread').children('.post').first().attr('id'); + if (initAutoupdate()) { + // Post form data over AJAX + var threadId = $('div.thread').children('.post').first().attr('id'); - var form = $('#form'); + var form = $('#form'); - var options = { - beforeSubmit: function(arr, $form, options) { - showAsErrors($('form'), gettext('Sending message...')); - }, - success: updateOnPost, - url: '/api/add_post/' + threadId + '/' - }; + var options = { + beforeSubmit: function(arr, $form, options) { + showAsErrors($('form'), gettext('Sending message...')); + }, + success: getThreadDiff, + url: '/api/add_post/' + threadId + '/' + }; - form.ajaxForm(options); + form.ajaxForm(options); - resetForm(form); - } + resetForm(form); } }); diff --git a/boards/templates/boards/base.html b/boards/templates/boards/base.html --- a/boards/templates/boards/base.html +++ b/boards/templates/boards/base.html @@ -29,8 +29,9 @@

diff --git a/boards/templates/boards/post_admin.html b/boards/templates/boards/post_admin.html deleted file mode 100644 --- a/boards/templates/boards/post_admin.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "boards/base.html" %} - -{% load i18n %} -{% load cache %} -{% load static from staticfiles %} -{% load board %} - -{% block head %} -#{{ post.id }} - {{ site_name }} -{% endblock %} - -{% block content %} - {% spaceless %} - - {% post_view post moderator=moderator %} - - {% if post.is_opening %} -
- {% trans 'Tags:' %} - {% for tag in post.thread_new.get_tags %} - #{{ tag.name }} - [X] - {% if not forloop.last %},{% endif %} - {% endfor %} -
-
{% csrf_token %} - {{ tag_form.as_div }} -
- -
-
-
-
- {% endif %} - - {% endspaceless %} -{% endblock %} diff --git a/boards/templates/boards/posting_general.html b/boards/templates/boards/posting_general.html --- a/boards/templates/boards/posting_general.html +++ b/boards/templates/boards/posting_general.html @@ -60,7 +60,10 @@ title="{% trans 'Hide tag' %}" class="not_fav" rel="nofollow">H {% endif %} - #{{ tag.name }} + {% autoescape off %} + {{ tag.get_view }} + {% endautoescape %} + [{% trans 'Edit tag' %}] {% endif %} diff --git a/boards/templates/boards/tag.html b/boards/templates/boards/tag.html --- a/boards/templates/boards/tag.html +++ b/boards/templates/boards/tag.html @@ -1,3 +1,5 @@
- #{{ tag.name }} -
\ No newline at end of file + {% autoescape off %} + {{ tag.get_view }} + {% endautoescape %} + diff --git a/boards/templates/boards/tags.html b/boards/templates/boards/tags.html --- a/boards/templates/boards/tags.html +++ b/boards/templates/boards/tags.html @@ -14,8 +14,9 @@ {% if all_tags %} {% for tag in all_tags %}
- - #{{ tag.name }} + {% autoescape off %} + {{ tag.get_view }} + {% endautoescape %}
{% endfor %} {% else %} diff --git a/boards/templates/search/search.html b/boards/templates/search/search.html --- a/boards/templates/search/search.html +++ b/boards/templates/search/search.html @@ -25,7 +25,7 @@ {% endif %} {% for result in page.object_list %} - {{ result.object.get_view }} + {{ result.object.get_search_view }} {% endfor %} {% if page.has_next %} @@ -35,4 +35,4 @@ {% endif %} {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/boards/tests/test_forms.py b/boards/tests/test_forms.py --- a/boards/tests/test_forms.py +++ b/boards/tests/test_forms.py @@ -1,7 +1,7 @@ from django.test import TestCase, Client import time from boards import settings -from boards.models import Post +from boards.models import Post, Tag import neboard @@ -22,6 +22,7 @@ class FormTest(TestCase): valid_tags = 'tag1 tag_2 тег_3' invalid_tags = '$%_356 ---' + Tag.objects.create(name='tag1', required=True) response = client.post(NEW_THREAD_PAGE, {'title': 'test title', 'text': TEST_TEXT, diff --git a/manage.py b/manage.py --- a/manage.py +++ b/manage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import sys