You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

244 lines
8.4 KiB

from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.utils.html import format_html
from ajaxdatatable.admin import AjaxDatatable
from unfold.admin import ModelAdmin
from django.contrib.admin import SimpleListFilter
from unfold.decorators import display, action
from django import forms
from utils.admin import project_admin_site
from apps.library.models import *
class BookCollectionAdmin(ModelAdmin):
list_display = ('title', 'display_position', 'status', 'order')
list_filter = ('status', 'display_position')
search_fields = ('title',)
project_admin_site.register(BookCollection, BookCollectionAdmin)
class BookAdminForm(forms.ModelForm):
class Meta:
model = Book
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Make thumbnail field required in the form
self.fields['thumbnail'].required = True
class BookAdmin(ModelAdmin):
form = BookAdminForm
list_display = ('title', 'display_categories', 'display_collections', 'status', 'view_count', 'created_at')
list_filter = ('status', 'pin', 'file_type', 'created_at', 'updated_at')
search_fields = ('title', 'slug', 'summary', 'description')
# autocomplete_fields = ('categories', 'collections', )
list_filter_submit = True
warn_unsaved_form = True
change_form_show_cancel_button = True
save_as = True
@display(description=_('Categories'))
def display_categories(self, obj):
categories = obj.categories.all()
if categories:
return ', '.join([category.title for category in categories])
return '-'
@display(description=_('Collections'))
def display_collections(self, obj):
collections = obj.collections.all()
if collections:
return ', '.join([collection.title for collection in collections])
return '-'
fieldsets = (
(None, {
'fields': ('title', 'summary', 'description', 'thumbnail', 'pages_count')
}),
(_('Status'), {
'fields': ('status', 'pin')
}),
(_('File Information'), {
'fields': ('file_type', 'book_file')
}),
(_('Relations'), {
'fields': ('categories', 'collections')
}),
(_('Statistics'), {
'fields': ('view_count', 'download_count')
}),
)
class BookCollectionAdminBase(ModelAdmin):
"""Base admin class for all book collection types"""
list_display = ('get_title', 'status', 'order', 'count_books')
list_filter = ('status', 'order')
search_fields = ('title',)
autocomplete_fields = ('books',)
ordering = ('order',)
list_filter_submit = True
warn_unsaved_form = True
change_form_show_cancel_button = True
fieldsets = (
(None, {
'fields': ('title', 'summary', 'status', 'order')
}),
(_('Books'), {
'fields': ('books',)
}),
)
exclude = ('display_position',)
@display(description=_('Title'))
def get_title(self, obj):
return str(obj.title)
# @display(description=_("Status"), ordering="status")
# def status_badge(self, obj):
# if obj.status:
# return format_html(
# '<span class="bg-green-100 text-green-800 px-2 py-1 rounded-full text-xs font-medium">{}</span>',
# _("Active")
# )
# return format_html(
# '<span class="bg-red-100 text-red-800 px-2 py-1 rounded-full text-xs font-medium">{}</span>',
# _("Inactive")
# )
@display(description=_('Number of Books'))
def count_books(self, obj):
count = obj.books.count()
if count > 0:
url = reverse('admin:library_book_changelist') + f'?collections__id__exact={obj.id}'
return format_html('<a href="{}">{}</a>', url, count)
return count
class PinnedBookCollectionAdmin(BookCollectionAdminBase):
"""Admin for pinned book collections only"""
list_before_template = "admin/library/pinnedbookcollection/change_list_before.html"
fieldsets = (
(None, {
'fields': ('title', 'summary', 'status', 'order', 'pin_top')
}),
(_('Books'), {
'fields': ('books',)
}),
)
def get_queryset(self, request):
# Only show pinned collections
return super().get_queryset(request).filter(display_position=BookCollection.DisplayPosition.PINNED)
def save_model(self, request, obj, form, change):
# Ensure the display_position is always set to PINNED
obj.display_position = BookCollection.DisplayPosition.PINNED
super().save_model(request, obj, form, change)
class MiddleBookCollectionAdmin(BookCollectionAdminBase):
"""Admin for middle section book collections only"""
def get_queryset(self, request):
# Only show middle section collections
return super().get_queryset(request).filter(display_position=BookCollection.DisplayPosition.MIDDLE)
# def has_add_permission(self, request):
# # Check if a middle collection already exists
# exists = BookCollection.objects.filter(display_position=BookCollection.DisplayPosition.MIDDLE).exists()
# # Only allow adding if no middle collection exists
# return not exists
# def has_delete_permission(self, request, obj=None):
# # Prevent deletion of the middle collection
# return False
def save_model(self, request, obj, form, change):
# Ensure the display_position is always set to MIDDLE
obj.display_position = BookCollection.DisplayPosition.MIDDLE
super().save_model(request, obj, form, change)
class CategoryAdmin(ModelAdmin):
list_display = ('title', 'slug', 'status_badge', 'count_books', 'created_at')
list_filter = ('status', 'created_at', 'updated_at')
search_fields = ('title', 'slug')
list_filter_submit = True
warn_unsaved_form = True
change_form_show_cancel_button = True
# Custom actions
actions_list = ['mark_as_active', 'mark_as_inactive']
actions_row = ['toggle_status']
fieldsets = (
(None, {
'fields': ('title', 'slug', 'status')
}),
(_('Timestamps'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ('created_at', 'updated_at')
@display(description=_("Status"), ordering="status")
def status_badge(self, obj):
if obj.status:
return format_html(
'<span class="bg-green-100 text-green-800 px-2 py-1 rounded-full text-xs font-medium">{}</span>',
_("Active")
)
return format_html(
'<span class="bg-red-100 text-red-800 px-2 py-1 rounded-full text-xs font-medium">{}</span>',
_("Inactive")
)
@display(description=_('Number of Books'))
def count_books(self, obj):
count = obj.books_count
if count > 0:
url = reverse('admin:library_book_changelist') + f'?categories__id__exact={obj.id}'
return format_html('<a href="{}">{}</a>', url, count)
return count
@action(description=_("Mark selected categories as active"))
def mark_as_active(self, request, queryset):
updated = queryset.update(status=True)
self.message_user(request, _("%(count)d categories were successfully marked as active.") % {"count": updated})
@action(description=_("Mark selected categories as inactive"))
def mark_as_inactive(self, request, queryset):
updated = queryset.update(status=False)
self.message_user(request, _("%(count)d categories were successfully marked as inactive.") % {"count": updated})
@action(description=_("Toggle status"))
def toggle_status(self, request, obj):
obj.status = not obj.status
obj.save(update_fields=["status"])
status_text = _("active") if obj.status else _("inactive")
self.message_user(request, _("Category '%(title)s' is now %(status)s.") % {"title": obj.title, "status": status_text})
# Register models with the custom admin site
project_admin_site.register(Book, BookAdmin)
project_admin_site.register(PinnedBookCollection, PinnedBookCollectionAdmin)
project_admin_site.register(MiddleBookCollection, MiddleBookCollectionAdmin)
project_admin_site.register(Category, CategoryAdmin)