diff --git a/boards/abstracts/settingsmanager.py b/boards/abstracts/settingsmanager.py --- a/boards/abstracts/settingsmanager.py +++ b/boards/abstracts/settingsmanager.py @@ -78,49 +78,49 @@ class SettingsManager: tag_names = self.get_setting(SETTING_FAVORITE_TAGS) tags = [] if tag_names: - tags = list(Tag.objects.filter(name__in=tag_names)) + tags = list(Tag.objects.filter(aliases__name__in=tag_names)) return tags def add_fav_tag(self, tag): tags = self.get_setting(SETTING_FAVORITE_TAGS) if not tags: - tags = [tag.name] + tags = [tag.get_name()] else: - if not tag.name in tags: - tags.append(tag.name) + if not tag.get_name() in tags: + tags.append(tag.get_name()) tags.sort() self.set_setting(SETTING_FAVORITE_TAGS, tags) def del_fav_tag(self, tag): tags = self.get_setting(SETTING_FAVORITE_TAGS) - if tag.name in tags: - tags.remove(tag.name) + if tag.get_name() in tags: + tags.remove(tag.get_name()) self.set_setting(SETTING_FAVORITE_TAGS, tags) def get_hidden_tags(self) -> list: tag_names = self.get_setting(SETTING_HIDDEN_TAGS) tags = [] if tag_names: - tags = list(Tag.objects.filter(name__in=tag_names)) + tags = list(Tag.objects.filter(aliases__name__in=tag_names)) return tags def add_hidden_tag(self, tag): tags = self.get_setting(SETTING_HIDDEN_TAGS) if not tags: - tags = [tag.name] + tags = [tag.get_name()] else: - if not tag.name in tags: - tags.append(tag.name) + if not tag.get_name() in tags: + tags.append(tag.get_name()) tags.sort() self.set_setting(SETTING_HIDDEN_TAGS, tags) def del_hidden_tag(self, tag): tags = self.get_setting(SETTING_HIDDEN_TAGS) - if tag.name in tags: - tags.remove(tag.name) + if tag.get_name() in tags: + tags.remove(tag.get_name()) self.set_setting(SETTING_HIDDEN_TAGS, tags) def get_fav_threads(self) -> dict: diff --git a/boards/admin.py b/boards/admin.py --- a/boards/admin.py +++ b/boards/admin.py @@ -83,12 +83,16 @@ class TagAdmin(admin.ModelAdmin): def display_children(self, obj: Tag): return ', '.join([str(child) for child in obj.get_children().all()]) + def name(self, obj: Tag): + return obj.get_name() + def save_model(self, request, obj, form, change): super().save_model(request, obj, form, change) for thread in obj.get_threads().all(): thread.refresh_tags() list_display = ('name', 'thread_count', 'display_children') search_fields = ('name',) + readonly_fields = ('name',) @admin.register(TagAlias) diff --git a/boards/forms/__init__.py b/boards/forms/__init__.py --- a/boards/forms/__init__.py +++ b/boards/forms/__init__.py @@ -472,15 +472,10 @@ class ThreadForm(PostForm): tag_name = tag_string.strip().lower() if tag_name == default_tag_name: required_tag_exists = True - tag, created = Tag.objects.get_or_create( + tag, created = Tag.objects.get_or_create_with_alias( name=tag_name, required=True) else: - tag = Tag.objects.get_by_alias(tag_name) - if tag: - created = False - else: - tag, created = Tag.objects.get_or_create( - name=tag_name) + tag, created = Tag.objects.get_or_create_with_alias(name=tag_name) tag_set.add(tag) # If this is a new tag, don't check for its parents because nobody diff --git a/boards/migrations/0062_auto_20170301_1053.py b/boards/migrations/0062_auto_20170301_1053.py new file mode 100644 --- /dev/null +++ b/boards/migrations/0062_auto_20170301_1053.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-03-01 08:53 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + def tag_name_to_alias(apps, schema_editor): + Tag = apps.get_model('boards', 'Tag') + TagAlias = apps.get_model('boards', 'TagAlias') + + for tag in Tag.objects.all(): + TagAlias.objects.get_or_create(name=tag.name, locale='default', parent=tag) + + dependencies = [ + ('boards', '0061_auto_20170227_1739'), + ] + + operations = [ + migrations.RunPython(tag_name_to_alias), + ] diff --git a/boards/migrations/0063_auto_20170301_1058.py b/boards/migrations/0063_auto_20170301_1058.py new file mode 100644 --- /dev/null +++ b/boards/migrations/0063_auto_20170301_1058.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-03-01 08:58 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('boards', '0062_auto_20170301_1053'), + ] + + operations = [ + migrations.AlterModelOptions( + name='tag', + options={}, + ), + migrations.RemoveField( + model_name='tag', + name='name', + ), + ] diff --git a/boards/models/tag.py b/boards/models/tag.py --- a/boards/models/tag.py +++ b/boards/models/tag.py @@ -18,6 +18,7 @@ import boards RELATED_TAGS_COUNT = 5 +DEFAULT_LOCALE = 'default' class TagAlias(models.Model, Viewable): @@ -38,8 +39,10 @@ class TagManager(models.Manager): Gets tags that have non-archived threads. """ - return self.annotate(num_threads=Count('thread_tags')).filter(num_threads__gt=0)\ - .order_by('name') + return self.annotate(num_threads=Count('thread_tags'))\ + .filter(num_threads__gt=0)\ + .filter(aliases__locale=DEFAULT_LOCALE)\ + .order_by('aliases__name') def get_tag_url_list(self, tags: list) -> str: """ @@ -56,6 +59,15 @@ class TagManager(models.Manager): return tag + def get_or_create_with_alias(self, name, required=False): + tag = self.get_by_alias(name) + created = False + if not tag: + tag = self.create(required=required) + TagAlias.objects.create(name=alias, locale=DEFAULT_LOCALE, parent=tag) + created = True + return tag, created + class Tag(models.Model, Viewable): """ @@ -67,17 +79,19 @@ class Tag(models.Model, Viewable): class Meta: app_label = 'boards' - ordering = ('name',) - name = models.CharField(max_length=100, db_index=True, unique=True) required = models.BooleanField(default=False, db_index=True) description = models.TextField(blank=True) parent = models.ForeignKey('Tag', null=True, blank=True, related_name='children') + @cached_result() + def get_name(self): + return self.aliases.get(locale=DEFAULT_LOCALE).name + def __str__(self): - return self.name + return self.get_name() def is_empty(self) -> bool: """ @@ -102,7 +116,7 @@ class Tag(models.Model, Viewable): return self.get_thread_count(status=STATUS_ARCHIVE) def get_absolute_url(self): - return reverse('tag', kwargs={'tag_name': self.name}) + return reverse('tag', kwargs={'tag_name': self.get_name()}) def get_threads(self): return self.thread_tags.order_by('-bump_time') @@ -118,7 +132,9 @@ class Tag(models.Model, Viewable): except TagAlias.DoesNotExist: localized_tag_name = '' - name = '{} ({})'.format(self.name, localized_tag_name) if localized_tag_name else self.name + default_name = self.get_name() + + name = '{} ({})'.format(default_name, localized_tag_name) if localized_tag_name else default_name link = '{}'.format( self.get_absolute_url(), name) if self.is_required(): @@ -141,7 +157,8 @@ class Tag(models.Model, Viewable): return posts.order_by('?').first() def get_first_letter(self): - return self.name and self.name[0] or '' + name = self.get_name() + return name and name[0] or '' def get_related_tags(self): return set(Tag.objects.filter(thread_tags__in=self.get_threads()).exclude( @@ -152,7 +169,7 @@ class Tag(models.Model, Viewable): """ Gets color hashed from the tag name. """ - return hashlib.md5(self.name.encode()).hexdigest()[:6] + return hashlib.md5(self.get_name().encode()).hexdigest()[:6] def get_parent(self): return self.parent diff --git a/boards/models/thread.py b/boards/models/thread.py --- a/boards/models/thread.py +++ b/boards/models/thread.py @@ -10,7 +10,7 @@ from boards import settings from boards.models import STATUS_BUMPLIMIT, STATUS_ACTIVE, STATUS_ARCHIVE from boards.models.attachment import FILE_TYPES_IMAGE from boards.models.post import Post -from boards.models.tag import Tag +from boards.models.tag import Tag, DEFAULT_LOCALE from boards.utils import cached_result, datetime_to_epoch FAV_THREAD_NO_UPDATES = -1 @@ -103,7 +103,7 @@ class Thread(models.Model): Gets a sorted tag list. """ - return self.tags.order_by('name') + return self.tags.filter(aliases__locale=DEFAULT_LOCALE).order_by('aliases__name') def bump(self): """ @@ -230,7 +230,7 @@ class Thread(models.Model): return 'T#{}'.format(self.id) def get_tag_url_list(self) -> list: - return boards.models.Tag.objects.get_tag_url_list(self.get_tags()) + return boards.models.Tag.objects.get_tag_url_list(self.get_tags().all()) def update_posts_time(self, exclude_posts=None): last_edit_time = self.last_edit_time diff --git a/boards/templates/boards/all_threads.html b/boards/templates/boards/all_threads.html --- a/boards/templates/boards/all_threads.html +++ b/boards/templates/boards/all_threads.html @@ -9,7 +9,7 @@ {% if tag %} -
-
- - {% trans 'Gallery' %} + {% trans 'Gallery' %} {% if tag.get_description %}{{ tag.get_description|safe }}
diff --git a/boards/views/api.py b/boards/views/api.py --- a/boards/views/api.py +++ b/boards/views/api.py @@ -10,7 +10,7 @@ from django.views.decorators.csrf import from boards.abstracts.settingsmanager import get_settings_manager from boards.forms import PostForm, PlainErrorList from boards.mdx_neboard import Parser -from boards.models import Post, Thread, Tag, Attachment +from boards.models import Post, Thread, Tag, Attachment, TagAlias from boards.models.thread import STATUS_ARCHIVE from boards.models.user import Notification from boards.utils import datetime_to_epoch @@ -178,7 +178,7 @@ def api_get_tags(request): # TODO Get favorite tags for the given user ID - tags = Tag.objects.get_not_empty_tags() + tags = TagAlias.objects.all() term = request.GET.get('term') if term is not None: diff --git a/boards/views/landing.py b/boards/views/landing.py --- a/boards/views/landing.py +++ b/boards/views/landing.py @@ -9,6 +9,7 @@ from django.views.decorators.csrf import from boards import settings from boards.models import Post from boards.models import Tag, Attachment, STATUS_ACTIVE +from boards.models.tag import DEFAULT_LOCALE from boards.views.base import BaseBoardView PARAM_SECTION_STR = 'section_str' @@ -23,7 +24,8 @@ class LandingView(BaseBoardView): params = dict() params[PARAM_SECTION_STR] = Tag.objects.get_tag_url_list( - Tag.objects.filter(required=True)) + Tag.objects.filter(required=True).filter( + aliases__locale=DEFAULT_LOCALE).order_by('aliases__name')) today = datetime.now() - timedelta(1) ops = Post.objects.filter(thread__replies__pub_time__gt=today, opening=True, thread__status=STATUS_ACTIVE)\ 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.core.urlresolvers import rev from boards.abstracts.settingsmanager import get_settings_manager, \ SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS -from boards.models import Tag +from boards.models import Tag, TagAlias from boards.views.all_threads import AllThreadsView from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD from boards.forms import ThreadForm, PlainErrorList @@ -24,7 +24,8 @@ class TagView(AllThreadsView, Dispatcher tag_name = None def get_threads(self): - tag = get_object_or_404(Tag, name=self.tag_name) + tag_alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = tag_alias.parent hidden_tags = self.settings_manager.get_hidden_tags() @@ -41,14 +42,15 @@ class TagView(AllThreadsView, Dispatcher settings_manager = get_settings_manager(kwargs['request']) - tag = get_object_or_404(Tag, name=self.tag_name) + tag_alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = tag_alias.parent params[PARAM_TAG] = tag fav_tag_names = settings_manager.get_setting(SETTING_FAVORITE_TAGS) hidden_tag_names = settings_manager.get_setting(SETTING_HIDDEN_TAGS) - params[PARAM_IS_FAVORITE] = fav_tag_names is not None and tag.name in fav_tag_names - params[PARAM_IS_HIDDEN] = hidden_tag_names is not None and tag.name in hidden_tag_names + params[PARAM_IS_FAVORITE] = fav_tag_names is not None and tag.get_name() in fav_tag_names + params[PARAM_IS_HIDDEN] = hidden_tag_names is not None and tag.get_name() in hidden_tag_names params[PARAM_RANDOM_IMAGE_POST] = tag.get_random_image_post() params[PARAM_RELATED_TAGS] = tag.get_related_tags() @@ -85,13 +87,15 @@ class TagView(AllThreadsView, Dispatcher return self.get(request, tag_name, form) def subscribe(self, request): - tag = get_object_or_404(Tag, name=self.tag_name) + alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = alias.parent settings_manager = get_settings_manager(request) settings_manager.add_fav_tag(tag) def unsubscribe(self, request): - tag = get_object_or_404(Tag, name=self.tag_name) + alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = alias.parent settings_manager = get_settings_manager(request) settings_manager.del_fav_tag(tag) @@ -102,7 +106,8 @@ class TagView(AllThreadsView, Dispatcher shown. """ - tag = get_object_or_404(Tag, name=self.tag_name) + alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = alias.parent settings_manager = get_settings_manager(request) settings_manager.add_hidden_tag(tag) @@ -112,7 +117,8 @@ class TagView(AllThreadsView, Dispatcher Removed tag from user's hidden tags. """ - tag = get_object_or_404(Tag, name=self.tag_name) + alias = get_object_or_404(TagAlias, name=self.tag_name) + tag = alias.parent settings_manager = get_settings_manager(request) settings_manager.del_hidden_tag(tag)