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.
248 lines
9.0 KiB
248 lines
9.0 KiB
from django import forms
|
|
from django.contrib import admin
|
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
|
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
|
|
from django.contrib.auth.models import Group
|
|
from django.db import models
|
|
from django.utils.html import format_html
|
|
from django.utils.translation import gettext_lazy as _, ngettext
|
|
from django.templatetags.static import static
|
|
|
|
from rest_framework.authtoken.models import TokenProxy
|
|
from unfold.admin import ModelAdmin, StackedInline
|
|
from unfold.contrib.forms.widgets import WysiwygWidget
|
|
from unfold.decorators import display
|
|
from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm
|
|
from unfold.sections import TableSection
|
|
from unfold.contrib.filters.admin import RangeDateTimeFilter
|
|
|
|
# Import Models
|
|
from apps.account.models import User, ClientUser, StudentUser, ProfessorUser, LocationHistory
|
|
|
|
# Import Admin Sites from utils
|
|
from utils.admin import project_admin_site, dovoodi_admin_site , is_dovoodi_panel
|
|
from apps.account.admin.location import LocationHistoryInline
|
|
from unfold.widgets import UnfoldAdminSelectWidget
|
|
|
|
# =========================================================
|
|
# 1. Base User Admin (Logic Shared by all User types)
|
|
# =========================================================
|
|
|
|
class UserAdminCreationForm(UserCreationForm):
|
|
class Meta(UserCreationForm.Meta):
|
|
model = User
|
|
fields = ("fullname", "email")
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
if 'fullname' in self.fields:
|
|
self.fields['fullname'].required = True
|
|
if 'email' in self.fields:
|
|
self.fields['email'].required = True
|
|
|
|
def clean_email(self):
|
|
email = self.cleaned_data.get('email')
|
|
if User.objects.filter(email=email).exists():
|
|
raise forms.ValidationError(_("A user with this email already exists."))
|
|
return email
|
|
|
|
class UserAdminChangeForm(UserChangeForm):
|
|
class Meta(UserChangeForm.Meta):
|
|
model = User
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
if 'fullname' in self.fields:
|
|
self.fields['fullname'].required = True
|
|
if 'email' in self.fields:
|
|
self.fields['email'].required = True
|
|
|
|
class UserAdmin(ModelAdmin, BaseUserAdmin):
|
|
form = UserAdminChangeForm
|
|
add_form = UserAdminCreationForm
|
|
change_password_form = AdminPasswordChangeForm
|
|
compressed_fields = False
|
|
list_before_template = "account/user_list_section.html"
|
|
list_display = ('fullname', 'email', 'is_active', 'display_date_joined',)
|
|
ordering = ("-id",)
|
|
search_fields = ('email', 'fullname', 'username',)
|
|
list_filter = [
|
|
"is_active",
|
|
"is_staff",
|
|
("last_login", RangeDateTimeFilter),
|
|
("date_joined", RangeDateTimeFilter),
|
|
]
|
|
inlines = [LocationHistoryInline]
|
|
|
|
add_fieldsets = (
|
|
(None, {
|
|
'classes': ('wide',),
|
|
'fields': (('fullname', 'email'), 'phone_number', 'birthdate', 'gender', 'avatar', 'skill', 'info'),
|
|
}),
|
|
(_('Location'), {
|
|
'fields': ('city', 'country'),
|
|
'classes': ('collapse',),
|
|
}),
|
|
(_('Password'), {
|
|
'fields': ('password1', 'password2'),
|
|
'classes': ('collapse',),
|
|
}),
|
|
(_('Permissions'), {
|
|
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups'),
|
|
'classes': ('collapse',),
|
|
}),
|
|
)
|
|
|
|
fieldsets = (
|
|
(None, {"fields": ("email", "fullname")}),
|
|
(_("Basic Information"), {
|
|
"fields": ("gender", "avatar", "phone_number", "birthdate", 'info', 'skill', "password"),
|
|
"classes": ["tab"],
|
|
}),
|
|
(_('Country & City'), {
|
|
'fields': ('city', 'country'),
|
|
"classes": ["tab"],
|
|
}),
|
|
(_('Device Information'), {
|
|
'fields': ('device_id', 'device_os', 'fcm', 'language',),
|
|
"classes": ["tab"],
|
|
}),
|
|
(_('Authentication'), {
|
|
'fields': ('display_auth_token',),
|
|
"classes": ["tab"],
|
|
}),
|
|
(_('Permissions'), {
|
|
'fields': ('user_type', 'is_active', 'is_staff', 'groups'),
|
|
"classes": ["tab"],
|
|
}),
|
|
(_('Important dates'), {
|
|
'fields': ('last_login', 'date_joined', 'deleted_at'),
|
|
"classes": ["tab"],
|
|
}),
|
|
)
|
|
|
|
formfield_overrides = {
|
|
models.TextField: {"widget": WysiwygWidget}
|
|
}
|
|
radio_fields = {"gender": admin.HORIZONTAL}
|
|
readonly_fields = ["last_login", "date_joined", "display_auth_token"]
|
|
|
|
def get_fieldsets(self, request, obj=None):
|
|
# UserAdmin.get_fieldsets returns add_fieldsets when obj is None
|
|
fieldsets = super().get_fieldsets(request, obj)
|
|
if is_dovoodi_panel(request):
|
|
new_fieldsets = []
|
|
for name, options in fieldsets:
|
|
# Hide entire Permissions section
|
|
if name == _('Permissions'):
|
|
continue
|
|
|
|
new_options = options.copy()
|
|
# Hide skill field inside "Basic Information" (Edit) or the titleless section (Add)
|
|
if name == _("Basic Information") or name is None:
|
|
fields = list(new_options.get("fields", []))
|
|
new_fields = []
|
|
for f in fields:
|
|
if isinstance(f, (list, tuple)):
|
|
inner_f = [inner for inner in f if inner != 'skill']
|
|
if inner_f:
|
|
new_fields.append(tuple(inner_f))
|
|
elif f != 'skill':
|
|
new_fields.append(f)
|
|
new_options["fields"] = tuple(new_fields)
|
|
|
|
new_fieldsets.append((name, new_options))
|
|
return tuple(new_fieldsets)
|
|
return fieldsets
|
|
|
|
@display(description=_("Date Joined"))
|
|
def display_date_joined(self, instance: User):
|
|
return instance.date_joined.strftime("%Y-%m-%d %H:%M") if instance.date_joined else "-"
|
|
|
|
@display(description=_("Last Login"))
|
|
def display_last_login(self, instance: User):
|
|
return instance.last_login.strftime("%Y-%m-%d %H:%M") if instance.last_login else "-"
|
|
|
|
@display(description=_("Authentication Token"))
|
|
def display_auth_token(self, instance: User):
|
|
from rest_framework.authtoken.models import Token
|
|
try:
|
|
token, created = Token.objects.get_or_create(user=instance)
|
|
return format_html('<code style="word-break: break-all;">{}</code>', token.key)
|
|
except Exception as e:
|
|
return format_html('<span class="error">{}</span>', str(e))
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.filter(email__isnull=False)
|
|
|
|
# =========================================================
|
|
# 2. Specific User Type Admins
|
|
# =========================================================
|
|
|
|
class GuestUserAdmin(UserAdmin):
|
|
list_display = ('device_id', 'device_os', 'is_active', 'display_date_joined',)
|
|
|
|
def has_add_permission(self, request):
|
|
if '_popup' in request.GET and request.GET['_popup'] == '1':
|
|
return True
|
|
return False
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.filter(email__isnull=True)
|
|
|
|
@display(description=_("Date Joined"))
|
|
def display_date_joined(self, instance: User):
|
|
return instance.date_joined.strftime("%Y-%m-%d %H:%M") if instance.date_joined else "-"
|
|
|
|
|
|
|
|
class GroupAdmin(BaseGroupAdmin, ModelAdmin):
|
|
list_display = ('name', 'permissions_count')
|
|
search_fields = ('name',)
|
|
ordering = ('name',)
|
|
filter_horizontal = ('permissions',)
|
|
|
|
fieldsets = (
|
|
(None, {'fields': ('name',)}),
|
|
(_('Permissions'), {'fields': ('permissions',), 'classes': ['tab']}),
|
|
)
|
|
|
|
@display(description=_("Permissions"))
|
|
def permissions_count(self, obj):
|
|
count = obj.permissions.count()
|
|
return ngettext("{count} permission", "{count} permissions", count).format(count=count) if count > 0 else "-"
|
|
|
|
|
|
# =========================================================
|
|
# 3. Registrations (SAFE METHOD)
|
|
# =========================================================
|
|
|
|
# A. DEFAULT DJANGO ADMIN (SAFE REGISTRATION)
|
|
# This is required because plugins like 'django-filer' expect User to be registered here.
|
|
try:
|
|
admin.site.unregister(User)
|
|
except admin.sites.NotRegistered:
|
|
pass
|
|
|
|
try:
|
|
admin.site.register(User, UserAdmin)
|
|
except admin.sites.AlreadyRegistered:
|
|
pass
|
|
|
|
# B. PROJECT ADMIN SITE (Imam Javad)
|
|
project_admin_site.register(User, UserAdmin)
|
|
project_admin_site.register(ClientUser, GuestUserAdmin)
|
|
project_admin_site.register(Group, GroupAdmin)
|
|
|
|
# C. DOVOODI ADMIN SITE
|
|
dovoodi_admin_site.register(User, UserAdmin)
|
|
dovoodi_admin_site.register(ClientUser, GuestUserAdmin)
|
|
dovoodi_admin_site.register(Group, GroupAdmin)
|
|
|
|
# D. Unregister TokenProxy safely (Cleaner UI)
|
|
try:
|
|
admin.site.unregister(TokenProxy)
|
|
except admin.sites.NotRegistered:
|
|
pass
|