diff --git a/bookmarks/api/serializers.py b/bookmarks/api/serializers.py index 38f19b9..a7ab3ef 100644 --- a/bookmarks/api/serializers.py +++ b/bookmarks/api/serializers.py @@ -125,6 +125,7 @@ class UserProfileSerializer(serializers.ModelSerializer): "bookmark_link_target", "web_archive_integration", "tag_search", + "tag_hierarchy", "enable_sharing", "enable_public_sharing", "enable_favicons", diff --git a/bookmarks/migrations/0037_userprofile_tag_hierarchy.py b/bookmarks/migrations/0037_userprofile_tag_hierarchy.py new file mode 100644 index 0000000..5c4c2fd --- /dev/null +++ b/bookmarks/migrations/0037_userprofile_tag_hierarchy.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-05-17 07:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookmarks", "0036_userprofile_auto_tagging_rules"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="tag_hierarchy", + field=models.BooleanField(default=False), + ), + ] diff --git a/bookmarks/models.py b/bookmarks/models.py index a8120d5..feb755f 100644 --- a/bookmarks/models.py +++ b/bookmarks/models.py @@ -404,6 +404,7 @@ class UserProfile(models.Model): blank=False, default=TAG_GROUPING_ALPHABETICAL, ) + tag_hierarchy = models.BooleanField(default=False, null=False) enable_sharing = models.BooleanField(default=False, null=False) enable_public_sharing = models.BooleanField(default=False, null=False) enable_favicons = models.BooleanField(default=False, null=False) @@ -433,6 +434,7 @@ class UserProfileForm(forms.ModelForm): "web_archive_integration", "tag_search", "tag_grouping", + "tag_hierarchy", "enable_sharing", "enable_public_sharing", "enable_favicons", diff --git a/bookmarks/queries.py b/bookmarks/queries.py index 6235e2f..2ced2cb 100644 --- a/bookmarks/queries.py +++ b/bookmarks/queries.py @@ -66,7 +66,14 @@ def _base_bookmarks_query( query_set = query_set.filter(conditions) for tag_name in query["tag_names"]: - query_set = query_set.filter(tags__name__iexact=tag_name) + if profile.tag_hierarchy: + # Filter for tags with the exact name or a parent tag + query_set = query_set.filter( + Q(tags__name__iexact=tag_name) | Q(tags__name__istartswith=f"{tag_name}/") + ) + else: + # Filter for tags with the exact name + query_set = query_set.filter(tags__name__iexact=tag_name) # Untagged bookmarks if query["untagged"]: diff --git a/bookmarks/templates/settings/general.html b/bookmarks/templates/settings/general.html index 15ba2f4..7b5959a 100644 --- a/bookmarks/templates/settings/general.html +++ b/bookmarks/templates/settings/general.html @@ -118,6 +118,16 @@ If disabled, tags will not be grouped. +
+ +
+ Allow to create tags with a hierarchical structure. + When enabled, tags can be organized in a tree-like structure, where each tag can have one or more parent tags separated by a slash character (/). Searching for a parent tag will also include bookmarks with child tags. +
+
Auto Tagging diff --git a/package-lock.json b/package-lock.json index 899a253..8d4926b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linkding", - "version": "1.30.0", + "version": "1.31.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linkding", - "version": "1.30.0", + "version": "1.31.0", "license": "MIT", "dependencies": { "@rollup/plugin-node-resolve": "^15.2.3",