|
|
@ -5,11 +5,13 @@ from functools import lru_cache |
|
|
from django import forms |
|
|
from django import forms |
|
|
from django.conf import settings |
|
|
from django.conf import settings |
|
|
from django.contrib.humanize.templatetags.humanize import intcomma |
|
|
from django.contrib.humanize.templatetags.humanize import intcomma |
|
|
from django.urls import reverse |
|
|
|
|
|
|
|
|
from django.urls import reverse, path |
|
|
from django.utils.safestring import mark_safe |
|
|
from django.utils.safestring import mark_safe |
|
|
from django.utils.translation import gettext_lazy as _ |
|
|
from django.utils.translation import gettext_lazy as _ |
|
|
from django.views.generic import RedirectView |
|
|
from django.views.generic import RedirectView |
|
|
from django.utils.translation import get_language |
|
|
from django.utils.translation import get_language |
|
|
|
|
|
from django.http import JsonResponse |
|
|
|
|
|
from django.views.decorators.http import require_POST |
|
|
|
|
|
|
|
|
# Unfold Imports |
|
|
# Unfold Imports |
|
|
from unfold.sites import UnfoldAdminSite |
|
|
from unfold.sites import UnfoldAdminSite |
|
|
@ -35,17 +37,20 @@ def admin_url_generator(request, url_name): |
|
|
Dynamically generates admin URLs based on the current active panel. |
|
|
Dynamically generates admin URLs based on the current active panel. |
|
|
Usage in settings.py: lambda request: admin_url_generator(request, "app_model_changelist") |
|
|
Usage in settings.py: lambda request: admin_url_generator(request, "app_model_changelist") |
|
|
""" |
|
|
""" |
|
|
# 1. Determine the current namespace using the robust check |
|
|
|
|
|
|
|
|
# Ensure admin sites are created and URLs are registered |
|
|
|
|
|
_ = project_admin_site.urls # Access URLs to ensure site is created |
|
|
|
|
|
_ = dovoodi_admin_site.urls # Access URLs to ensure site is created |
|
|
|
|
|
|
|
|
|
|
|
# 1. Determine the current namespace |
|
|
if is_dovoodi_panel(request): |
|
|
if is_dovoodi_panel(request): |
|
|
namespace = 'dovoodi_admin' |
|
|
namespace = 'dovoodi_admin' |
|
|
else: |
|
|
else: |
|
|
# Default to the main admin |
|
|
|
|
|
namespace = 'imam_javad_admin' |
|
|
namespace = 'imam_javad_admin' |
|
|
|
|
|
|
|
|
# 2. Construct the view name |
|
|
# 2. Construct the view name |
|
|
full_view_name = f"{namespace}:{url_name}" |
|
|
full_view_name = f"{namespace}:{url_name}" |
|
|
|
|
|
|
|
|
# 3. Resolve the URL |
|
|
|
|
|
|
|
|
# 3. Try Django URL reversal |
|
|
try: |
|
|
try: |
|
|
return reverse(full_view_name) |
|
|
return reverse(full_view_name) |
|
|
except Exception: |
|
|
except Exception: |
|
|
@ -58,6 +63,14 @@ def dashboard_callback(request, context): |
|
|
def variables(request): |
|
|
def variables(request): |
|
|
return {"plausible_domain": getattr(settings, 'PLAUSIBLE_DOMAIN', '')} |
|
|
return {"plausible_domain": getattr(settings, 'PLAUSIBLE_DOMAIN', '')} |
|
|
|
|
|
|
|
|
|
|
|
# Toggle sidebar view for Unfold compatibility |
|
|
|
|
|
@require_POST |
|
|
|
|
|
def toggle_sidebar(request): |
|
|
|
|
|
"""Toggle sidebar state for Unfold admin interface""" |
|
|
|
|
|
# This is a simple view that just returns success |
|
|
|
|
|
# The actual sidebar state is handled client-side |
|
|
|
|
|
return JsonResponse({'status': 'success'}) |
|
|
|
|
|
|
|
|
# --------------------------------------------------------- |
|
|
# --------------------------------------------------------- |
|
|
# 2. Custom Login Form |
|
|
# 2. Custom Login Form |
|
|
# --------------------------------------------------------- |
|
|
# --------------------------------------------------------- |
|
|
@ -95,6 +108,18 @@ class FormulaAdminSite(UnfoldAdminSite): |
|
|
super().__init__(*args, **kwargs) |
|
|
super().__init__(*args, **kwargs) |
|
|
# Set login form after initialization to avoid circular import |
|
|
# Set login form after initialization to avoid circular import |
|
|
self.login_form = LoginForm.get_form() |
|
|
self.login_form = LoginForm.get_form() |
|
|
|
|
|
|
|
|
|
|
|
def get_form(self, request, obj=None, **kwargs): |
|
|
|
|
|
"""Override to ensure form is properly initialized""" |
|
|
|
|
|
form = super().get_form(request, obj, **kwargs) |
|
|
|
|
|
return form |
|
|
|
|
|
|
|
|
|
|
|
def get_urls(self): |
|
|
|
|
|
urls = super().get_urls() |
|
|
|
|
|
custom_urls = [ |
|
|
|
|
|
path('toggle_sidebar/', toggle_sidebar, name='toggle_sidebar'), |
|
|
|
|
|
] |
|
|
|
|
|
return custom_urls + urls |
|
|
|
|
|
|
|
|
def _get_colors(self, key, *args): |
|
|
def _get_colors(self, key, *args): |
|
|
"""Override colors for Imam Javad admin panel with green theme""" |
|
|
"""Override colors for Imam Javad admin panel with green theme""" |
|
|
@ -167,6 +192,18 @@ class DovoodiAdminSite(UnfoldAdminSite): |
|
|
super().__init__(*args, **kwargs) |
|
|
super().__init__(*args, **kwargs) |
|
|
# Set login form after initialization to avoid circular import |
|
|
# Set login form after initialization to avoid circular import |
|
|
self.login_form = LoginForm.get_form() |
|
|
self.login_form = LoginForm.get_form() |
|
|
|
|
|
|
|
|
|
|
|
def get_form(self, request, obj=None, **kwargs): |
|
|
|
|
|
"""Override to ensure form is properly initialized""" |
|
|
|
|
|
form = super().get_form(request, obj, **kwargs) |
|
|
|
|
|
return form |
|
|
|
|
|
|
|
|
|
|
|
def get_urls(self): |
|
|
|
|
|
urls = super().get_urls() |
|
|
|
|
|
custom_urls = [ |
|
|
|
|
|
path('toggle_sidebar/', toggle_sidebar, name='toggle_sidebar'), |
|
|
|
|
|
] |
|
|
|
|
|
return custom_urls + urls |
|
|
|
|
|
|
|
|
def _get_colors(self, key, *args): |
|
|
def _get_colors(self, key, *args): |
|
|
"""Override colors for Dovoodi admin panel with blue/teal theme matching frontend""" |
|
|
"""Override colors for Dovoodi admin panel with blue/teal theme matching frontend""" |
|
|
@ -240,6 +277,7 @@ class AdminSitePlaceholder(UnfoldAdminSite): |
|
|
self._site_class = site_class |
|
|
self._site_class = site_class |
|
|
self._name = name |
|
|
self._name = name |
|
|
self._real_instance = None |
|
|
self._real_instance = None |
|
|
|
|
|
self._registry = {} # Store registrations until real instance is created |
|
|
|
|
|
|
|
|
# 2. THE FIX: Copy visual attributes immediately so Templates see them! |
|
|
# 2. THE FIX: Copy visual attributes immediately so Templates see them! |
|
|
self.site_header = getattr(site_class, 'site_header', 'Django Admin') |
|
|
self.site_header = getattr(site_class, 'site_header', 'Django Admin') |
|
|
@ -258,12 +296,28 @@ class AdminSitePlaceholder(UnfoldAdminSite): |
|
|
for attr in ['site_header', 'site_title', 'index_title', 'site_subheader']: |
|
|
for attr in ['site_header', 'site_title', 'index_title', 'site_subheader']: |
|
|
if hasattr(self._real_instance, attr): |
|
|
if hasattr(self._real_instance, attr): |
|
|
setattr(self, attr, getattr(self._real_instance, attr)) |
|
|
setattr(self, attr, getattr(self._real_instance, attr)) |
|
|
|
|
|
|
|
|
|
|
|
# Copy any existing registrations from the placeholder to the real instance |
|
|
|
|
|
if hasattr(self, '_registry'): |
|
|
|
|
|
for model, admin_class in self._registry.items(): |
|
|
|
|
|
self._real_instance.register(model, admin_class) |
|
|
|
|
|
|
|
|
|
|
|
# Replace the global reference with the real instance |
|
|
|
|
|
import sys |
|
|
|
|
|
current_module = sys.modules[__name__] |
|
|
|
|
|
if hasattr(current_module, self._name): |
|
|
|
|
|
setattr(current_module, self._name, self._real_instance) |
|
|
|
|
|
|
|
|
return self._real_instance |
|
|
return self._real_instance |
|
|
|
|
|
|
|
|
def __getattr__(self, name): |
|
|
def __getattr__(self, name): |
|
|
# Delegate all attribute access to the real instance for proper CSS and template loading |
|
|
# Delegate all attribute access to the real instance for proper CSS and template loading |
|
|
return getattr(self._get_real_instance(), name) |
|
|
return getattr(self._get_real_instance(), name) |
|
|
|
|
|
|
|
|
|
|
|
def get_form(self, request, obj=None, **kwargs): |
|
|
|
|
|
"""Delegate get_form to the real admin site instance""" |
|
|
|
|
|
return self._get_real_instance().get_form(request, obj, **kwargs) |
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs): |
|
|
def __call__(self, *args, **kwargs): |
|
|
return self._get_real_instance()(*args, **kwargs) |
|
|
return self._get_real_instance()(*args, **kwargs) |
|
|
|
|
|
|
|
|
@ -277,9 +331,141 @@ class AdminSitePlaceholder(UnfoldAdminSite): |
|
|
def each_context(self, request): |
|
|
def each_context(self, request): |
|
|
return self._get_real_instance().each_context(request) |
|
|
return self._get_real_instance().each_context(request) |
|
|
|
|
|
|
|
|
# Create placeholder instances that will be replaced with real instances when Django is ready |
|
|
|
|
|
project_admin_site = AdminSitePlaceholder(FormulaAdminSite, 'imam_javad_admin') |
|
|
|
|
|
dovoodi_admin_site = AdminSitePlaceholder(DovoodiAdminSite, 'dovoodi_admin') |
|
|
|
|
|
|
|
|
def register(self, model_or_iterable, admin_class=None, **options): |
|
|
|
|
|
"""Store registrations in placeholder until real instance is created""" |
|
|
|
|
|
if isinstance(model_or_iterable, (list, tuple)): |
|
|
|
|
|
for model in model_or_iterable: |
|
|
|
|
|
self.register(model, admin_class, **options) |
|
|
|
|
|
else: |
|
|
|
|
|
model = model_or_iterable |
|
|
|
|
|
if model in self._registry: |
|
|
|
|
|
# If already registered, update the admin class |
|
|
|
|
|
self._registry[model] = admin_class |
|
|
|
|
|
else: |
|
|
|
|
|
self._registry[model] = admin_class |
|
|
|
|
|
|
|
|
|
|
|
# Also register with the real instance if it exists |
|
|
|
|
|
if self._real_instance is not None: |
|
|
|
|
|
self._real_instance.register(model, admin_class, **options) |
|
|
|
|
|
|
|
|
|
|
|
# Create lazy-loading admin site instances that properly inherit from AdminSite |
|
|
|
|
|
class LazyAdminSite(UnfoldAdminSite): |
|
|
|
|
|
def __init__(self, site_class, name): |
|
|
|
|
|
# Don't call super().__init__() to avoid creating the real instance yet |
|
|
|
|
|
self._site_class = site_class |
|
|
|
|
|
self._name = name |
|
|
|
|
|
self._instance = None |
|
|
|
|
|
# Set basic attributes that Django expects for isinstance checks |
|
|
|
|
|
self.name = name |
|
|
|
|
|
|
|
|
|
|
|
def _force_init(self): |
|
|
|
|
|
"""Force initialization immediately""" |
|
|
|
|
|
if self._instance is None: |
|
|
|
|
|
self._instance = self._site_class(name=self._name) |
|
|
|
|
|
# Copy all attributes to this instance |
|
|
|
|
|
for attr in dir(self._instance): |
|
|
|
|
|
if not attr.startswith('_') and attr not in ('register', 'unregister', 'is_registered'): |
|
|
|
|
|
try: |
|
|
|
|
|
setattr(self, attr, getattr(self._instance, attr)) |
|
|
|
|
|
except (AttributeError, TypeError): |
|
|
|
|
|
pass |
|
|
|
|
|
def _ensure_instance(self): |
|
|
|
|
|
"""Ensure the real instance exists""" |
|
|
|
|
|
if self._instance is None: |
|
|
|
|
|
self._instance = self._site_class(name=self._name) |
|
|
|
|
|
# Copy essential attributes to this lazy wrapper for compatibility |
|
|
|
|
|
essential_attrs = ['site_header', 'site_title', 'index_title', 'site_url', 'login_template'] |
|
|
|
|
|
for attr in essential_attrs: |
|
|
|
|
|
if hasattr(self._instance, attr): |
|
|
|
|
|
setattr(self, attr, getattr(self._instance, attr)) |
|
|
|
|
|
|
|
|
|
|
|
def _get_instance(self): |
|
|
|
|
|
self._ensure_instance() |
|
|
|
|
|
return self._instance |
|
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, name): |
|
|
|
|
|
self._ensure_instance() |
|
|
|
|
|
return getattr(self._instance, name) |
|
|
|
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs): |
|
|
|
|
|
return self._get_instance()(*args, **kwargs) |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
def urls(self): |
|
|
|
|
|
"""Ensure URLs are accessed to create the instance""" |
|
|
|
|
|
return self._get_instance().urls |
|
|
|
|
|
|
|
|
|
|
|
def get_urls(self): |
|
|
|
|
|
"""Delegate get_urls to ensure proper URL registration""" |
|
|
|
|
|
return self._get_instance().get_urls() |
|
|
|
|
|
|
|
|
|
|
|
def register(self, model_or_iterable, admin_class=None, **options): |
|
|
|
|
|
"""Register models with the real admin site instance""" |
|
|
|
|
|
self._ensure_instance() |
|
|
|
|
|
return self._instance.register(model_or_iterable, admin_class, **options) |
|
|
|
|
|
|
|
|
|
|
|
# Create lazy admin site instances |
|
|
|
|
|
project_admin_site = LazyAdminSite(FormulaAdminSite, 'imam_javad_admin') |
|
|
|
|
|
dovoodi_admin_site = LazyAdminSite(DovoodiAdminSite, 'dovoodi_admin') |
|
|
|
|
|
|
|
|
|
|
|
# from django.contrib.admin.sites import AdminSite |
|
|
|
|
|
|
|
|
|
|
|
# class LazyAdminSite(AdminSite): |
|
|
|
|
|
# """Lazy wrapper that initializes the real admin site on first access""" |
|
|
|
|
|
|
|
|
|
|
|
# def __init__(self, site_class, name): |
|
|
|
|
|
# # Don't call super().__init__() - avoid app registry checks at import time |
|
|
|
|
|
# self._site_class = site_class |
|
|
|
|
|
# self._name = name |
|
|
|
|
|
# self._instance = None |
|
|
|
|
|
|
|
|
|
|
|
# def _get_instance(self): |
|
|
|
|
|
# """Initialize the real site on first access (after Django is ready)""" |
|
|
|
|
|
# if self._instance is None: |
|
|
|
|
|
# self._instance = self._site_class(name=self._name) |
|
|
|
|
|
# return self._instance |
|
|
|
|
|
|
|
|
|
|
|
# def __getattr__(self, name): |
|
|
|
|
|
# """Delegate all attribute access to the real instance""" |
|
|
|
|
|
# if name.startswith('_'): |
|
|
|
|
|
# raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") |
|
|
|
|
|
# return getattr(self._get_instance(), name) |
|
|
|
|
|
|
|
|
|
|
|
# @property |
|
|
|
|
|
# def urls(self): |
|
|
|
|
|
# """Required for Django URL routing""" |
|
|
|
|
|
# return self._get_instance().urls |
|
|
|
|
|
|
|
|
|
|
|
# @property |
|
|
|
|
|
# def media(self): |
|
|
|
|
|
# """Expose the media from the real admin instance""" |
|
|
|
|
|
# return self._get_instance().media |
|
|
|
|
|
|
|
|
|
|
|
# @property |
|
|
|
|
|
# def form_class(self): |
|
|
|
|
|
# """Expose form class""" |
|
|
|
|
|
# return self._get_instance().form_class |
|
|
|
|
|
|
|
|
|
|
|
# def has_permission(self, request): |
|
|
|
|
|
# """Check if user has admin permission""" |
|
|
|
|
|
# return self._get_instance().has_permission(request) |
|
|
|
|
|
|
|
|
|
|
|
# def each_context(self, request): |
|
|
|
|
|
# """Return context for admin templates""" |
|
|
|
|
|
# return self._get_instance().each_context(request) |
|
|
|
|
|
|
|
|
|
|
|
# def get_urls(self): |
|
|
|
|
|
# """Get admin URLs""" |
|
|
|
|
|
# return self._get_instance().get_urls() |
|
|
|
|
|
|
|
|
|
|
|
# def register(self, model, admin_class=None, **options): |
|
|
|
|
|
# """Register a model with the admin site""" |
|
|
|
|
|
# return self._get_instance().register(model, admin_class, **options) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# # Create lazy instances (NO initialization at import time!) |
|
|
|
|
|
# project_admin_site = LazyAdminSite(FormulaAdminSite, 'imam_javad_admin') |
|
|
|
|
|
# dovoodi_admin_site = LazyAdminSite(DovoodiAdminSite, 'dovoodi_admin') |
|
|
|
|
|
|
|
|
# Function to replace placeholders with real instances when Django is ready |
|
|
# Function to replace placeholders with real instances when Django is ready |
|
|
def replace_placeholders_with_real_sites(): |
|
|
def replace_placeholders_with_real_sites(): |
|
|
|