update
This commit is contained in:
247
reports/admin.py
Normal file
247
reports/admin.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""
|
||||
Admin configuration for reports app.
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from django import forms
|
||||
from .models import ScamTag, ScamReport, ScamVerification, SiteSettings, TakedownRequest
|
||||
|
||||
|
||||
@admin.register(ScamTag)
|
||||
class ScamTagAdmin(admin.ModelAdmin):
|
||||
"""Scam tag admin."""
|
||||
list_display = ('name', 'slug', 'color')
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
search_fields = ('name',)
|
||||
|
||||
|
||||
@admin.register(ScamReport)
|
||||
class ScamReportAdmin(admin.ModelAdmin):
|
||||
"""Scam report admin."""
|
||||
list_display = ('title', 'reporter', 'scam_type', 'status', 'verification_score', 'created_at')
|
||||
list_filter = ('status', 'scam_type', 'is_public', 'created_at')
|
||||
search_fields = ('title', 'description', 'reported_url', 'reported_email', 'reported_phone')
|
||||
readonly_fields = ('created_at', 'updated_at', 'verified_at', 'reporter_ip')
|
||||
filter_horizontal = ('tags',)
|
||||
date_hierarchy = 'created_at'
|
||||
|
||||
fieldsets = (
|
||||
('Report Information', {
|
||||
'fields': ('title', 'description', 'scam_type', 'tags')
|
||||
}),
|
||||
('Reported Entities', {
|
||||
'fields': ('reported_url', 'reported_email', 'reported_phone', 'reported_company')
|
||||
}),
|
||||
('Reporter', {
|
||||
'fields': ('reporter', 'is_anonymous', 'reporter_ip')
|
||||
}),
|
||||
('Status', {
|
||||
'fields': ('status', 'verification_score', 'is_public', 'verified_at')
|
||||
}),
|
||||
('Evidence', {
|
||||
'fields': ('evidence_files',)
|
||||
}),
|
||||
('Timestamps', {
|
||||
'fields': ('created_at', 'updated_at')
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(ScamVerification)
|
||||
class ScamVerificationAdmin(admin.ModelAdmin):
|
||||
"""Scam verification admin."""
|
||||
list_display = ('report', 'verification_method', 'confidence_score', 'verified_by', 'created_at')
|
||||
list_filter = ('verification_method', 'created_at')
|
||||
search_fields = ('report__title', 'notes')
|
||||
readonly_fields = ('created_at',)
|
||||
|
||||
|
||||
@admin.register(SiteSettings)
|
||||
class SiteSettingsAdmin(admin.ModelAdmin):
|
||||
"""Site settings admin - singleton pattern."""
|
||||
|
||||
def has_add_permission(self, request):
|
||||
# Only allow one instance
|
||||
return not SiteSettings.objects.exists()
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
# Prevent deletion
|
||||
return False
|
||||
|
||||
fieldsets = (
|
||||
('Контактна Информация', {
|
||||
'fields': ('contact_email', 'contact_phone', 'contact_address'),
|
||||
'description': 'Тези настройки се използват навсякъде в сайта - в подножието, страницата за контакти, структурираните данни и др.'
|
||||
}),
|
||||
('Настройки на Имейл Сървър', {
|
||||
'fields': (
|
||||
'email_backend',
|
||||
'email_host',
|
||||
'email_port',
|
||||
'email_use_tls',
|
||||
'email_use_ssl',
|
||||
'email_host_user',
|
||||
'email_host_password',
|
||||
'default_from_email',
|
||||
'email_timeout',
|
||||
),
|
||||
'description': 'Настройки за SMTP сървър. Използват се за всички имейли в платформата - контактни форми, нулиране на пароли, уведомления и др. Паролата се криптира автоматично.'
|
||||
}),
|
||||
('Информация', {
|
||||
'fields': ('updated_at',),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
readonly_fields = ('updated_at',)
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
"""Customize form to handle password field."""
|
||||
form = super().get_form(request, obj, **kwargs)
|
||||
|
||||
# Make password field a password input
|
||||
form.base_fields['email_host_password'].widget = forms.PasswordInput(attrs={
|
||||
'class': 'vTextField',
|
||||
'autocomplete': 'new-password'
|
||||
})
|
||||
|
||||
# Add help text
|
||||
form.base_fields['email_host_password'].help_text = 'Въведете нова парола или оставете празно, за да запазите текущата.'
|
||||
|
||||
return form
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""Handle password encryption and clear cache."""
|
||||
# If password field is empty and we're editing, keep the old password
|
||||
if change and not form.cleaned_data.get('email_host_password'):
|
||||
old_obj = self.model.objects.get(pk=obj.pk)
|
||||
obj.email_host_password = old_obj.email_host_password
|
||||
|
||||
# Validate TLS/SSL are mutually exclusive
|
||||
if obj.email_use_tls and obj.email_use_ssl:
|
||||
from django.contrib import messages
|
||||
messages.warning(request, 'TLS и SSL не могат да бъдат активирани едновременно. SSL е деактивиран, използва се TLS.')
|
||||
obj.email_use_ssl = False
|
||||
|
||||
# Save will encrypt the password if it's provided
|
||||
super().save_model(request, obj, form, change)
|
||||
# Clear email backend cache to reload settings
|
||||
from django.core.cache import cache
|
||||
cache.delete('site_settings')
|
||||
|
||||
def changelist_view(self, request, extra_context=None):
|
||||
# Redirect to the single instance if it exists
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
|
||||
if SiteSettings.objects.exists():
|
||||
obj = SiteSettings.objects.get(pk=1)
|
||||
url = reverse('admin:reports_sitesettings_change', args=[str(obj.pk)])
|
||||
return redirect(url)
|
||||
return super().changelist_view(request, extra_context)
|
||||
|
||||
def response_change(self, request, obj):
|
||||
"""Handle test email button."""
|
||||
if "_test_email" in request.POST:
|
||||
try:
|
||||
from django.core.mail import send_mail
|
||||
from django.contrib import messages
|
||||
|
||||
test_email = request.POST.get('test_email_address', request.user.email)
|
||||
if not test_email:
|
||||
messages.error(request, 'Моля, въведете имейл адрес за тест.')
|
||||
return super().response_change(request, obj)
|
||||
|
||||
# Check if SMTP is configured
|
||||
if obj.email_backend == 'django.core.mail.backends.smtp.EmailBackend' and not obj.email_host:
|
||||
messages.warning(request, 'SMTP сървърът не е конфигуриран. Моля, въведете Email Host преди изпращане на тестов имейл.')
|
||||
return super().response_change(request, obj)
|
||||
|
||||
# Get the connection to check backend type
|
||||
from django.core.mail import get_connection, EmailMessage
|
||||
connection = get_connection()
|
||||
backend_name = connection.__class__.__name__
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(f"Using email backend: {backend_name}")
|
||||
|
||||
# Check underlying backend
|
||||
if hasattr(connection, '_backend') and connection._backend:
|
||||
underlying_backend = connection._backend.__class__.__name__
|
||||
logger.info(f"Underlying backend: {underlying_backend}")
|
||||
|
||||
# Send email using EmailMessage for better error handling
|
||||
email = EmailMessage(
|
||||
subject='Тестов Имейл от Портал за Докладване на Измами',
|
||||
body='Това е тестов имейл за проверка на настройките на имейл сървъра. Ако получавате този имейл, настройките са правилни.',
|
||||
from_email=obj.default_from_email,
|
||||
to=[test_email],
|
||||
connection=connection,
|
||||
)
|
||||
|
||||
result = email.send(fail_silently=False)
|
||||
|
||||
logger.info(f"Email send result: {result} (1 = success, 0 = failed)")
|
||||
|
||||
# Check which backend was actually used
|
||||
if 'Console' in backend_name or 'console' in str(connection.__class__.__module__):
|
||||
messages.warning(request, f'Имейлът е изпратен чрез конзолен backend (за разработка). За реално изпращане, конфигурирайте SMTP настройките. Backend: {backend_name}')
|
||||
else:
|
||||
messages.success(request, f'Тестов имейл изпратен успешно до {test_email}! Използван backend: {backend_name}')
|
||||
except Exception as e:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.exception("Error sending test email")
|
||||
error_msg = str(e)
|
||||
if 'authentication failed' in error_msg.lower():
|
||||
messages.error(request, f'Грешка при удостоверяване: Проверете потребителското име и паролата.')
|
||||
elif 'connection' in error_msg.lower() or 'timeout' in error_msg.lower():
|
||||
messages.error(request, f'Грешка при свързване: Проверете SMTP сървъра и порта.')
|
||||
else:
|
||||
messages.error(request, f'Грешка при изпращане на тестов имейл: {error_msg}')
|
||||
|
||||
return super().response_change(request, obj)
|
||||
|
||||
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
||||
extra_context = extra_context or {}
|
||||
if object_id:
|
||||
obj = self.get_object(request, object_id)
|
||||
if obj:
|
||||
extra_context['show_test_email'] = True
|
||||
return super().changeform_view(request, object_id, form_url, extra_context)
|
||||
|
||||
|
||||
@admin.register(TakedownRequest)
|
||||
class TakedownRequestAdmin(admin.ModelAdmin):
|
||||
"""Takedown request admin."""
|
||||
list_display = ('report', 'requester_name', 'requester_email', 'status', 'created_at', 'reviewed_by')
|
||||
list_filter = ('status', 'created_at', 'reviewed_at')
|
||||
search_fields = ('requester_name', 'requester_email', 'report__title', 'reason')
|
||||
readonly_fields = ('created_at', 'updated_at', 'ip_address', 'user_agent')
|
||||
date_hierarchy = 'created_at'
|
||||
|
||||
fieldsets = (
|
||||
('Информация за Доклада', {
|
||||
'fields': ('report',)
|
||||
}),
|
||||
('Информация за Заявителя', {
|
||||
'fields': ('requester_name', 'requester_email', 'requester_phone')
|
||||
}),
|
||||
('Детайли на Заявката', {
|
||||
'fields': ('reason', 'evidence')
|
||||
}),
|
||||
('Статус и Преглед', {
|
||||
'fields': ('status', 'reviewed_by', 'review_notes', 'reviewed_at')
|
||||
}),
|
||||
('Техническа Информация', {
|
||||
'fields': ('ip_address', 'user_agent', 'created_at', 'updated_at'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if change and 'status' in form.changed_data and obj.status in ['approved', 'rejected']:
|
||||
from django.utils import timezone
|
||||
obj.reviewed_by = request.user
|
||||
obj.reviewed_at = timezone.now()
|
||||
super().save_model(request, obj, form, change)
|
||||
Reference in New Issue
Block a user