5c3c27e0af
Раздел «Справка» (категории/статьи/предложка), правки ad_connector и обновление тестов feed под обязательную 2FA. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
127 lines
4.8 KiB
Python
127 lines
4.8 KiB
Python
from django.contrib import admin, messages
|
|
from django.utils import timezone
|
|
from tinymce.widgets import TinyMCE
|
|
from unfold.admin import ModelAdmin
|
|
|
|
from django import forms
|
|
|
|
from apps.help.models import HelpArticle, HelpCategory, HelpContribution
|
|
|
|
|
|
class HelpArticleAdminForm(forms.ModelForm):
|
|
class Meta:
|
|
model = HelpArticle
|
|
fields = '__all__'
|
|
widgets = {'body': TinyMCE()}
|
|
|
|
|
|
@admin.register(HelpCategory)
|
|
class HelpCategoryAdmin(ModelAdmin):
|
|
list_display = ['title', 'slug', 'icon', 'sort_order', 'is_active', 'updated_at']
|
|
list_filter = ['is_active']
|
|
search_fields = ['title', 'slug', 'description']
|
|
prepopulated_fields = {'slug': ('title',)}
|
|
ordering = ['sort_order', 'title']
|
|
|
|
|
|
@admin.register(HelpArticle)
|
|
class HelpArticleAdmin(ModelAdmin):
|
|
form = HelpArticleAdminForm
|
|
list_display = [
|
|
'title', 'category', 'is_published', 'is_featured',
|
|
'sort_order', 'author', 'updated_at',
|
|
]
|
|
list_filter = ['is_published', 'is_featured', 'category']
|
|
search_fields = ['title', 'body', 'slug']
|
|
list_select_related = ['category', 'author']
|
|
prepopulated_fields = {'slug': ('title',)}
|
|
autocomplete_fields = ['category', 'author']
|
|
ordering = ['category__sort_order', 'sort_order', 'title']
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
if not change and not obj.author_id:
|
|
obj.author = request.user
|
|
super().save_model(request, obj, form, change)
|
|
|
|
|
|
class HelpContributionAdminForm(forms.ModelForm):
|
|
class Meta:
|
|
model = HelpContribution
|
|
fields = '__all__'
|
|
widgets = {
|
|
'body': forms.Textarea(attrs={'rows': 12}),
|
|
'review_note': forms.TextInput(attrs={'style': 'width:100%;'}),
|
|
}
|
|
|
|
|
|
@admin.register(HelpContribution)
|
|
class HelpContributionAdmin(ModelAdmin):
|
|
form = HelpContributionAdminForm
|
|
list_display = [
|
|
'title', 'kind', 'category', 'status', 'author',
|
|
'created_at', 'reviewed_by', 'reviewed_at',
|
|
]
|
|
list_filter = ['status', 'kind', 'category']
|
|
search_fields = ['title', 'body', 'author__username', 'author__last_name']
|
|
list_select_related = ['category', 'author', 'reviewed_by', 'article']
|
|
readonly_fields = ['created_at', 'updated_at', 'reviewed_at']
|
|
autocomplete_fields = ['category', 'article', 'author', 'reviewed_by']
|
|
actions = ['approve_contributions', 'reject_contributions']
|
|
ordering = ['-created_at']
|
|
|
|
fieldsets = (
|
|
(None, {
|
|
'fields': ('kind', 'status', 'category', 'article', 'title', 'body'),
|
|
}),
|
|
('Автор и проверка', {
|
|
'fields': (
|
|
'author', 'reviewed_by', 'reviewed_at', 'review_note',
|
|
'created_at', 'updated_at',
|
|
),
|
|
}),
|
|
)
|
|
|
|
@admin.action(description='Одобрить и опубликовать')
|
|
def approve_contributions(self, request, queryset):
|
|
ok = 0
|
|
for contrib in queryset.select_related('article', 'category'):
|
|
if contrib.status == HelpContribution.STATUS_APPROVED:
|
|
continue
|
|
contrib.apply_approval(request.user)
|
|
ok += 1
|
|
self.message_user(
|
|
request,
|
|
f'Одобрено предложений: {ok}',
|
|
messages.SUCCESS,
|
|
)
|
|
|
|
@admin.action(description='Отклонить (нужен комментарий в карточке или общий)')
|
|
def reject_contributions(self, request, queryset):
|
|
ok = 0
|
|
for contrib in queryset:
|
|
if contrib.status != HelpContribution.STATUS_PENDING:
|
|
continue
|
|
note = contrib.review_note or 'Отклонено администратором'
|
|
contrib.reject(request.user, note)
|
|
ok += 1
|
|
self.message_user(
|
|
request,
|
|
f'Отклонено предложений: {ok}',
|
|
messages.WARNING,
|
|
)
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
"""При смене статуса на approved/rejected в карточке — применить логику."""
|
|
if change and 'status' in form.changed_data:
|
|
previous = HelpContribution.objects.filter(pk=obj.pk).values_list('status', flat=True).first()
|
|
if obj.status == HelpContribution.STATUS_APPROVED and previous != HelpContribution.STATUS_APPROVED:
|
|
obj.apply_approval(request.user)
|
|
return
|
|
if obj.status == HelpContribution.STATUS_REJECTED and previous != HelpContribution.STATUS_REJECTED:
|
|
obj.reject(request.user, obj.review_note or 'Отклонено')
|
|
return
|
|
if not obj.reviewed_by_id and obj.status != HelpContribution.STATUS_PENDING:
|
|
obj.reviewed_by = request.user
|
|
obj.reviewed_at = timezone.now()
|
|
super().save_model(request, obj, form, change)
|