84 lines
4.2 KiB
Python
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
|