248 lines
12 KiB
Python
248 lines
12 KiB
Python
"""
|
||
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)
|