(backend) allow masking documents from the list view

Once users have visited a document to which they have access,
they can't remove it from their list view anymore. Several
users reported that this is annoying because a document that
gets a lot of updates keeps popping up at the top of their list
view.

They want to be able to mask the document in a click. We propose
to add a "masked documents" section in the left side bar where the
masked documents can still be found.
This commit is contained in:
Samuel Paccoud - DINUM
2025-07-13 19:56:07 +02:00
parent 228bdf733e
commit 0b301b95c8
12 changed files with 560 additions and 12 deletions

View File

@@ -60,6 +60,9 @@ class ListDocumentFilter(DocumentFilter):
is_creator_me = django_filters.BooleanFilter(
method="filter_is_creator_me", label=_("Creator is me")
)
is_masked = django_filters.BooleanFilter(
method="filter_is_masked", label=_("Masked")
)
is_favorite = django_filters.BooleanFilter(
method="filter_is_favorite", label=_("Favorite")
)
@@ -106,3 +109,22 @@ class ListDocumentFilter(DocumentFilter):
return queryset
return queryset.filter(is_favorite=bool(value))
# pylint: disable=unused-argument
def filter_is_masked(self, queryset, name, value):
"""
Filter documents based on whether they are masked by the current user.
Example:
- /api/v1.0/documents/?is_masked=true
→ Filters documents marked as masked by the logged-in user
- /api/v1.0/documents/?is_masked=false
→ Filters documents not marked as masked by the logged-in user
"""
user = self.request.user
if not user.is_authenticated:
return queryset
queryset_method = queryset.filter if bool(value) else queryset.exclude
return queryset_method(link_traces__user=user, link_traces__is_masked=True)

View File

@@ -455,9 +455,8 @@ class DocumentViewSet(
# Annotate favorite status and filter if applicable as late as possible
queryset = queryset.annotate_is_favorite(user)
queryset = filterset.filters["is_favorite"].filter(
queryset, filter_data["is_favorite"]
)
for field in ["is_favorite", "is_masked"]:
queryset = filterset.filters[field].filter(queryset, filter_data[field])
# Apply ordering only now that everything is filtered and annotated
queryset = filters.OrderingFilter().filter_queryset(
@@ -1109,15 +1108,50 @@ class DocumentViewSet(
document=document, user=user
).delete()
if deleted:
return drf.response.Response(
{"detail": "Document unmarked as favorite"},
status=drf.status.HTTP_204_NO_CONTENT,
)
return drf.response.Response(status=drf.status.HTTP_204_NO_CONTENT)
return drf.response.Response(
{"detail": "Document was already not marked as favorite"},
status=drf.status.HTTP_200_OK,
)
@drf.decorators.action(detail=True, methods=["post", "delete"], url_path="mask")
def mask(self, request, *args, **kwargs):
"""Mask or unmask the document for the logged-in user based on the HTTP method."""
# Check permissions first
document = self.get_object()
user = request.user
try:
link_trace = models.LinkTrace.objects.get(document=document, user=user)
except models.LinkTrace.DoesNotExist:
return drf.response.Response(
{"detail": "User never accessed this document before."},
status=status.HTTP_400_BAD_REQUEST,
)
if request.method == "POST":
if link_trace.is_masked:
return drf.response.Response(
{"detail": "Document was already masked"},
status=drf.status.HTTP_200_OK,
)
link_trace.is_masked = True
link_trace.save(update_fields=["is_masked"])
return drf.response.Response(
{"detail": "Document was masked"},
status=drf.status.HTTP_201_CREATED,
)
# Handle DELETE method to unmask document
if not link_trace.is_masked:
return drf.response.Response(
{"detail": "Document was already not masked"},
status=drf.status.HTTP_200_OK,
)
link_trace.is_masked = False
link_trace.save(update_fields=["is_masked"])
return drf.response.Response(status=drf.status.HTTP_204_NO_CONTENT)
@drf.decorators.action(detail=True, methods=["post"], url_path="attachment-upload")
def attachment_upload(self, request, *args, **kwargs):
"""Upload a file related to a given document"""