Merge branch 'dev' into feature-ai

This commit is contained in:
shamoon
2025-10-15 16:32:55 -07:00
40 changed files with 3415 additions and 615 deletions

View File

@@ -61,6 +61,7 @@ from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from drf_spectacular.utils import extend_schema
from drf_spectacular.utils import extend_schema_serializer
from drf_spectacular.utils import extend_schema_view
from drf_spectacular.utils import inline_serializer
from guardian.utils import get_group_obj_perms_model
@@ -159,6 +160,7 @@ from documents.serialisers import CustomFieldSerializer
from documents.serialisers import DocumentListSerializer
from documents.serialisers import DocumentSerializer
from documents.serialisers import DocumentTypeSerializer
from documents.serialisers import EmailSerializer
from documents.serialisers import NotesSerializer
from documents.serialisers import PostDocumentSerializer
from documents.serialisers import RunTaskViewSerializer
@@ -486,6 +488,14 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
ordering_fields = ("name", "matching_algorithm", "match", "document_count")
@extend_schema_serializer(
component_name="EmailDocumentRequest",
exclude_fields=("documents",),
)
class EmailDocumentDetailSchema(EmailSerializer):
pass
@extend_schema_view(
retrieve=extend_schema(
description="Retrieve a single document",
@@ -653,20 +663,28 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
404: None,
},
),
email=extend_schema(
email_document=extend_schema(
description="Email the document to one or more recipients as an attachment.",
request=inline_serializer(
name="EmailRequest",
fields={
"addresses": serializers.CharField(),
"subject": serializers.CharField(),
"message": serializers.CharField(),
"use_archive_version": serializers.BooleanField(default=True),
},
),
request=EmailDocumentDetailSchema,
responses={
200: inline_serializer(
name="EmailResponse",
name="EmailDocumentResponse",
fields={"message": serializers.CharField()},
),
400: None,
403: None,
404: None,
500: None,
},
deprecated=True,
),
email_documents=extend_schema(
operation_id="email_documents",
description="Email one or more documents as attachments to one or more recipients.",
request=EmailSerializer,
responses={
200: inline_serializer(
name="EmailDocumentsResponse",
fields={"message": serializers.CharField()},
),
400: None,
@@ -1236,55 +1254,57 @@ class DocumentViewSet(
return Response(sorted(entries, key=lambda x: x["timestamp"], reverse=True))
@action(methods=["post"], detail=True)
def email(self, request, pk=None):
try:
doc = Document.objects.select_related("owner").get(pk=pk)
@action(methods=["post"], detail=True, url_path="email")
# TODO: deprecated as of 2.19, remove in future release
def email_document(self, request, pk=None):
request_data = request.data.copy()
request_data.setlist("documents", [pk])
return self.email_documents(request, data=request_data)
@action(
methods=["post"],
detail=False,
url_path="email",
serializer_class=EmailSerializer,
)
def email_documents(self, request, data=None):
serializer = EmailSerializer(data=data or request.data)
serializer.is_valid(raise_exception=True)
validated_data = serializer.validated_data
document_ids = validated_data.get("documents")
addresses = validated_data.get("addresses").split(",")
addresses = [addr.strip() for addr in addresses]
subject = validated_data.get("subject")
message = validated_data.get("message")
use_archive_version = validated_data.get("use_archive_version", True)
documents = Document.objects.select_related("owner").filter(pk__in=document_ids)
for document in documents:
if request.user is not None and not has_perms_owner_aware(
request.user,
"view_document",
doc,
document,
):
return HttpResponseForbidden("Insufficient permissions")
except Document.DoesNotExist:
raise Http404
try:
if (
"addresses" not in request.data
or "subject" not in request.data
or "message" not in request.data
):
return HttpResponseBadRequest("Missing required fields")
use_archive_version = request.data.get("use_archive_version", True)
addresses = request.data.get("addresses").split(",")
if not all(
re.match(r"[^@]+@[^@]+\.[^@]+", address.strip())
for address in addresses
):
return HttpResponseBadRequest("Invalid email address found")
send_email(
subject=request.data.get("subject"),
body=request.data.get("message"),
subject=subject,
body=message,
to=addresses,
attachment=(
doc.archive_path
if use_archive_version and doc.has_archive_version
else doc.source_path
),
attachment_mime_type=doc.mime_type,
attachments=documents,
use_archive=use_archive_version,
)
logger.debug(
f"Sent document {doc.id} via email to {addresses}",
f"Sent documents {[doc.id for doc in documents]} via email to {addresses}",
)
return Response({"message": "Email sent"})
except Exception as e:
logger.warning(f"An error occurred emailing document: {e!s}")
logger.warning(f"An error occurred emailing documents: {e!s}")
return HttpResponseServerError(
"Error emailing document, check logs for more detail.",
"Error emailing documents, check logs for more detail.",
)