Files
guilan-ace-backend/apps/users/admin.py
Amirhossein Khalili 3ec28638d1 feat: replace email flows with in-app notifications
Add recipient notification detail and filtering, a queued admin composer, and contextual automatic titles. Retire email delivery, obsolete auth routes, newsletter data, and related templates and settings.
2026-09-26 12:23:10 +03:30

121 lines
4.9 KiB
Python

from django import forms
from django.contrib import admin, messages
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.template.response import TemplateResponse
from import_export.admin import ImportExportModelAdmin
from simplemde.widgets import SimpleMDEEditor
from apps.users.models import User, University, Major
from apps.users.resources import UserResource
from apps.notifications.tasks import send_manual_notification
from core.admin import SoftDeleteListFilter, BaseModelAdmin
class UserAdminForm(forms.ModelForm):
mobile = forms.CharField(required=True)
bio = forms.CharField(widget=SimpleMDEEditor(), required=False)
student_id = forms.CharField(required=False)
class Meta:
model = User
fields = '__all__'
class ManualNotificationForm(forms.Form):
title = forms.CharField(max_length=200, label="عنوان")
digest = forms.CharField(max_length=500, label="توضیح کوتاه", widget=forms.Textarea(attrs={"rows": 3}))
body_markdown = forms.CharField(required=False, label="متن کامل (Markdown)", widget=SimpleMDEEditor())
@admin.register(User)
class UserAdmin(BaseUserAdmin, BaseModelAdmin, ImportExportModelAdmin):
form = UserAdminForm
resource_class = UserResource
list_display = ('email', 'mobile', 'username', 'university', 'is_email_verified', 'is_mobile_verified', 'date_joined')
list_filter = ('is_email_verified', 'is_mobile_verified', 'is_staff', 'year_of_study', SoftDeleteListFilter)
search_fields = ('email', 'mobile', 'username', 'student_id', 'first_name', 'last_name')
ordering = ('-date_joined',)
fieldsets = (
('Auth Credentials', {'fields': ('username', 'email', 'mobile', 'password')}),
('Personal info', {
'fields': ('first_name', 'last_name', 'student_id', 'university', 'year_of_study', 'major', 'bio', 'profile_picture')
}),
('Permissions', {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions',),
}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
('Email identity', {'fields': ('is_email_verified',)}),
('Mobile Verification', {
'fields': ('is_mobile_verified',)
}),
('Soft Delete', {
'fields': ('is_deleted', 'deleted_at'),
'classes': ('collapse',)
}),
)
add_fieldsets = (
(
'Step 1',
{
'classes': ('wide',),
'fields': ('email', 'mobile', 'student_id', 'password1', 'password2', 'usable_password'),
},
),
)
readonly_fields = ('deleted_at',)
actions = BaseModelAdmin.actions + ['send_notification']
def get_actions(self, request):
actions = super().get_actions(request)
if not request.user.has_perm('users.send_notification'):
actions.pop('send_notification', None)
return actions
@admin.action(description="Send in-app notification to selected users")
def send_notification(self, request, queryset):
if not request.user.has_perm('users.send_notification'):
self.message_user(request, "اجازه ارسال اعلان ندارید.", level=messages.ERROR)
return None
form = ManualNotificationForm(request.POST if 'confirm_send' in request.POST else None)
user_ids = list(queryset.filter(is_active=True, is_deleted=False).values_list('pk', flat=True))
if not user_ids:
self.message_user(request, "کاربر فعال برای ارسال انتخاب نشده است.", level=messages.WARNING)
return None
if 'confirm_send' in request.POST and form.is_valid() and user_ids:
send_manual_notification.delay(
user_ids,
form.cleaned_data['title'],
form.cleaned_data['digest'],
form.cleaned_data['body_markdown'] or None,
)
self.message_user(request, f"ارسال اعلان برای {len(user_ids)} کاربر در صف قرار گرفت.")
return None
return TemplateResponse(request, 'admin/users/send_notification.html', {
**self.admin_site.each_context(request),
'title': 'ارسال اعلان',
'opts': self.model._meta,
'form': form,
'selected_ids': user_ids,
'selected_count': len(user_ids),
'action_checkbox_name': ACTION_CHECKBOX_NAME,
})
@admin.register(University)
class UniversityAdmin(BaseModelAdmin):
list_display = ('name', 'code', 'is_active', 'created_at')
list_filter = ('is_active', SoftDeleteListFilter)
search_fields = ('name', 'code')
@admin.register(Major)
class MajorAdmin(BaseModelAdmin):
list_display = ('name', 'code', 'is_active', 'created_at')
list_filter = ('is_active', SoftDeleteListFilter)
search_fields = ('name', 'code')