Files
customerTicket/apps/tickets/staff_conversation.py
T
2026-10-04 16:42:19 +03:30

84 lines
4.2 KiB
Python

"""Staff conversation access; message grants never become ticket grants."""
import hashlib
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import JsonResponse
from django.template.loader import render_to_string
from .models import MessageShare, TicketAccessGrant
from .policies import TicketPolicy
from .sharing import staff_message_context
def received_messages(user, ticket=None):
grants = MessageShare.objects.filter(user=user, revoked_at__isnull=True, message__deleted_at__isnull=True)
return grants.filter(message__ticket=ticket) if ticket is not None else grants
def unread_shares(user):
profile = getattr(user, "staff_profile", None)
if not user.is_authenticated or not user.is_active or user.account_type != "STAFF" or not profile or not profile.is_active:
return 0
return (received_messages(user).filter(first_viewed_at__isnull=True).count()
+ TicketAccessGrant.objects.filter(user=user, revoked_at__isnull=True, first_viewed_at__isnull=True).count())
def visible_messages(ticket, user):
profile = getattr(user, "staff_profile", None)
if not user.is_active or (profile is not None and not profile.is_active):
raise PermissionDenied
query = ticket.ticket_messages.all()
if TicketPolicy.can_view(user, ticket):
return query
grants = received_messages(user, ticket)
if not grants.exists():
raise PermissionDenied
return query.filter(pk__in=grants.values("message_id"))
def message_context(ticket, user, conversation):
context = staff_message_context(ticket, user, conversation)
grants = {g.message_id: g for g in received_messages(user, ticket).select_related("granted_by__staff_profile")}
for item in conversation:
item.received_share = grants.get(item.pk)
context["limited_share"] = not TicketPolicy.can_view(user, ticket)
return context
def shared_ticket_rows(user, params):
"""All received shares use ordinary ticket metadata and filters."""
from .models import Ticket
from .selectors import filter_staff_tickets, staff_ticket_rows
full = staff_ticket_rows(user)
query = Ticket.objects.select_related("customer__customer_profile", "category", "owner__staff_profile", "selected_expert__staff_profile", "owning_unit", "assigned_unit", "group_dispatch").filter(
Q(pk__in=full.values("pk")) | Q(pk__in=received_messages(user).values("message__ticket_id")))
rows = list(filter_staff_tickets(query, user, params)[:100])
full_ids = set(full.filter(pk__in=[ticket.pk for ticket in rows]).values_list("pk", flat=True))
for ticket in rows:
ticket.limited_share = ticket.pk not in full_ids
return rows
def limited_poll(request, ticket):
"""Version depends exclusively on authorized messages and their active grants."""
from .views import conversation_page
query = visible_messages(ticket, request.user)
grants = list(received_messages(request.user, ticket).values_list("pk", "note"))
source = repr((list(query.order_by("pk").values_list("pk", "updated_at")), grants,
ticket.status, ticket.priority, ticket.subject, ticket.province, ticket.city))
version = hashlib.sha256(source.encode()).hexdigest()[:20]
latest = query.order_by("-created_at", "-pk").first()
cursor = str(latest.public_id) if latest else ""
data = {"changed": False, "version": version, "cursor": cursor}
if request.GET.get("version") != version or request.GET.get("cursor", "") != cursor:
conversation, has_older, older_cursor = conversation_page(query.select_related("author").prefetch_related("attachments"))
context = {"ticket": ticket, "viewer_user": request.user, "conversation": conversation,
"has_older": has_older, "older_cursor": older_cursor,
**message_context(ticket, request.user, conversation)}
data.update(changed=True, replace=True, staff_limited=True, count=query.count(), status=ticket.get_status_display(),
html=render_to_string("tickets/_staff_older_messages.html", context, request=request))
response = JsonResponse(data)
response["Cache-Control"] = "private, no-store"
return response