diff --git a/bookmarks/admin.py b/bookmarks/admin.py index 7c8dbc0..c351998 100644 --- a/bookmarks/admin.py +++ b/bookmarks/admin.py @@ -1,17 +1,84 @@ -from django.contrib import admin +from django.contrib import admin, messages +from django.contrib.admin import AdminSite +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import User +from django.db.models import Count, QuerySet +from django.utils.translation import ngettext, gettext +from rest_framework.authtoken.admin import TokenAdmin +from rest_framework.authtoken.models import Token from bookmarks.models import Bookmark, Tag +from bookmarks.services.bookmarks import archive_bookmark, unarchive_bookmark + + +class LinkdingAdminSite(AdminSite): + site_header = 'linkding administration' + site_title = 'linkding Admin' + -@admin.register(Bookmark) class AdminBookmark(admin.ModelAdmin): - list_display = ('title', 'url', 'date_added') - search_fields = ('title', 'url', 'tags__name') - list_filter = ('tags',) - ordering = ('-date_added', ) + list_display = ('resolved_title', 'url', 'is_archived', 'owner', 'date_added') + search_fields = ('title', 'description', 'website_title', 'website_description', 'url', 'tags__name') + list_filter = ('owner__username', 'is_archived', 'tags',) + ordering = ('-date_added',) + actions = ['archive_selected_bookmarks', 'unarchive_selected_bookmarks'] + + def archive_selected_bookmarks(self, request, queryset: QuerySet): + for bookmark in queryset: + archive_bookmark(bookmark) + bookmarks_count = queryset.count() + self.message_user(request, ngettext( + '%d bookmark was successfully archived.', + '%d bookmarks were successfully archived.', + bookmarks_count, + ) % bookmarks_count, messages.SUCCESS) + + def unarchive_selected_bookmarks(self, request, queryset: QuerySet): + for bookmark in queryset: + unarchive_bookmark(bookmark) + bookmarks_count = queryset.count() + self.message_user(request, ngettext( + '%d bookmark was successfully unarchived.', + '%d bookmarks were successfully unarchived.', + bookmarks_count, + ) % bookmarks_count, messages.SUCCESS) + -@admin.register(Tag) class AdminTag(admin.ModelAdmin): - list_display = ('name', 'date_added', 'owner') - search_fields = ('name', 'owner__username') - list_filter = ('owner__username', ) - ordering = ('-date_added', ) + list_display = ('name', 'bookmarks_count', 'owner', 'date_added') + search_fields = ('name', 'owner__username') + list_filter = ('owner__username',) + ordering = ('-date_added',) + actions = ['delete_unused_tags'] + + def get_queryset(self, request): + queryset = super().get_queryset(request) + queryset = queryset.annotate(bookmarks_count=Count("bookmark")) + return queryset + + def bookmarks_count(self, obj): + return obj.bookmarks_count + + def delete_unused_tags(self, request, queryset: QuerySet): + unused_tags = queryset.filter(bookmark__isnull=True) + unused_tags_count = unused_tags.count() + for tag in unused_tags: + tag.delete() + + if unused_tags_count > 0: + self.message_user(request, ngettext( + '%d unused tag was successfully deleted.', + '%d unused tags were successfully deleted.', + unused_tags_count, + ) % unused_tags_count, messages.SUCCESS) + else: + self.message_user(request, gettext( + 'There were no unused tags in the selection', + ), messages.SUCCESS) + + +linkding_admin_site = LinkdingAdminSite() +linkding_admin_site.register(Bookmark, AdminBookmark) +linkding_admin_site.register(Tag, AdminTag) +linkding_admin_site.register(User, UserAdmin) +linkding_admin_site.register(Token, TokenAdmin) diff --git a/bookmarks/styles/base.scss b/bookmarks/styles/base.scss index dabb795..8c2388f 100644 --- a/bookmarks/styles/base.scss +++ b/bookmarks/styles/base.scss @@ -52,4 +52,9 @@ h2 { // Remove left padding from first pagination link .pagination .page-item:first-child a { padding-left: 0; +} + +// Override border color for tab block +.tab-block { + border-bottom: solid 1px $border-color; } \ No newline at end of file diff --git a/bookmarks/styles/settings.scss b/bookmarks/styles/settings.scss index 11415de..b60bb96 100644 --- a/bookmarks/styles/settings.scss +++ b/bookmarks/styles/settings.scss @@ -1,6 +1,11 @@ .settings-page { section.content-area { margin-bottom: 2rem; + + h2 { + font-size: 1.0rem; + margin-bottom: 0.8rem; + } } .input-group > input[type=submit] { diff --git a/bookmarks/templates/bookmarks/empty_bookmarks.html b/bookmarks/templates/bookmarks/empty_bookmarks.html index aed6895..376b1fa 100644 --- a/bookmarks/templates/bookmarks/empty_bookmarks.html +++ b/bookmarks/templates/bookmarks/empty_bookmarks.html @@ -2,7 +2,7 @@
You have no bookmarks yet
You can get started by adding bookmarks, - importing your existing bookmarks or configuring the - bookmarklet. + importing your existing bookmarks or configuring the + bookmarklet.
diff --git a/bookmarks/templates/settings/api.html b/bookmarks/templates/settings/api.html new file mode 100644 index 0000000..e8fca55 --- /dev/null +++ b/bookmarks/templates/settings/api.html @@ -0,0 +1,25 @@ +{% extends "bookmarks/layout.html" %} + +{% block content %} +The following token can be used to authenticate 3rd-party applications against the REST API:
+Please treat this token as you would any other credential. Any party with access to this + token can access and manage all your bookmarks.
+If you think that a token was compromised you can revoke (delete) it in the admin panel. After deleting the token, a new one will be generated when you reload this settings page.
+Import bookmarks and tags in the Netscape HTML format. This will execute a sync where new bookmarks are + added and existing ones are updated.
+ +Export all bookmarks in Netscape HTML format.
+ Download (.html) + {% if export_error %} ++ {{ export_error }} +
+Import bookmarks and tags in the Netscape HTML format. This will execute a sync where new bookmarks are - added and existing ones are updated.
- -Export all bookmarks in Netscape HTML format.
- Download (.html) - {% if export_error %} -- {{ export_error }} -
-The bookmarklet is a quick way to add new bookmarks without opening the linkding application - first. Here's how it works:
-Drag the following bookmarklet to your browsers toolbar:
- 📎 Add bookmark -The following token can be used to authenticate 3rd-party applications against the REST API:
-Please treat this token as you would any other credential. Any party with access to this token can access and manage all your bookmarks.
-The bookmarklet is a quick way to add new bookmarks without opening the linkding application + first. Here's how it works:
+Drag the following bookmarklet to your browsers toolbar:
+ 📎 Add bookmark +