197 lines
8.5 KiB
Python
197 lines
8.5 KiB
Python
"""
|
|
Base Admin Mixins for account and site/sector filtering.
|
|
"""
|
|
from django.contrib import admin, messages
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.db import models, transaction
|
|
|
|
|
|
class AccountAdminMixin:
|
|
"""Mixin for admin classes that need account filtering"""
|
|
|
|
def get_queryset(self, request):
|
|
"""Filter queryset by account"""
|
|
qs = super().get_queryset(request)
|
|
# Check for account field
|
|
has_account_field = hasattr(qs.model, 'account')
|
|
if has_account_field:
|
|
# Superuser and developers can see all
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return qs
|
|
# Filter by user's account
|
|
user_account = getattr(request.user, 'account', None)
|
|
if user_account:
|
|
return qs.filter(account=user_account)
|
|
return qs
|
|
|
|
def has_view_permission(self, request, obj=None):
|
|
"""Check if user can view this object"""
|
|
if obj:
|
|
obj_account = getattr(obj, 'account', None)
|
|
if obj_account:
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
user_account = getattr(request.user, 'account', None)
|
|
if user_account:
|
|
return obj_account == user_account
|
|
return super().has_view_permission(request, obj)
|
|
|
|
def has_change_permission(self, request, obj=None):
|
|
"""Check if user can change this object"""
|
|
if obj:
|
|
obj_account = getattr(obj, 'account', None)
|
|
if obj_account:
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
user_account = getattr(request.user, 'account', None)
|
|
if user_account:
|
|
return obj_account == user_account
|
|
return super().has_change_permission(request, obj)
|
|
|
|
def has_delete_permission(self, request, obj=None):
|
|
"""Check if user can delete this object"""
|
|
if obj:
|
|
obj_account = getattr(obj, 'account', None)
|
|
if obj_account:
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
user_account = getattr(request.user, 'account', None)
|
|
if user_account:
|
|
return obj_account == user_account
|
|
return super().has_delete_permission(request, obj)
|
|
|
|
|
|
|
|
class SiteSectorAdminMixin:
|
|
"""Mixin for admin classes that need site/sector filtering"""
|
|
|
|
def get_queryset(self, request):
|
|
"""Filter queryset by site/sector access"""
|
|
qs = super().get_queryset(request)
|
|
if hasattr(qs.model, 'site') and hasattr(qs.model, 'sector'):
|
|
# Superuser and developers can see all
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return qs
|
|
# Filter by accessible sites
|
|
if hasattr(request.user, 'get_accessible_sites'):
|
|
accessible_sites = request.user.get_accessible_sites()
|
|
return qs.filter(site__in=accessible_sites)
|
|
return qs
|
|
|
|
def has_view_permission(self, request, obj=None):
|
|
"""Check if user can view this object"""
|
|
if obj and hasattr(obj, 'site'):
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
if hasattr(request.user, 'get_accessible_sites'):
|
|
accessible_sites = request.user.get_accessible_sites()
|
|
return obj.site in accessible_sites
|
|
return super().has_view_permission(request, obj)
|
|
|
|
def has_change_permission(self, request, obj=None):
|
|
"""Check if user can change this object"""
|
|
if obj and hasattr(obj, 'site'):
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
if hasattr(request.user, 'get_accessible_sites'):
|
|
accessible_sites = request.user.get_accessible_sites()
|
|
return obj.site in accessible_sites
|
|
return super().has_change_permission(request, obj)
|
|
|
|
def has_delete_permission(self, request, obj=None):
|
|
"""Check if user can delete this object"""
|
|
if obj and hasattr(obj, 'site'):
|
|
if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()):
|
|
return True
|
|
if hasattr(request.user, 'get_accessible_sites'):
|
|
accessible_sites = request.user.get_accessible_sites()
|
|
return obj.site in accessible_sites
|
|
return super().has_delete_permission(request, obj)
|
|
|
|
|
|
|
|
# ============================================================================
|
|
# Custom ModelAdmin for Sidebar Fix + Delete Fix
|
|
# ============================================================================
|
|
|
|
from unfold.admin import ModelAdmin as UnfoldModelAdmin
|
|
|
|
|
|
class Igny8ModelAdmin(UnfoldModelAdmin):
|
|
"""
|
|
Custom ModelAdmin that:
|
|
1. Ensures sidebar_navigation is set correctly on ALL pages
|
|
2. Uses standard Django filters
|
|
|
|
"""
|
|
|
|
# Standard Django filters
|
|
pass
|
|
|
|
def _inject_sidebar_context(self, request, extra_context=None):
|
|
"""Helper to inject custom sidebar into context"""
|
|
if extra_context is None:
|
|
extra_context = {}
|
|
|
|
# Get our custom sidebar from the admin site
|
|
from igny8_core.admin.site import admin_site
|
|
|
|
# CRITICAL: Get the full Unfold context (includes all branding, form classes, etc.)
|
|
# This is what makes the logo/title appear properly
|
|
unfold_context = admin_site.each_context(request)
|
|
|
|
# Get the current path to detect active group
|
|
current_path = request.path
|
|
|
|
sidebar_navigation = admin_site.get_sidebar_list(request)
|
|
|
|
# Detect active group and expand it by setting collapsible=False
|
|
for group in sidebar_navigation:
|
|
group_is_active = False
|
|
for item in group.get('items', []):
|
|
# Unfold stores resolved link in 'link_callback', original lambda in 'link'
|
|
item_link = item.get('link_callback') or item.get('link', '')
|
|
# Convert to string (handles lazy proxy objects and ensures it's a string)
|
|
try:
|
|
item_link = str(item_link) if item_link else ''
|
|
except:
|
|
item_link = ''
|
|
# Skip if it's a function representation (e.g., "<function ...>")
|
|
if item_link.startswith('<'):
|
|
continue
|
|
# Check if current path matches this item's link
|
|
if item_link and current_path.startswith(item_link):
|
|
item['active'] = True
|
|
group_is_active = True
|
|
|
|
# If any item in this group is active, expand the group
|
|
if group_is_active:
|
|
group['collapsible'] = False # Expanded state
|
|
else:
|
|
group['collapsible'] = True # Collapsed state
|
|
|
|
# Merge Unfold context with our custom sidebar
|
|
unfold_context['sidebar_navigation'] = sidebar_navigation
|
|
unfold_context['available_apps'] = admin_site.get_app_list(request, app_label=None)
|
|
unfold_context['app_list'] = unfold_context['available_apps']
|
|
|
|
# Merge with any existing extra_context
|
|
unfold_context.update(extra_context)
|
|
|
|
return unfold_context
|
|
|
|
def changelist_view(self, request, extra_context=None):
|
|
"""Override to inject custom sidebar"""
|
|
extra_context = self._inject_sidebar_context(request, extra_context)
|
|
return super().changelist_view(request, extra_context)
|
|
|
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
|
"""Override to inject custom sidebar"""
|
|
extra_context = self._inject_sidebar_context(request, extra_context)
|
|
return super().change_view(request, object_id, form_url, extra_context)
|
|
|
|
def add_view(self, request, form_url='', extra_context=None):
|
|
"""Override to inject custom sidebar"""
|
|
extra_context = self._inject_sidebar_context(request, extra_context)
|
|
return super().add_view(request, form_url, extra_context)
|