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.
 
 

200 lines
7.6 KiB

from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin, StackedInline, TabularInline
from unfold.decorators import display, action
from apps.cms.models.post import (
PostCategory,
PostTag,
Post,
PostRevision,
PostComment,
PostLike,
MediaAsset,
)
from utils.admin import project_admin_site
class PostRevisionInline(TabularInline):
model = PostRevision
extra = 0
fields = ('id', 'author', 'title', 'status', 'reviewed_by', 'reviewed_at', 'created_at')
readonly_fields = ('id', 'author', 'title', 'status', 'reviewed_by', 'reviewed_at', 'created_at')
show_change_link = True
class PostCommentInline(TabularInline):
model = PostComment
extra = 0
fields = ('author_name', 'author_email', 'user', 'content', 'status', 'created_at')
readonly_fields = ('created_at',)
@admin.register(Post, site=project_admin_site)
class PostAdmin(ModelAdmin):
list_display = (
'title',
'post_type_badge',
'language',
'category',
'status_badge',
'author_type',
'author',
'institution',
'views_count',
'likes_count',
'is_featured',
'publish_date',
)
list_display_links = ('title', 'post_type_badge', 'category', 'author')
list_filter = ('post_type', 'author_type', 'language', 'status', 'is_featured', 'category', 'institution')
search_fields = ('title', 'excerpt', 'content', 'author__email', 'author__fullname', 'institution__name')
prepopulated_fields = {'slug': ('title',)}
readonly_fields = ('reading_time_minutes', 'views_count', 'likes_count', 'reviewed_at', 'created_at', 'updated_at')
inlines = [PostRevisionInline, PostCommentInline]
actions = ['publish_posts', 'mark_pending_review', 'archive_posts', 'toggle_featured']
fieldsets = (
(_('Article Content'), {
'fields': ('title', 'slug', 'post_type', 'category', 'language', 'status', 'is_featured')
}),
(_('Editorial & Moderation'), {
'fields': ('rejection_reason', 'reviewed_by', 'reviewed_at')
}),
(_('Author & Affiliation'), {
'fields': ('author_type', 'author', 'institution', 'author_role_label', 'author_custom_avatar')
}),
(_('Media & Excerpt'), {
'fields': ('featured_image', 'excerpt', 'content', 'tags')
}),
(_('Metrics & Scheduling'), {
'fields': ('reading_time_minutes', 'views_count', 'likes_count', 'publish_date', 'created_at', 'updated_at')
}),
)
@display(description=_('Type'))
def post_type_badge(self, obj):
return obj.get_post_type_display()
@display(description=_('Status'), label={
Post.Status.PUBLISHED: "success",
Post.Status.PENDING_REVIEW: "warning",
Post.Status.DRAFT: "info",
Post.Status.REJECTED: "danger",
Post.Status.ARCHIVED: "secondary",
})
def status_badge(self, obj):
return obj.get_status_display()
@action(description=_('Publish selected posts'))
def publish_posts(self, request, queryset):
count = queryset.update(status=Post.Status.PUBLISHED)
self.message_user(request, _(f"{count} post(s) published successfully."))
@action(description=_('Mark selected posts as Pending Review'))
def mark_pending_review(self, request, queryset):
count = queryset.update(status=Post.Status.PENDING_REVIEW)
self.message_user(request, _(f"{count} post(s) marked as pending review."))
@action(description=_('Archive selected posts'))
def archive_posts(self, request, queryset):
count = queryset.update(status=Post.Status.ARCHIVED)
self.message_user(request, _(f"{count} post(s) archived."))
@action(description=_('Toggle featured status for selected posts'))
def toggle_featured(self, request, queryset):
for item in queryset:
item.is_featured = not item.is_featured
item.save(update_fields=['is_featured'])
self.message_user(request, _("Featured status updated."))
@admin.register(PostCategory, site=project_admin_site)
class PostCategoryAdmin(ModelAdmin):
list_display = ('name', 'slug', 'language', 'created_at')
list_display_links = ('name', 'slug')
list_filter = ('language',)
search_fields = ('name', 'description')
prepopulated_fields = {'slug': ('name',)}
@admin.register(PostTag, site=project_admin_site)
class PostTagAdmin(ModelAdmin):
list_display = ('name', 'slug')
list_display_links = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(PostComment, site=project_admin_site)
class PostCommentAdmin(ModelAdmin):
list_display = ('author_display', 'post', 'status_badge', 'created_at')
list_display_links = ('author_display', 'post')
list_filter = ('status', 'created_at')
search_fields = ('author_name', 'author_email', 'content', 'post__title')
actions = ['approve_comments', 'mark_as_spam']
@display(description=_('Author'))
def author_display(self, obj):
return obj.author_name or (obj.user.fullname if obj.user else "Anonymous")
@display(description=_('Status'), label={
PostComment.Status.APPROVED: "success",
PostComment.Status.PENDING: "warning",
PostComment.Status.SPAM: "danger",
})
def status_badge(self, obj):
return obj.get_status_display()
@action(description=_('Approve selected comments'))
def approve_comments(self, request, queryset):
count = queryset.update(status=PostComment.Status.APPROVED)
self.message_user(request, _(f"{count} comment(s) approved."))
@action(description=_('Mark selected comments as spam'))
def mark_as_spam(self, request, queryset):
count = queryset.update(status=PostComment.Status.SPAM)
self.message_user(request, _(f"{count} comment(s) marked as spam."))
@admin.register(PostLike, site=project_admin_site)
class PostLikeAdmin(ModelAdmin):
list_display = ('user', 'post', 'created_at')
list_display_links = ('user', 'post')
search_fields = ('user__email', 'user__fullname', 'post__title')
readonly_fields = ('created_at',)
@admin.register(MediaAsset, site=project_admin_site)
class MediaAssetAdmin(ModelAdmin):
list_display = ('title', 'media_type', 'file_size', 'uploaded_by', 'created_at')
list_display_links = ('title', 'uploaded_by')
list_filter = ('media_type', 'created_at')
search_fields = ('title', 'uploaded_by__email')
readonly_fields = ('created_at',)
@admin.register(PostRevision, site=project_admin_site)
class PostRevisionAdmin(ModelAdmin):
list_display = ('id', 'post', 'author', 'status_badge', 'created_at', 'reviewed_by', 'reviewed_at')
list_display_links = ('id', 'post')
list_filter = ('status', 'created_at')
search_fields = ('title', 'content', 'author__email', 'post__title')
readonly_fields = ('created_at', 'updated_at', 'reviewed_at')
actions = ['approve_revisions']
@display(description=_('Status'), label={
PostRevision.Status.APPROVED: "success",
PostRevision.Status.PENDING: "warning",
PostRevision.Status.REJECTED: "danger",
})
def status_badge(self, obj):
return obj.get_status_display()
@action(description=_('Approve and merge selected revisions'))
def approve_revisions(self, request, queryset):
count = 0
for rev in queryset.filter(status=PostRevision.Status.PENDING):
rev.apply_to_post(editor=request.user)
count += 1
self.message_user(request, _(f"{count} revision(s) approved and merged into live posts."))