mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-08 11:18:28 +02:00
Allow searching for untagged bookmarks (#226)
Co-authored-by: Sascha Ißbrücker <sascha.issbruecker@gmail.com>
This commit is contained in:
@@ -55,6 +55,12 @@ def _base_bookmarks_query(user: User, query_string: str) -> QuerySet:
|
|||||||
tags__name__iexact=tag_name
|
tags__name__iexact=tag_name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Untagged bookmarks
|
||||||
|
if query['untagged']:
|
||||||
|
query_set = query_set.filter(
|
||||||
|
tags=None
|
||||||
|
)
|
||||||
|
|
||||||
# Sort by date added
|
# Sort by date added
|
||||||
query_set = query_set.order_by('-date_added')
|
query_set = query_set.order_by('-date_added')
|
||||||
|
|
||||||
@@ -90,11 +96,15 @@ def _parse_query_string(query_string):
|
|||||||
keywords = query_string.strip().split(' ')
|
keywords = query_string.strip().split(' ')
|
||||||
keywords = [word for word in keywords if word]
|
keywords = [word for word in keywords if word]
|
||||||
|
|
||||||
search_terms = [word for word in keywords if word[0] != '#']
|
search_terms = [word for word in keywords if word[0] != '#' and word[0] != '!']
|
||||||
tag_names = [word[1:] for word in keywords if word[0] == '#']
|
tag_names = [word[1:] for word in keywords if word[0] == '#']
|
||||||
tag_names = unique(tag_names, str.lower)
|
tag_names = unique(tag_names, str.lower)
|
||||||
|
|
||||||
|
# Special search commands
|
||||||
|
untagged = '!untagged' in keywords
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'search_terms': search_terms,
|
'search_terms': search_terms,
|
||||||
'tag_names': tag_names,
|
'tag_names': tag_names,
|
||||||
|
'untagged': untagged,
|
||||||
}
|
}
|
||||||
|
@@ -15,3 +15,7 @@
|
|||||||
.text-gray-dark {
|
.text-gray-dark {
|
||||||
color: $gray-color-dark;
|
color: $gray-color-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.align-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
@@ -33,8 +33,10 @@
|
|||||||
|
|
||||||
{# Tag list #}
|
{# Tag list #}
|
||||||
<section class="content-area column col-4 hide-md">
|
<section class="content-area column col-4 hide-md">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header align-baseline">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<a href="?q=!untagged" class="text-gray text-sm">Show Untagged</a>
|
||||||
</div>
|
</div>
|
||||||
{% tag_cloud tags %}
|
{% tag_cloud tags %}
|
||||||
</section>
|
</section>
|
||||||
|
@@ -33,8 +33,10 @@
|
|||||||
|
|
||||||
{# Tag list #}
|
{# Tag list #}
|
||||||
<section class="content-area column col-4 hide-md">
|
<section class="content-area column col-4 hide-md">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header align-baseline">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<a href="?q=!untagged" class="text-gray text-sm">Show Untagged</a>
|
||||||
</div>
|
</div>
|
||||||
{% tag_cloud tags %}
|
{% tag_cloud tags %}
|
||||||
</section>
|
</section>
|
||||||
|
@@ -156,3 +156,8 @@ class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
response = self.client.get(reverse('bookmarks:index'))
|
response = self.client.get(reverse('bookmarks:index'))
|
||||||
|
|
||||||
self.assertVisibleBookmarks(response, visible_bookmarks, '_self')
|
self.assertVisibleBookmarks(response, visible_bookmarks, '_self')
|
||||||
|
|
||||||
|
def test_should_show_link_for_untagged_bookmarks(self):
|
||||||
|
response = self.client.get(reverse('bookmarks:index'))
|
||||||
|
|
||||||
|
self.assertContains(response, '<a href="?q=!untagged" class="text-gray text-sm">Show Untagged</a>')
|
||||||
|
@@ -289,6 +289,60 @@ class QueriesTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
self.assertEqual(bookmark.tag_string, None)
|
self.assertEqual(bookmark.tag_string, None)
|
||||||
self.assertTrue(bookmark.tag_projection)
|
self.assertTrue(bookmark.tag_projection)
|
||||||
|
|
||||||
|
def test_query_bookmarks_untagged_should_return_untagged_bookmarks_only(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
untagged_bookmark = self.setup_bookmark()
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_bookmarks(self.user, '!untagged')
|
||||||
|
self.assertCountEqual(list(query), [untagged_bookmark])
|
||||||
|
|
||||||
|
def test_query_bookmarks_untagged_should_be_combinable_with_search_terms(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
untagged_bookmark = self.setup_bookmark(title='term1')
|
||||||
|
self.setup_bookmark(title='term2')
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_bookmarks(self.user, '!untagged term1')
|
||||||
|
self.assertCountEqual(list(query), [untagged_bookmark])
|
||||||
|
|
||||||
|
def test_query_bookmarks_untagged_should_not_be_combinable_with_tags(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
self.setup_bookmark()
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_bookmarks(self.user, f'!untagged #{tag.name}')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
def test_query_archived_bookmarks_untagged_should_return_untagged_bookmarks_only(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
untagged_bookmark = self.setup_bookmark(is_archived=True)
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmarks(self.user, '!untagged')
|
||||||
|
self.assertCountEqual(list(query), [untagged_bookmark])
|
||||||
|
|
||||||
|
def test_query_archived_bookmarks_untagged_should_be_combinable_with_search_terms(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
untagged_bookmark = self.setup_bookmark(is_archived=True, title='term1')
|
||||||
|
self.setup_bookmark(is_archived=True, title='term2')
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmarks(self.user, '!untagged term1')
|
||||||
|
self.assertCountEqual(list(query), [untagged_bookmark])
|
||||||
|
|
||||||
|
def test_query_archived_bookmarks_untagged_should_not_be_combinable_with_tags(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
self.setup_bookmark(is_archived=True)
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmarks(self.user, f'!untagged #{tag.name}')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
def test_query_bookmark_tags_should_return_all_tags_for_empty_query(self):
|
def test_query_bookmark_tags_should_return_all_tags_for_empty_query(self):
|
||||||
self.setup_tag_search_data()
|
self.setup_tag_search_data()
|
||||||
|
|
||||||
@@ -460,3 +514,35 @@ class QueriesTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
query = queries.query_archived_bookmark_tags(self.user, '')
|
query = queries.query_archived_bookmark_tags(self.user, '')
|
||||||
|
|
||||||
self.assertQueryResult(query, [self.get_tags_from_bookmarks(owned_bookmarks)])
|
self.assertQueryResult(query, [self.get_tags_from_bookmarks(owned_bookmarks)])
|
||||||
|
|
||||||
|
def test_query_bookmark_tags_untagged_should_never_return_any_tags(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
self.setup_bookmark()
|
||||||
|
self.setup_bookmark(title='term1')
|
||||||
|
self.setup_bookmark(title='term1', tags=[tag])
|
||||||
|
self.setup_bookmark(tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_bookmark_tags(self.user, '!untagged')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
query = queries.query_bookmark_tags(self.user, '!untagged term1')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
query = queries.query_bookmark_tags(self.user, f'!untagged #{tag.name}')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
def test_query_archived_bookmark_tags_untagged_should_never_return_any_tags(self):
|
||||||
|
tag = self.setup_tag()
|
||||||
|
self.setup_bookmark(is_archived=True)
|
||||||
|
self.setup_bookmark(is_archived=True, title='term1')
|
||||||
|
self.setup_bookmark(is_archived=True, title='term1', tags=[tag])
|
||||||
|
self.setup_bookmark(is_archived=True, tags=[tag])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmark_tags(self.user, '!untagged')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmark_tags(self.user, '!untagged term1')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
|
||||||
|
query = queries.query_archived_bookmark_tags(self.user, f'!untagged #{tag.name}')
|
||||||
|
self.assertCountEqual(list(query), [])
|
||||||
|
Reference in New Issue
Block a user