feat: add document management views, URLs, and permission unit tests

This commit is contained in:
Karl Determe 2026-08-18 14:29:00 +02:00
parent 80f2956872
commit 061fb6dc62
3 changed files with 820 additions and 777 deletions

View file

@ -8,7 +8,17 @@ from common.models import Role, UserConfig
from interventions.models import Intervention from interventions.models import Intervention
from projects.models import Project from projects.models import Project
from .models import DocumentAttachment, DocumentFolder, DocumentFolderShare, DocumentShare, ManagedDocument from .models import (
DocumentAccessLink,
DocumentAttachment,
DocumentFolder,
DocumentFolderAccessLink,
DocumentFolderAttachment,
DocumentFolderShare,
DocumentShare,
DocumentVersion,
ManagedDocument,
)
from .views import ensure_document_deletable from .views import ensure_document_deletable
from .permissions import ( from .permissions import (
filter_documents_for_user, filter_documents_for_user,
@ -772,3 +782,30 @@ class DocumentGeoreferenceAutoZoomTests(TestCase):
from .views import _get_associated_asset_coords from .views import _get_associated_asset_coords
coords = _get_associated_asset_coords(self.document) coords = _get_associated_asset_coords(self.document)
self.assertEqual(coords, [4.3517, 50.8503]) self.assertEqual(coords, [4.3517, 50.8503])
class PublicAccessLinkTests(TestCase):
def setUp(self):
self.owner = User.objects.create_user(username="link_owner", password="pwd")
self.document = ManagedDocument.objects.create(title="Public Test Doc", created_by=self.owner)
self.doc_link = DocumentAccessLink.objects.create(
document=self.document,
permission=ManagedDocument.PERMISSION_VIEW,
)
self.folder = DocumentFolder.objects.create(name="Public Folder", slug="public-folder", created_by=self.owner)
self.folder_link = DocumentFolderAccessLink.objects.create(
folder=self.folder,
permission=ManagedDocument.PERMISSION_VIEW,
)
def test_anonymous_user_can_access_public_document_view(self):
url = reverse("documents:public_view", args=[self.doc_link.token])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Public Test Doc")
def test_anonymous_user_can_access_public_folder_view(self):
url = reverse("documents:folder_public", args=[self.folder_link.token])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Public Folder")

View file

@ -1,3 +1,4 @@
from django.contrib.auth.decorators import login_not_required
from django.urls import path from django.urls import path
from . import views from . import views
@ -60,11 +61,11 @@ urlpatterns = [
path("<int:pk>/remove-folder/<int:folder_id>/", views.RemoveFolderFromDocumentView.as_view(), name="remove_folder"), path("<int:pk>/remove-folder/<int:folder_id>/", views.RemoveFolderFromDocumentView.as_view(), name="remove_folder"),
path("<int:pk>/add-tag/", views.AddTagToDocumentView.as_view(), name="add_tag"), path("<int:pk>/add-tag/", views.AddTagToDocumentView.as_view(), name="add_tag"),
path("<int:pk>/remove-tag/<int:tag_id>/", views.RemoveTagFromDocumentView.as_view(), name="remove_tag"), path("<int:pk>/remove-tag/<int:tag_id>/", views.RemoveTagFromDocumentView.as_view(), name="remove_tag"),
path("public/<str:token>/", views.PublicDocumentView.as_view(), name="public_view"), path("public/<str:token>/", login_not_required(views.PublicDocumentView.as_view()), name="public_view"),
path("public/<str:token>/download/", views.PublicDocumentDownloadView.as_view(), name="public_download"), path("public/<str:token>/download/", login_not_required(views.PublicDocumentDownloadView.as_view()), name="public_download"),
path( path(
"folders/public/<str:token>/", "folders/public/<str:token>/",
views.PublicFolderView.as_view(), login_not_required(views.PublicFolderView.as_view()),
name="folder_public", name="folder_public",
), ),
path("<int:pk>/georeference/", views.DocumentGeoreferenceView.as_view(), name="georeference"), path("<int:pk>/georeference/", views.DocumentGeoreferenceView.as_view(), name="georeference"),

View file

@ -3,6 +3,7 @@ import mimetypes
import os import os
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_not_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import transaction from django.db import transaction
@ -11,6 +12,7 @@ from django.db.models.functions import Lower
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.http import url_has_allowed_host_and_scheme from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -1352,6 +1354,7 @@ class RemoveTagFromDocumentView(LoginRequiredMixin, View):
return redirect(document.get_absolute_url()) return redirect(document.get_absolute_url())
@method_decorator(login_not_required, name="dispatch")
class PublicDocumentView(View): class PublicDocumentView(View):
template_name = "documents/document_public.html" template_name = "documents/document_public.html"
@ -1376,6 +1379,7 @@ class PublicDocumentView(View):
) )
@method_decorator(login_not_required, name="dispatch")
class PublicDocumentDownloadView(View): class PublicDocumentDownloadView(View):
def get(self, request, token): def get(self, request, token):
link = get_object_or_404( link = get_object_or_404(
@ -1409,6 +1413,7 @@ class PublicDocumentDownloadView(View):
return response return response
@method_decorator(login_not_required, name="dispatch")
class PublicFolderView(View): class PublicFolderView(View):
template_name = "documents/documentfolder_public.html" template_name = "documents/documentfolder_public.html"