Repository: sissbruecker/linkding
Pull request #737 (2024-05-17 08:31:24)

Preview auto tags in bookmark form
Shows which tags would automatically be added when creating a bookmark.

![Bildschirmfoto 2024-05-17 um 10 29 53](https://github.com/sissbruecker/linkding/assets/357820/0921e5a3-ec94-432d-aeb0-bca2c6003176)



Patch:
diff --git a/bookmarks/api/routes.py b/bookmarks/api/routes.py
index 41f7321..5249c51 100644
--- a/bookmarks/api/routes.py
+++ b/bookmarks/api/routes.py
@@ -11,6 +11,7 @@ from bookmarks.api.serializers import (
     UserProfileSerializer,
 )
 from bookmarks.models import Bookmark, BookmarkSearch, Tag, User
+from bookmarks.services import auto_tagging
 from bookmarks.services.bookmarks import (
     archive_bookmark,
     unarchive_bookmark,
@@ -107,8 +108,18 @@ class BookmarkViewSet(
         else:
             metadata = website_loader.load_website_metadata(url)
 
+        # Return tags that would be automatically applied to the bookmark
+        profile = request.user.profile
+        auto_tags = []
+        if profile.auto_tagging_rules:
+            auto_tags = auto_tagging.get_tags(profile.auto_tagging_rules, url)
+
         return Response(
-            {"bookmark": existing_bookmark_data, "metadata": metadata.to_dict()},
+            {
+                "bookmark": existing_bookmark_data,
+                "metadata": metadata.to_dict(),
+                "auto_tags": auto_tags,
+            },
             status=status.HTTP_200_OK,
         )
 
diff --git a/bookmarks/styles/bookmark-form.scss b/bookmarks/styles/bookmark-form.scss
index e80af60..1b31754 100644
--- a/bookmarks/styles/bookmark-form.scss
+++ b/bookmarks/styles/bookmark-form.scss
@@ -36,12 +36,11 @@
   .form-input-hint.bookmark-exists {
     display: none;
     color: $warning-color;
+  }
 
-    a {
-      color: $warning-color;
-      text-decoration: underline;
-      font-weight: bold;
-    }
+  .form-input-hint.auto-tags {
+    display: none;
+    color: $success-color;
   }
 
   details.notes textarea {
diff --git a/bookmarks/templates/bookmarks/form.html b/bookmarks/templates/bookmarks/form.html
index 2dd05f8..d9beb61 100644
--- a/bookmarks/templates/bookmarks/form.html
+++ b/bookmarks/templates/bookmarks/form.html
@@ -23,10 +23,10 @@
     <label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label>
     {{ form.tag_string|add_class:"form-input"|attr:"ld-tag-autocomplete"|attr:"autocomplete:off"|attr:"autocapitalize:off" }}
     <div class="form-input-hint">
-      Enter any number of tags separated by space and <strong>without</strong> the hash (#). If a tag does not
-      exist it will be
-      automatically created.
+      Enter any number of tags separated by space and <strong>without</strong> the hash (#).
+      If a tag does not exist it will be automatically created.
     </div>
+    <div class="form-input-hint auto-tags"></div>
     {{ form.tag_string.errors }}
   </div>
   <div class="form-group has-icon-right">
@@ -197,6 +197,18 @@
             } else {
               bookmarkExistsHint.style['display'] = 'none';
             }
+
+            // Preview auto tags
+            const autoTags = data.auto_tags;
+            const autoTagsHint = document.querySelector('.form-input-hint.auto-tags');
+
+            if (autoTags.length > 0) {
+              autoTags.sort();
+              autoTagsHint.style['display'] = 'block';
+              autoTagsHint.innerHTML = `Auto tags: ${autoTags.join(" ")}`;
+            } else {
+              autoTagsHint.style['display'] = 'none';
+            }
           });
       }
 
diff --git a/requirements.txt b/requirements.txt
index 39f477f..c14b38a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -61,7 +61,7 @@ python-dateutil==2.8.2
     # via -r requirements.in
 pytz==2023.3.post1
     # via djangorestframework
-requests==2.31.0
+requests==2.32.0
     # via
     #   -r requirements.in
     #   mozilla-django-oidc


Test patch:
diff --git a/bookmarks/e2e/e2e_test_bookmark_form.py b/bookmarks/e2e/e2e_test_bookmark_form.py
index c4da421..8c031d5 100644
--- a/bookmarks/e2e/e2e_test_bookmark_form.py
+++ b/bookmarks/e2e/e2e_test_bookmark_form.py
@@ -85,3 +85,25 @@ class BookmarkFormE2ETestCase(LinkdingE2ETestCase):
 
             page.get_by_label("URL").fill(bookmark.url)
             expect(details).to_have_attribute("open", value="")
+
+    def test_create_should_preview_auto_tags(self):
+        profile = self.get_or_create_test_user().profile
+        profile.auto_tagging_rules = "github.com dev github"
+        profile.save()
+
+        with sync_playwright() as p:
+            # Open page with URL that should have auto tags
+            browser = self.setup_browser(p)
+            page = browser.new_page()
+            url = self.live_server_url + reverse("bookmarks:new")
+            url += f"?url=https%3A%2F%2Fgithub.com%2Fsissbruecker%2Flinkding"
+            page.goto(url)
+
+            auto_tags_hint = page.locator(".form-input-hint.auto-tags")
+            expect(auto_tags_hint).to_be_visible()
+            expect(auto_tags_hint).to_have_text("Auto tags: dev github")
+
+            # Change to URL without auto tags
+            page.get_by_label("URL").fill("https://example.com")
+
+            expect(auto_tags_hint).to_be_hidden()
diff --git a/bookmarks/tests/test_bookmarks_api.py b/bookmarks/tests/test_bookmarks_api.py
index 4c58d2c..58448df 100644
--- a/bookmarks/tests/test_bookmarks_api.py
+++ b/bookmarks/tests/test_bookmarks_api.py
@@ -742,6 +742,34 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
             self.assertEqual(bookmark.website_description, metadata["description"])
             self.assertIsNone(metadata["preview_image"])
 
+    def test_check_returns_no_auto_tags_if_none_configured(self):
+        self.authenticate()
+
+        url = reverse("bookmarks:bookmark-check")
+        check_url = urllib.parse.quote_plus("https://example.com")
+        response = self.get(
+            f"{url}?url={check_url}", expected_status_code=status.HTTP_200_OK
+        )
+        auto_tags = response.data["auto_tags"]
+
+        self.assertCountEqual(auto_tags, [])
+
+    def test_check_returns_matching_auto_tags(self):
+        self.authenticate()
+
+        profile = self.get_or_create_test_user().profile
+        profile.auto_tagging_rules = "example.com tag1 tag2"
+        profile.save()
+
+        url = reverse("bookmarks:bookmark-check")
+        check_url = urllib.parse.quote_plus("https://example.com")
+        response = self.get(
+            f"{url}?url={check_url}", expected_status_code=status.HTTP_200_OK
+        )
+        auto_tags = response.data["auto_tags"]
+
+        self.assertCountEqual(auto_tags, ["tag1", "tag2"])
+
     def test_can_only_access_own_bookmarks(self):
         self.authenticate()
         self.setup_bookmark()