Add option for disabling tag grouping (#735)

* Configurable tag grouping

* update tag group name

---------

Co-authored-by: Sascha Ißbrücker <sascha.issbruecker@gmail.com>
This commit is contained in:
Viacheslav Slinko 2024-05-17 09:38:08 +03:00 committed by GitHub
parent a92a35cfb8
commit e03f536925
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 106 additions and 2 deletions

View file

@ -0,0 +1,22 @@
# Generated by Django 5.0.3 on 2024-05-14 08:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookmarks", "0034_bookmark_preview_image_file_and_more"),
]
operations = [
migrations.AddField(
model_name="userprofile",
name="tag_grouping",
field=models.CharField(
choices=[("alphabetical", "Alphabetical"), ("disabled", "Disabled")],
default="alphabetical",
max_length=12,
),
),
]

View file

@ -352,6 +352,12 @@ class UserProfile(models.Model):
(TAG_SEARCH_STRICT, "Strict"), (TAG_SEARCH_STRICT, "Strict"),
(TAG_SEARCH_LAX, "Lax"), (TAG_SEARCH_LAX, "Lax"),
] ]
TAG_GROUPING_ALPHABETICAL = "alphabetical"
TAG_GROUPING_DISABLED = "disabled"
TAG_GROUPING_CHOICES = [
(TAG_GROUPING_ALPHABETICAL, "Alphabetical"),
(TAG_GROUPING_DISABLED, "Disabled"),
]
user = models.OneToOneField( user = models.OneToOneField(
get_user_model(), related_name="profile", on_delete=models.CASCADE get_user_model(), related_name="profile", on_delete=models.CASCADE
) )
@ -392,6 +398,12 @@ class UserProfile(models.Model):
blank=False, blank=False,
default=TAG_SEARCH_STRICT, default=TAG_SEARCH_STRICT,
) )
tag_grouping = models.CharField(
max_length=12,
choices=TAG_GROUPING_CHOICES,
blank=False,
default=TAG_GROUPING_ALPHABETICAL,
)
enable_sharing = models.BooleanField(default=False, null=False) enable_sharing = models.BooleanField(default=False, null=False)
enable_public_sharing = models.BooleanField(default=False, null=False) enable_public_sharing = models.BooleanField(default=False, null=False)
enable_favicons = models.BooleanField(default=False, null=False) enable_favicons = models.BooleanField(default=False, null=False)
@ -419,6 +431,7 @@ class UserProfileForm(forms.ModelForm):
"bookmark_link_target", "bookmark_link_target",
"web_archive_integration", "web_archive_integration",
"tag_search", "tag_search",
"tag_grouping",
"enable_sharing", "enable_sharing",
"enable_public_sharing", "enable_public_sharing",
"enable_favicons", "enable_favicons",

View file

@ -110,6 +110,14 @@
result will also include bookmarks where a search term matches otherwise. result will also include bookmarks where a search term matches otherwise.
</div> </div>
</div> </div>
<div class="form-group">
<label for="{{ form.tag_grouping.id_for_label }}" class="form-label">Tag grouping</label>
{{ form.tag_grouping|add_class:"form-select width-25 width-sm-100" }}
<div class="form-input-hint">
In alphabetical mode, tags will be grouped by the first letter.
If disabled, tags will not be grouped.
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="{{ form.enable_favicons.id_for_label }}" class="form-checkbox"> <label for="{{ form.enable_favicons.id_for_label }}" class="form-checkbox">
{{ form.enable_favicons }} {{ form.enable_favicons }}

View file

@ -34,6 +34,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
"enable_preview_images": False, "enable_preview_images": False,
"enable_automatic_html_snapshots": True, "enable_automatic_html_snapshots": True,
"tag_search": UserProfile.TAG_SEARCH_STRICT, "tag_search": UserProfile.TAG_SEARCH_STRICT,
"tag_grouping": UserProfile.TAG_GROUPING_ALPHABETICAL,
"display_url": False, "display_url": False,
"display_view_bookmark_action": True, "display_view_bookmark_action": True,
"display_edit_bookmark_action": True, "display_edit_bookmark_action": True,
@ -92,6 +93,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
"enable_preview_images": True, "enable_preview_images": True,
"enable_automatic_html_snapshots": False, "enable_automatic_html_snapshots": False,
"tag_search": UserProfile.TAG_SEARCH_LAX, "tag_search": UserProfile.TAG_SEARCH_LAX,
"tag_grouping": UserProfile.TAG_GROUPING_DISABLED,
"display_url": True, "display_url": True,
"display_view_bookmark_action": False, "display_view_bookmark_action": False,
"display_edit_bookmark_action": False, "display_edit_bookmark_action": False,
@ -141,6 +143,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
form_data["enable_automatic_html_snapshots"], form_data["enable_automatic_html_snapshots"],
) )
self.assertEqual(self.user.profile.tag_search, form_data["tag_search"]) self.assertEqual(self.user.profile.tag_search, form_data["tag_search"])
self.assertEqual(self.user.profile.tag_grouping, form_data["tag_grouping"])
self.assertEqual(self.user.profile.display_url, form_data["display_url"]) self.assertEqual(self.user.profile.display_url, form_data["display_url"])
self.assertEqual( self.assertEqual(
self.user.profile.display_view_bookmark_action, self.user.profile.display_view_bookmark_action,

View file

@ -140,6 +140,43 @@ class TagCloudTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
], ],
) )
def test_group_when_grouping_disabled(self):
profile = self.get_or_create_test_user().profile
profile.tag_grouping = UserProfile.TAG_GROUPING_DISABLED
profile.save()
tags = [
self.setup_tag(name="Cockatoo"),
self.setup_tag(name="Badger"),
self.setup_tag(name="Buffalo"),
self.setup_tag(name="Chihuahua"),
self.setup_tag(name="Alpaca"),
self.setup_tag(name="Coyote"),
self.setup_tag(name="Aardvark"),
self.setup_tag(name="Bumblebee"),
self.setup_tag(name="Armadillo"),
]
self.setup_bookmark(tags=tags)
rendered_template = self.render_template()
self.assertTagGroups(
rendered_template,
[
[
"Aardvark",
"Alpaca",
"Armadillo",
"Badger",
"Buffalo",
"Bumblebee",
"Chihuahua",
"Cockatoo",
"Coyote",
],
],
)
def test_no_duplicate_tag_names(self): def test_no_duplicate_tag_names(self):
tags = [ tags = [
self.setup_tag(name="shared", user=self.setup_user(enable_sharing=True)), self.setup_tag(name="shared", user=self.setup_user(enable_sharing=True)),

View file

@ -264,7 +264,16 @@ class TagGroup:
return f"<{self.char} TagGroup>" return f"<{self.char} TagGroup>"
@staticmethod @staticmethod
def create_tag_groups(tags: Set[Tag]): def create_tag_groups(mode: str, tags: Set[Tag]):
if mode == UserProfile.TAG_GROUPING_ALPHABETICAL:
return TagGroup._create_tag_groups_alphabetical(tags)
elif mode == UserProfile.TAG_GROUPING_DISABLED:
return TagGroup._create_tag_groups_disabled(tags)
else:
raise ValueError(f"{mode} is not a valid tag grouping mode")
@staticmethod
def _create_tag_groups_alphabetical(tags: Set[Tag]):
# Ensure groups, as well as tags within groups, are ordered alphabetically # Ensure groups, as well as tags within groups, are ordered alphabetically
sorted_tags = sorted(tags, key=lambda x: str.lower(x.name)) sorted_tags = sorted(tags, key=lambda x: str.lower(x.name))
group = None group = None
@ -289,6 +298,18 @@ class TagGroup:
groups.append(cjk_group) groups.append(cjk_group)
return groups return groups
@staticmethod
def _create_tag_groups_disabled(tags: Set[Tag]):
if len(tags) == 0:
return []
sorted_tags = sorted(tags, key=lambda x: str.lower(x.name))
group = TagGroup("Ungrouped")
for tag in sorted_tags:
group.tags.append(tag)
return [group]
class TagCloudContext: class TagCloudContext:
request_context = RequestContext request_context = RequestContext
@ -311,7 +332,7 @@ class TagCloudContext:
) )
has_selected_tags = len(unique_selected_tags) > 0 has_selected_tags = len(unique_selected_tags) > 0
unselected_tags = set(unique_tags).symmetric_difference(unique_selected_tags) unselected_tags = set(unique_tags).symmetric_difference(unique_selected_tags)
groups = TagGroup.create_tag_groups(unselected_tags) groups = TagGroup.create_tag_groups(user_profile.tag_grouping, unselected_tags)
self.tags = unique_tags self.tags = unique_tags
self.groups = groups self.groups = groups