from django import forms from django.contrib import admin from django.utils.translation import gettext_lazy as _ from unfold.admin import ModelAdmin, TabularInline from unfold.contrib.forms.widgets import WysiwygWidget from unfold.decorators import display, action from utils.json_editor_field import JsonEditorWidget import json from utils.admin import dovoodi_admin_site,dovoodi_admin_site from ..models import ( Hadis, HadisReference, HadisTag, HadisStatus, ReferenceImage, HadisCollection, HadisInCollection, HadisCorrection ) # Custom Forms for JSON Fields class HadisAdminForm(forms.ModelForm): """Custom form for Hadis with JSON editor widgets""" class Meta: model = Hadis fields = '__all__' widgets = { 'explanation': WysiwygWidget(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for translation JSON field translation_schema = { "type": "array", "title": "Translations", "items": { "type": "object", "title": "Translation", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu"] } }, "text": { # <‑‑ use text, not title "type": "string", "title": "Translation Text" } }, "required": ["language_code", "text"] # <‑‑ update required key } } # Schema for links JSON field (array of objects with title and link) links_schema = { "type": "array", "title": "Links", "items": { "type": "object", "title": "Link", "properties": { "title": { "type": "string", "title": "Link Title" }, "link": { "type": "string", "title": "URL", "format": "uri" } }, "required": ["title", "link"] } } # Schema for title_narrator JSON field title_narrator_schema = { "type": "array", "title": "Title Narrators", "items": { "type": "object", "title": "Title Narrator", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Narrator Text" } }, "required": ["language_code", "text"] } } # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Schema for hadis_status_text JSON field hadis_status_text_schema = { "type": "array", "title": "Status Texts", "items": { "type": "object", "title": "Status Text", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Status Text" } }, "required": ["language_code", "text"] } } # Schema for address JSON field (text is an array of strings) address_schema = { "type": "array", "title": "Addresses", "items": { "type": "object", "title": "Address", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "array", "title": "Address Parts", "items": { "type": "string", "title": "Address Part" } } }, "required": ["language_code", "text"] } } # Schema for explanation JSON field (text is an array of objects with title and detail) explanation_schema = { "type": "array", "title": "Explanations", "items": { "type": "object", "title": "Explanation", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "array", "title": "Explanation Items", "items": { "type": "object", "title": "Explanation Item", "properties": { "title": { "type": "string", "title": "Title" }, "detail": { "type": "string", "title": "Detail", "format": "textarea" } }, "required": ["title", "detail"] } } }, "required": ["language_code", "text"] } } # Schema for explanations JSON field (array of objects with title and description) # explanations_schema = { # "type": "array", # "title": "Explanations", # "items": { # "type": "object", # "title": "Explanation", # "properties": { # "language_code": { # "type": "string", # "title": "Language Code", # "enum": ["en", "fa", "ar", "ur", "ru"], # "options": { # "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] # } # }, # "title": { # "type": "string", # "title": "Title" # }, # "description": { # "type": "string", # "title": "Description" # } # }, # "required": ["language_code", "title", "description"] # } # } # Schema for address_details JSON field (array of objects with text and priority) # address_details_schema = { # "type": "array", # "title": "Address Details", # "items": { # "type": "object", # "title": "Address Detail", # "properties": { # "text": { # "type": "string", # "title": "Address Text" # }, # "priority": { # "type": "integer", # "title": "Priority", # "minimum": 0 # } # }, # "required": ["text", "priority"] # } # } # Apply JSON editor widgets self.fields['translation'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(translation_schema), 'title': 'Translations' }) self.fields['links'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(links_schema), 'title': 'Links' }) self.fields['title_narrator'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_narrator_schema), 'title': 'Title Narrators' }) self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) # self.fields['hadis_status_text'].widget = JsonEditorWidget(attrs={ # 'schema': json.dumps(hadis_status_text_schema), # 'title': 'Status Texts' # }) self.fields['address'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(address_schema), 'title': 'Addresses' }) self.fields['explanation'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(explanation_schema), 'title': 'Explanations' }) # self.fields['explanations'].widget = JsonEditorWidget(attrs={ # 'schema': json.dumps(explanations_schema), # 'title': 'Explanations' # }) # self.fields['address_details'].widget = JsonEditorWidget(attrs={ # 'schema': json.dumps(address_details_schema), # 'title': 'Address Details' # }) # Custom Forms for JSON Fields class HadisCollectionAdminForm(forms.ModelForm): """Custom form for HadisCollection with JSON editor widgets""" class Meta: model = HadisCollection fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for summary JSON field summary_schema = { "type": "array", "title": "Summaries", "items": { "type": "object", "title": "Summary", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Summary Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['summary'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(summary_schema), 'title': 'Summaries' }) class HadisTagAdminForm(forms.ModelForm): """Custom form for HadisTag with JSON editor widgets""" class Meta: model = HadisTag fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) class HadisStatusAdminForm(forms.ModelForm): """Custom form for HadisStatus with JSON editor widgets""" class Meta: model = HadisStatus fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) class HadisReferenceAdminForm(forms.ModelForm): """Custom form for HadisReference with JSON editor widgets""" class Meta: model = HadisReference fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) # Inline Admin Classes class ReferenceImageInline(TabularInline): """Inline for ReferenceImage in HadisReference admin""" model = ReferenceImage extra = 0 fields = ('thumbnail', 'priority') ordering = ('priority',) class HadisReferenceInline(TabularInline): """Inline for HadisReference in Hadis admin""" model = HadisReference extra = 0 fields = ('book_reference',) readonly_fields = ('created_at',) # Main Admin Classes class HadisTagAdmin(ModelAdmin): """Admin for HadisTag model""" form = HadisTagAdminForm list_display = ('get_title_display', 'status', 'created_at') list_filter = ('status', 'created_at') search_fields = ('title',) readonly_fields = ('created_at', 'updated_at') fieldsets = ( (None, { 'fields': ('title', 'status') }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Title'), ordering='title') def get_title_display(self, obj): return self._extract_first_text(obj.title) def _extract_first_text(self, json_data): """Helper to safely extract the first 'text' from a JSON list""" if json_data and isinstance(json_data, list) and len(json_data) > 0: first_item = json_data[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' class HadisStatusAdmin(ModelAdmin): """Admin for HadisStatus model""" form = HadisStatusAdminForm list_display = ('get_title_display', 'color', 'order', 'get_description_display') list_filter = ('color',) search_fields = ('title', 'description') ordering = ('order',) readonly_fields = ('slug',) fieldsets = ( (None, { 'fields': ('title', 'slug', 'color', 'order', 'description') }), ) @display(description=_('Title'), ordering='title') def get_title_display(self, obj): return self._extract_first_text(obj.title) @display(description=_('Description')) def get_description_display(self, obj): return self._extract_first_text(obj.description) def _extract_first_text(self, json_data): """Helper to safely extract the first 'text' from a JSON list""" if json_data and isinstance(json_data, list) and len(json_data) > 0: first_item = json_data[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' class HadisAdmin(ModelAdmin): """Admin for Hadis model""" form = HadisAdminForm list_display = ('number', 'get_title_display', 'category', 'status', 'hadis_status', 'created_at','slug') list_filter = ('status', 'hadis_status', 'category', 'created_at') search_fields = ('title', 'text', 'category__title', 'slug') readonly_fields = ('created_at', 'updated_at', 'share_link', 'slug') ordering = ('category', 'number') inlines = [HadisReferenceInline] filter_horizontal = ('tags',) fieldsets = ( (None, { 'fields': ('category', 'number', 'title', 'title_narrator', 'status', 'slug') }), (_('Content'), { 'fields': ('text', 'translation', 'explanation','description') }), (_('Status & Classification'), { 'fields': ('hadis_status','tags') }), (_('Additional Information'), { 'fields': ('address', 'links', 'share_link'), 'classes': ('collapse',) }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Title'), ordering='title') def get_title_display(self, obj): return self._extract_first_text(obj.title) def _extract_first_text(self, json_data): """Helper to safely extract the first 'text' from a JSON list""" if json_data and isinstance(json_data, list) and len(json_data) > 0: first_item = json_data[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' class HadisReferenceAdmin(ModelAdmin): """Admin for HadisReference model""" form = HadisReferenceAdminForm list_display = ('hadis', 'book_reference', 'created_at') list_filter = ('created_at', 'book_reference') search_fields = ('hadis__title', 'book_reference__title') readonly_fields = ('created_at',) inlines = [ReferenceImageInline] fieldsets = ( (None, { 'fields': ('hadis', 'book_reference','description') }), (_('Timestamps'), { 'fields': ('created_at',), 'classes': ('collapse',) }), ) class ReferenceImageAdmin(ModelAdmin): """Admin for ReferenceImage model""" list_display = ('reference', 'thumbnail', 'priority') list_filter = ('priority',) search_fields = ('reference__hadis__title', 'reference__book__title') ordering = ('reference', 'priority') fieldsets = ( (None, { 'fields': ('reference', 'thumbnail', 'priority') }), ) class HadisInCollectionInline(TabularInline): """Inline for HadisInCollection in HadisCollection admin""" model = HadisInCollection extra = 0 fields = ('hadis', 'order') ordering = ('order',) class HadisCollectionAdmin(ModelAdmin): """Admin for HadisCollection model""" form = HadisCollectionAdminForm list_display = ('get_title_display', 'slug', 'status', 'order', 'created_at') list_filter = ('status', 'created_at') search_fields = ('title', 'slug', 'summary') readonly_fields = ('slug', 'created_at', 'updated_at') ordering = ('order',) inlines = [HadisInCollectionInline] fieldsets = ( (None, { 'fields': ('title', 'slug', 'summary', 'status', 'order') }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Title'), ordering='title') def get_title_display(self, obj): return self._extract_first_text(obj.title) def _extract_first_text(self, json_data): """Helper to safely extract the first 'text' from a JSON list""" if json_data and isinstance(json_data, list) and len(json_data) > 0: first_item = json_data[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' class HadisInCollectionAdmin(ModelAdmin): """Admin for HadisInCollection model""" list_display = ('hadis', 'collection', 'order', 'created_at') list_filter = ('collection', 'created_at') search_fields = ('hadis__title', 'collection__title') readonly_fields = ('created_at',) ordering = ('collection', 'order') fieldsets = ( (None, { 'fields': ('hadis', 'collection', 'order') }), (_('Timestamps'), { 'fields': ('created_at',), 'classes': ('collapse',) }), ) class HadisCorrectionAdminForm(forms.ModelForm): """Custom form for HadisCorrection with JSON editor widgets""" class Meta: model = HadisCorrection fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Schema for translation JSON field translation_schema = { "type": "array", "title": "Translations", "items": { "type": "object", "title": "Translation", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Translation Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) self.fields['translation'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(translation_schema), 'title': 'Translations' }) class HadisCorrectionAdmin(ModelAdmin): """Admin for HadisCorrection model""" form = HadisCorrectionAdminForm list_display = ('hadis', 'get_title_display', 'slug', 'created_at') list_filter = ('created_at', 'hadis__category') search_fields = ('hadis__title', 'title', 'slug') readonly_fields = ('slug', 'created_at', 'updated_at', 'share_link') ordering = ('-created_at',) fieldsets = ( (None, { 'fields': ('hadis', 'title', 'slug') }), (_('Content'), { 'fields': ('description', 'translation') }), (_('Additional Information'), { 'fields': ('share_link',), 'classes': ('collapse',) }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Title'), ordering='title') def get_title_display(self, obj): return self._extract_first_text(obj.title) def _extract_first_text(self, json_data): """Helper to safely extract the first 'text' from a JSON list""" if json_data and isinstance(json_data, list) and len(json_data) > 0: first_item = json_data[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' # Register models with dovoodi admin site dovoodi_admin_site.register(HadisTag, HadisTagAdmin) dovoodi_admin_site.register(HadisStatus, HadisStatusAdmin) dovoodi_admin_site.register(Hadis, HadisAdmin) dovoodi_admin_site.register(HadisReference, HadisReferenceAdmin) dovoodi_admin_site.register(ReferenceImage, ReferenceImageAdmin) dovoodi_admin_site.register(HadisCollection, HadisCollectionAdmin) dovoodi_admin_site.register(HadisInCollection, HadisInCollectionAdmin) dovoodi_admin_site.register(HadisCorrection, HadisCorrectionAdmin)