Files
customerTicket/apps/tickets/sharing_views.py
T

114 lines
4.8 KiB
Python

"""Routes available only to staff, including isolated message attachment delivery."""
import mimetypes
from django.conf import settings
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 FileResponse, 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 signed_media_redirect
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):
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 (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):
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 TicketPolicy.can_view(request.user, ticket):
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)
if settings.USE_NGINX_MEDIA:
return signed_media_redirect(attachment.relative_path, download=download)
content_type = attachment.mime_type or mimetypes.guess_type(attachment.original_name)[0] or "application/octet-stream"
response = FileResponse(attachment.file.open("rb"), content_type=content_type, as_attachment=download, filename=attachment.original_name)
response["X-Content-Type-Options"] = "nosniff"
return response