Files

111 lines
4.5 KiB
Python

"""Routes available only to staff, including isolated message attachment delivery."""
from django.contrib import messages
from django.db import transaction
from django.utils import timezone
from django.core.exceptions import PermissionDenied, ValidationError
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.cache import never_cache
from django.views.decorators.http import require_GET, require_POST
from apps.attachments.delivery import attachment_response
from apps.attachments.models import Attachment
from .models import MessageShare, Ticket, TicketReadState
from .policies import TicketPolicy
from apps.messaging.read_receipts import message_read_ids
from .staff_conversation import received_messages, unread_shares, visible_messages
from .sharing import mark_share_opened, revoke_staff_share
from .views import staff_required, mark_messages_read
def received_message_share(request, share_id):
from .dispatch_responses import can_read_response
grant = get_object_or_404(MessageShare.objects.select_related("message__author__staff_profile", "granted_by__staff_profile"), public_id=share_id)
profile = getattr(request.user, "staff_profile", None)
if not can_read_response(request.user, grant.message):
raise PermissionDenied
if (grant.user_id != request.user.pk or grant.revoked_at or grant.message.deleted_at
or not request.user.is_active or not profile or not profile.is_active):
raise PermissionDenied
return grant
def submit_share_form(request, form, save, success_message):
if not form.is_valid():
messages.error(request, "گیرنده، سطح دسترسی یا توضیح اشتراک معتبر نیست.")
return
try:
save()
except ValidationError as error:
messages.error(request, " ".join(error.messages))
else:
messages.success(request, success_message)
@staff_required
@require_GET
@never_cache
def inbox(request):
from django.urls import reverse
return redirect(reverse("staff_tickets:list") + "?tab=shared")
@staff_required
@require_POST
def revoke_message(request, public_id, share_id):
grant = get_object_or_404(MessageShare.objects.select_related("message__ticket"), public_id=share_id, message__ticket__public_id=public_id)
revoke_staff_share(grant, request.user)
messages.success(request, "دسترسی به پیام اشتراکی لغو شد؛ سابقه آن حفظ شده است.")
return redirect("staff_tickets:detail", public_id=public_id)
@staff_required
@require_GET
@never_cache
def shared_message(request, share_id):
grant = received_message_share(request, share_id)
return redirect("staff_tickets:detail", public_id=grant.message.ticket.public_id)
@staff_required
@require_POST
@never_cache
@transaction.atomic
def read_shared_messages(request, public_id):
from .dispatch_responses import can_record_ticket_read
ticket = get_object_or_404(Ticket.objects.select_for_update(), public_id=public_id)
allowed = visible_messages(ticket, request.user)
try:
ids = message_read_ids(request)
except ValidationError:
return JsonResponse({"ok": False, "error": "invalid_message_ids"}, status=400)
acknowledged = list(allowed.filter(public_id__in=ids, deleted_at__isnull=True))
mark_messages_read(acknowledged, request.user)
accepted = [item.public_id for item in acknowledged]
grants = received_messages(request.user, ticket).filter(message__public_id__in=accepted)
for grant in grants:
if grant.first_viewed_at is None:
mark_share_opened(grant, request.user)
latest = allowed.filter(deleted_at__isnull=True).order_by('-created_at', '-pk').first()
if latest and latest.public_id in accepted and can_record_ticket_read(ticket, request.user):
TicketReadState.objects.update_or_create(ticket=ticket, user=request.user,
defaults={'last_read_at': timezone.now(), 'last_read_message': latest})
return JsonResponse({"unread_shares": unread_shares(request.user), "read_ids": [str(value) for value in accepted]})
@staff_required
@require_GET
@never_cache
def unread_count(request):
return JsonResponse({"unread_shares": unread_shares(request.user)})
@staff_required
@require_GET
@never_cache
def shared_attachment(request, share_id, attachment_id, download=False):
grant = received_message_share(request, share_id)
attachment = get_object_or_404(Attachment, public_id=attachment_id, message_id=grant.message_id)
return attachment_response(attachment, download=download)