fixed a bunch of things, hopefully didn't break things

This commit is contained in:
pacnpal
2024-11-05 21:51:02 +00:00
parent 2e8a725933
commit eb5d2acab5
30 changed files with 944 additions and 569 deletions

View File

@@ -1,17 +1,27 @@
from typing import Any, Dict, Optional, Type, Union, cast
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.contenttypes.models import ContentType
from django.http import JsonResponse, HttpResponseForbidden
from django.http import JsonResponse, HttpResponseForbidden, HttpRequest, HttpResponse
from django.core.exceptions import PermissionDenied
from django.views.generic import DetailView
from django.views.generic import DetailView, View
from django.utils import timezone
from django.db import models
from django.contrib.auth import get_user_model
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import AnonymousUser
import json
from .models import EditSubmission, PhotoSubmission
from .models import EditSubmission, PhotoSubmission, UserType
class EditSubmissionMixin:
User = get_user_model()
class EditSubmissionMixin(DetailView):
"""
Mixin for handling edit submissions with proper moderation.
"""
def handle_edit_submission(self, request, changes, reason='', source='', submission_type='EDIT'):
model: Optional[Type[models.Model]] = None
def handle_edit_submission(self, request: HttpRequest, changes: Dict[str, Any], reason: str = '',
source: str = '', submission_type: str = 'EDIT') -> JsonResponse:
"""
Handle an edit submission based on user's role.
@@ -31,6 +41,9 @@ class EditSubmissionMixin:
'message': 'You must be logged in to make edits.'
}, status=403)
if not self.model:
raise ValueError("model attribute must be set")
content_type = ContentType.objects.get_for_model(self.model)
# Create the submission
@@ -46,16 +59,17 @@ class EditSubmissionMixin:
# For edits, set the object_id
if submission_type == 'EDIT':
obj = self.get_object()
submission.object_id = obj.id
submission.object_id = getattr(obj, 'id', None)
# Auto-approve for moderators and above
if request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
obj = submission.approve(request.user)
user_role = getattr(request.user, 'role', None)
if user_role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
obj = submission.approve(cast(UserType, request.user))
return JsonResponse({
'status': 'success',
'message': 'Changes saved successfully.',
'auto_approved': True,
'redirect_url': obj.get_absolute_url() if hasattr(obj, 'get_absolute_url') else None
'redirect_url': getattr(obj, 'get_absolute_url', lambda: None)()
})
# Submit for approval for regular users
@@ -66,7 +80,7 @@ class EditSubmissionMixin:
'auto_approved': False
})
def post(self, request, *args, **kwargs):
def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> JsonResponse:
"""Handle POST requests for editing"""
if not request.user.is_authenticated:
return JsonResponse({
@@ -87,7 +101,8 @@ class EditSubmissionMixin:
'message': 'No changes provided.'
}, status=400)
if not reason and request.user.role == 'USER':
user_role = getattr(request.user, 'role', None)
if not reason and user_role == 'USER':
return JsonResponse({
'status': 'error',
'message': 'Please provide a reason for your changes.'
@@ -108,11 +123,13 @@ class EditSubmissionMixin:
'message': str(e)
}, status=500)
class PhotoSubmissionMixin:
class PhotoSubmissionMixin(DetailView):
"""
Mixin for handling photo submissions with proper moderation.
"""
def handle_photo_submission(self, request):
model: Optional[Type[models.Model]] = None
def handle_photo_submission(self, request: HttpRequest) -> JsonResponse:
"""Handle a photo submission based on user's role"""
if not request.user.is_authenticated:
return JsonResponse({
@@ -120,6 +137,9 @@ class PhotoSubmissionMixin:
'message': 'You must be logged in to upload photos.'
}, status=403)
if not self.model:
raise ValueError("model attribute must be set")
try:
obj = self.get_object()
except (AttributeError, self.model.DoesNotExist):
@@ -139,14 +159,15 @@ class PhotoSubmissionMixin:
submission = PhotoSubmission(
user=request.user,
content_type=content_type,
object_id=obj.id,
object_id=getattr(obj, 'id', None),
photo=request.FILES['photo'],
caption=request.POST.get('caption', ''),
date_taken=request.POST.get('date_taken')
)
# Auto-approve for moderators and above
if request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
user_role = getattr(request.user, 'role', None)
if user_role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
submission.auto_approve()
return JsonResponse({
'status': 'success',
@@ -164,63 +185,81 @@ class PhotoSubmissionMixin:
class ModeratorRequiredMixin(UserPassesTestMixin):
"""Require moderator or higher role for access"""
def test_func(self):
request: Optional[HttpRequest] = None
def test_func(self) -> bool:
if not self.request:
return False
user_role = getattr(self.request.user, 'role', None)
return (
self.request.user.is_authenticated and
self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']
user_role in ['MODERATOR', 'ADMIN', 'SUPERUSER']
)
def handle_no_permission(self):
if not self.request.user.is_authenticated:
def handle_no_permission(self) -> HttpResponse:
if not self.request or not self.request.user.is_authenticated:
return super().handle_no_permission()
return HttpResponseForbidden("You must be a moderator to access this page.")
class AdminRequiredMixin(UserPassesTestMixin):
"""Require admin or superuser role for access"""
def test_func(self):
request: Optional[HttpRequest] = None
def test_func(self) -> bool:
if not self.request:
return False
user_role = getattr(self.request.user, 'role', None)
return (
self.request.user.is_authenticated and
self.request.user.role in ['ADMIN', 'SUPERUSER']
user_role in ['ADMIN', 'SUPERUSER']
)
def handle_no_permission(self):
if not self.request.user.is_authenticated:
def handle_no_permission(self) -> HttpResponse:
if not self.request or not self.request.user.is_authenticated:
return super().handle_no_permission()
return HttpResponseForbidden("You must be an admin to access this page.")
class InlineEditMixin:
"""Add inline editing context to views"""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if hasattr(self, 'request') and self.request.user.is_authenticated:
request: Optional[HttpRequest] = None
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs) # type: ignore
if self.request and self.request.user.is_authenticated:
context['can_edit'] = True
context['can_auto_approve'] = self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']
user_role = getattr(self.request.user, 'role', None)
context['can_auto_approve'] = user_role in ['MODERATOR', 'ADMIN', 'SUPERUSER']
if isinstance(self, DetailView):
obj = self.get_object()
obj = self.get_object() # type: ignore
context['pending_edits'] = EditSubmission.objects.filter(
content_type=ContentType.objects.get_for_model(obj),
object_id=obj.id,
content_type=ContentType.objects.get_for_model(obj.__class__),
object_id=getattr(obj, 'id', None),
status='NEW'
).select_related('user').order_by('-created_at')
return context
class HistoryMixin:
"""Add edit history context to views"""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs) # type: ignore
# Only add history context for DetailViews
if isinstance(self, DetailView):
obj = self.get_object()
obj = self.get_object() # type: ignore
# Get historical records ordered by date
context['history'] = obj.history.all().select_related('history_user').order_by('-history_date')
# Get historical records ordered by date if available
history = getattr(obj, 'history', None)
if history is not None:
context['history'] = history.all().select_related('history_user').order_by('-history_date')
else:
context['history'] = []
# Get related edit submissions
content_type = ContentType.objects.get_for_model(obj)
content_type = ContentType.objects.get_for_model(obj.__class__)
context['edit_submissions'] = EditSubmission.objects.filter(
content_type=content_type,
object_id=obj.id
object_id=getattr(obj, 'id', None)
).exclude(
status='NEW'
).select_related('user', 'handled_by').order_by('-created_at')