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.
250 lines
8.6 KiB
250 lines
8.6 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 dovoodi_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',)
|
|
|
|
dovoodi_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': ()
|
|
}),
|
|
('Detail', {
|
|
'fields': ('title', 'slogan', 'thumbnail', 'pages_count', 'publisher', 'year_of_publication', 'isbn', 'numnber_of_volume')
|
|
}),
|
|
("Summary", {
|
|
'fields': ('summary_title', 'summary')
|
|
}),
|
|
(_('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
|
|
dovoodi_admin_site.register(Book, BookAdmin)
|
|
dovoodi_admin_site.register(PinnedBookCollection, PinnedBookCollectionAdmin)
|
|
dovoodi_admin_site.register(MiddleBookCollection, MiddleBookCollectionAdmin)
|
|
dovoodi_admin_site.register(Category, CategoryAdmin)
|
|
|