Show More
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | from __future__ import unicode_literals | |||
|
3 | ||||
|
4 | from django.db import models, migrations | |||
|
5 | ||||
|
6 | ||||
|
7 | class Migration(migrations.Migration): | |||
|
8 | ||||
|
9 | dependencies = [ | |||
|
10 | ('boards', '0028_auto_20150928_2211'), | |||
|
11 | ] | |||
|
12 | ||||
|
13 | operations = [ | |||
|
14 | migrations.AddField( | |||
|
15 | model_name='tag', | |||
|
16 | name='parent', | |||
|
17 | field=models.ForeignKey(to='boards.Tag', null=True), | |||
|
18 | ), | |||
|
19 | ] |
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | from __future__ import unicode_literals | |||
|
3 | ||||
|
4 | from django.db import models, migrations | |||
|
5 | ||||
|
6 | ||||
|
7 | class Migration(migrations.Migration): | |||
|
8 | ||||
|
9 | dependencies = [ | |||
|
10 | ('boards', '0029_tag_parent'), | |||
|
11 | ] | |||
|
12 | ||||
|
13 | operations = [ | |||
|
14 | migrations.AlterField( | |||
|
15 | model_name='tag', | |||
|
16 | name='parent', | |||
|
17 | field=models.ForeignKey(related_name='children', null=True, to='boards.Tag'), | |||
|
18 | ), | |||
|
19 | ] |
@@ -324,20 +324,27 b' class ThreadForm(PostForm):' | |||||
324 | _('Inappropriate characters in tags.')) |
|
324 | _('Inappropriate characters in tags.')) | |
325 |
|
325 | |||
326 | required_tag_exists = False |
|
326 | required_tag_exists = False | |
327 |
|
|
327 | tag_set = set() | |
328 | try: |
|
328 | for tag_string in tags.split(): | |
329 |
|
|
329 | tag, created = Tag.objects.get_or_create(name=tag_string.strip().lower()) | |
|
330 | tag_set.add(tag) | |||
|
331 | ||||
|
332 | # If this is a new tag, don't check for its parents because nobody | |||
|
333 | # added them yet | |||
|
334 | if not created: | |||
|
335 | tag_set |= tag.get_all_parents() | |||
|
336 | ||||
|
337 | for tag in tag_set: | |||
|
338 | if tag.required: | |||
330 | required_tag_exists = True |
|
339 | required_tag_exists = True | |
331 | break |
|
340 | break | |
332 | except ObjectDoesNotExist: |
|
|||
333 | pass |
|
|||
334 |
|
341 | |||
335 | if not required_tag_exists: |
|
342 | if not required_tag_exists: | |
336 | all_tags = Tag.objects.filter(required=True) |
|
343 | all_tags = Tag.objects.filter(required=True) | |
337 | raise forms.ValidationError( |
|
344 | raise forms.ValidationError( | |
338 | _('Need at least one section.')) |
|
345 | _('Need at least one section.')) | |
339 |
|
346 | |||
340 | return tags |
|
347 | return tag_set | |
341 |
|
348 | |||
342 | def clean(self): |
|
349 | def clean(self): | |
343 | cleaned_data = super(ThreadForm, self).clean() |
|
350 | cleaned_data = super(ThreadForm, self).clean() |
1 | NO CONTENT: modified file, binary diff hidden |
|
NO CONTENT: modified file, binary diff hidden |
@@ -483,4 +483,10 b' msgid "favorites"' | |||||
483 | msgstr "ΠΈΠ·Π±ΡΠ°Π½Π½ΠΎΠ΅" |
|
483 | msgstr "ΠΈΠ·Π±ΡΠ°Π½Π½ΠΎΠ΅" | |
484 |
|
484 | |||
485 | msgid "Loading..." |
|
485 | msgid "Loading..." | |
486 | msgstr "ΠΠ°Π³ΡΡΠ·ΠΊΠ°..." No newline at end of file |
|
486 | msgstr "ΠΠ°Π³ΡΡΠ·ΠΊΠ°..." | |
|
487 | ||||
|
488 | msgid "Category:" | |||
|
489 | msgstr "ΠΠ°ΡΠ΅Π³ΠΎΡΠΈΡ:" | |||
|
490 | ||||
|
491 | msgid "Subcategories:" | |||
|
492 | msgstr "ΠΠΎΠ΄ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΠΈ:" |
@@ -48,6 +48,8 b' class Tag(models.Model, Viewable):' | |||||
48 | required = models.BooleanField(default=False, db_index=True) |
|
48 | required = models.BooleanField(default=False, db_index=True) | |
49 | description = models.TextField(blank=True) |
|
49 | description = models.TextField(blank=True) | |
50 |
|
50 | |||
|
51 | parent = models.ForeignKey('Tag', null=True, related_name='children') | |||
|
52 | ||||
51 | def __str__(self): |
|
53 | def __str__(self): | |
52 | return self.name |
|
54 | return self.name | |
53 |
|
55 | |||
@@ -115,3 +117,18 b' class Tag(models.Model, Viewable):' | |||||
115 | Gets color hashed from the tag name. |
|
117 | Gets color hashed from the tag name. | |
116 | """ |
|
118 | """ | |
117 | return hashlib.md5(self.name.encode()).hexdigest()[:6] |
|
119 | return hashlib.md5(self.name.encode()).hexdigest()[:6] | |
|
120 | ||||
|
121 | def get_parent(self): | |||
|
122 | return self.parent | |||
|
123 | ||||
|
124 | def get_all_parents(self): | |||
|
125 | parents = set() | |||
|
126 | parent = self.get_parent() | |||
|
127 | if parent and parent not in parents: | |||
|
128 | parents.add(parent) | |||
|
129 | parents |= parent.get_all_parents() | |||
|
130 | ||||
|
131 | return parents | |||
|
132 | ||||
|
133 | def get_children(self): | |||
|
134 | return self.children |
@@ -73,13 +73,18 b'' | |||||
73 | <p>{{ tag.get_description|safe }}</p> |
|
73 | <p>{{ tag.get_description|safe }}</p> | |
74 | {% endif %} |
|
74 | {% endif %} | |
75 | <p>{% blocktrans with active_thread_count=tag.get_active_thread_count thread_count=tag.get_thread_count post_count=tag.get_post_count %}This tag has {{ thread_count }} threads ({{ active_thread_count}} active) and {{ post_count }} posts.{% endblocktrans %}</p> |
|
75 | <p>{% blocktrans with active_thread_count=tag.get_active_thread_count thread_count=tag.get_thread_count post_count=tag.get_post_count %}This tag has {{ thread_count }} threads ({{ active_thread_count}} active) and {{ post_count }} posts.{% endblocktrans %}</p> | |
76 |
{% if |
|
76 | {% if tag.get_parent %} | |
77 | <p>{% trans 'Related tags:' %} |
|
77 | <p>{% trans 'Category:' %} {{ tag.get_parent.get_view|safe }}</p> | |
78 | {% for rel_tag in related_tags %} |
|
78 | {% endif %} | |
79 | {{ rel_tag.get_view|safe }}{% if not forloop.last %}, {% else %}.{% endif %} |
|
79 | {% with children=tag.get_children.all %} | |
|
80 | {% if children %} | |||
|
81 | <p>{% trans 'Subcategories:' %} | |||
|
82 | {% for child in children %} | |||
|
83 | {{ child.get_view|safe }}{% if not forloop.last %}, {% else %}.{% endif %} | |||
80 | {% endfor %} |
|
84 | {% endfor %} | |
81 | </p> |
|
85 | </p> | |
82 | {% endif %} |
|
86 | {% endif %} | |
|
87 | {% endwith %} | |||
83 | </div> |
|
88 | </div> | |
84 | </div> |
|
89 | </div> | |
85 | {% endif %} |
|
90 | {% endif %} |
@@ -104,24 +104,6 b' class AllThreadsView(PostMixin, BaseBoar' | |||||
104 | return reverse('index') + '?page=' \ |
|
104 | return reverse('index') + '?page=' \ | |
105 | + str(current_page.next_page_number()) |
|
105 | + str(current_page.next_page_number()) | |
106 |
|
106 | |||
107 | @staticmethod |
|
|||
108 | def parse_tags_string(tag_strings): |
|
|||
109 | """ |
|
|||
110 | Parses tag list string and returns tag object list. |
|
|||
111 | """ |
|
|||
112 |
|
||||
113 | tags = [] |
|
|||
114 |
|
||||
115 | if tag_strings: |
|
|||
116 | tag_strings = tag_strings.split(TAG_DELIMITER) |
|
|||
117 | for tag_name in tag_strings: |
|
|||
118 | tag_name = tag_name.strip().lower() |
|
|||
119 | if len(tag_name) > 0: |
|
|||
120 | tag, created = Tag.objects.get_or_create(name=tag_name) |
|
|||
121 | tags.append(tag) |
|
|||
122 |
|
||||
123 | return tags |
|
|||
124 |
|
||||
125 | @transaction.atomic |
|
107 | @transaction.atomic | |
126 | def create_thread(self, request, form: ThreadForm, html_response=True): |
|
108 | def create_thread(self, request, form: ThreadForm, html_response=True): | |
127 | """ |
|
109 | """ | |
@@ -146,9 +128,7 b' class AllThreadsView(PostMixin, BaseBoar' | |||||
146 |
|
128 | |||
147 | text = self._remove_invalid_links(text) |
|
129 | text = self._remove_invalid_links(text) | |
148 |
|
130 | |||
149 |
tag |
|
131 | tags = data[FORM_TAGS] | |
150 |
|
||||
151 | tags = self.parse_tags_string(tag_strings) |
|
|||
152 |
|
132 | |||
153 | post = Post.objects.create_post(title=title, text=text, file=file, |
|
133 | post = Post.objects.create_post(title=title, text=text, file=file, | |
154 | ip=ip, tags=tags, opening_posts=threads, |
|
134 | ip=ip, tags=tags, opening_posts=threads, |
General Comments 0
You need to be logged in to leave comments.
Login now