diff --git a/loko/documents/forms.py b/loko/documents/forms.py
index 0c1a5db..d2bd38d 100644
--- a/loko/documents/forms.py
+++ b/loko/documents/forms.py
@@ -16,8 +16,14 @@ from .models import (
DocumentValidation,
DocumentVersion,
ManagedDocument,
+ VisibilityScope,
+)
+from .permissions import (
+ filter_folders_for_user,
+ user_can_change_visibility,
)
from common.models import Thematic
+from common.utils import get_short_name
User = get_user_model()
@@ -34,23 +40,42 @@ class TagField(forms.CharField):
class DocumentUpdateForm(forms.ModelForm):
class Meta:
model = ManagedDocument
- fields = ["title", "description", "visibility", "thematics"]
+ fields = ["title", "description", "visibility", "thematics", "folders"]
widgets = {
"title": forms.TextInput(attrs={"class": "form-control"}),
"description": forms.Textarea(attrs={"class": "form-control", "rows": 4}),
"visibility": forms.Select(attrs={"class": "form-select"}),
"thematics": forms.SelectMultiple(attrs={"class": "form-select"}),
+ "folders": forms.SelectMultiple(attrs={"class": "form-select"}),
}
def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
self.fields["title"].widget.attrs.setdefault("class", "form-control")
self.fields["description"].widget.attrs.setdefault("class", "form-control")
- self.fields["visibility"].widget.attrs.setdefault("class", "form-select")
- self.fields["visibility"].required = False
+ if "visibility" in self.fields:
+ self.fields["visibility"].widget.attrs.setdefault("class", "form-select")
+ self.fields["visibility"].required = False
+ if self.user and not user_can_change_visibility(self.user):
+ self.fields.pop("visibility", None)
if "thematics" in self.fields:
self.fields["thematics"].queryset = Thematic.objects.order_by("name_fr")
self.fields["thematics"].required = False
+ if "folders" in self.fields:
+ self.fields["folders"].widget.attrs.setdefault("class", "form-select")
+ self.fields["folders"].required = False
+ if self.user:
+ self.fields["folders"].queryset = filter_folders_for_user(
+ DocumentFolder.objects.all(),
+ self.user,
+ required_permission=ManagedDocument.PERMISSION_VIEW,
+ ).order_by("name")
+
+ def clean_visibility(self):
+ if self.user and not user_can_change_visibility(self.user):
+ return self.instance.visibility
+ return self.cleaned_data.get("visibility") or self.instance.visibility
class ManagedDocumentForm(forms.ModelForm):
@@ -72,13 +97,16 @@ class ManagedDocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop("user", None)
+ self.user = user
initial_folder = kwargs.pop("initial_folder", None)
super().__init__(*args, **kwargs)
- self.fields["visibility"].required = False
+ if "visibility" in self.fields:
+ self.fields["visibility"].required = False
+ if user and not user_can_change_visibility(user):
+ self.fields.pop("visibility", None)
# Limiter les folders accessibles à l'utilisateur
if user:
- from .permissions import filter_folders_for_user
accessible_folders = filter_folders_for_user(
DocumentFolder.objects.all(),
user,
@@ -103,16 +131,11 @@ class ManagedDocumentForm(forms.ModelForm):
tags_input = self.cleaned_data.pop("tags_input", [])
document = super().save(commit=False)
document.created_by = user
+ if user and not user_can_change_visibility(user):
+ document.visibility = VisibilityScope.INTERNAL
if commit:
document.save()
self.save_m2m()
- # if tags_input:
- # for tag_name in tags_input:
- # tag, _ = DocumentTag.objects.get_or_create(
- # slug=slugify(tag_name),
- # defaults={"name_fr": tag_name, "name_nl": tag_name},
- # )
- # document.tags.add(tag)
file = self.cleaned_data["file"]
comment = self.cleaned_data.get("comment", "")
version = DocumentVersion(
@@ -165,6 +188,7 @@ class DocumentShareForm(forms.ModelForm):
if document:
self.fields["user"].queryset = User.objects.exclude(document_shares__document=document)
self.fields["user"].widget.attrs.setdefault("class", "form-select")
+ self.fields["user"].label_from_instance = lambda obj: get_short_name(obj) or obj.username
def save(self, commit=True, creator=None, document=None):
share = super().save(commit=False)
@@ -193,6 +217,7 @@ class DocumentFolderShareForm(forms.ModelForm):
document_folder_shares__folder=folder
)
self.fields["user"].widget.attrs.setdefault("class", "form-select")
+ self.fields["user"].label_from_instance = lambda obj: get_short_name(obj) or obj.username
def save(self, commit=True, creator=None, folder=None):
share = super().save(commit=False)
@@ -366,6 +391,8 @@ class DocumentFolderForm(forms.ModelForm):
super().__init__(*args, **kwargs)
if "visibility" in self.fields:
self.fields["visibility"].required = False
+ if self.request_user and not user_can_change_visibility(self.request_user):
+ self.fields.pop("visibility", None)
instance = getattr(self, "instance", None)
if instance and instance.pk:
@@ -385,15 +412,20 @@ class DocumentFolderForm(forms.ModelForm):
if not instance or not getattr(instance, "is_predefined", False):
self.fields.pop("thematics", None)
- self.fields["created_by"].label = _("Owner")
- self.fields["created_by"].required = False
-
if "created_by" in self.fields:
+ self.fields["created_by"].label = _("Owner")
+ self.fields["created_by"].required = False
self.fields["created_by"].queryset = User.objects.order_by("username")
+ self.fields["created_by"].label_from_instance = lambda obj: get_short_name(obj) or obj.username
if not self._can_edit_owner():
self.fields.pop("created_by", None)
+ def clean_visibility(self):
+ if self.request_user and not user_can_change_visibility(self.request_user):
+ return self.instance.visibility if self.instance.pk else VisibilityScope.INTERNAL
+ return self.cleaned_data.get("visibility") or (self.instance.visibility if self.instance.pk else VisibilityScope.INTERNAL)
+
def _can_edit_owner(self):
user = self.request_user
if user is None or not getattr(user, "is_authenticated", False):
diff --git a/loko/documents/models.py b/loko/documents/models.py
index 8b5cc23..095f886 100644
--- a/loko/documents/models.py
+++ b/loko/documents/models.py
@@ -112,10 +112,10 @@ class DocumentTag(models.Model):
class VisibilityScope(models.TextChoices):
- PRIVATE = "private", _("Private (Creator only)")
- RESTRICTED = "restricted", _("Shared with specific users")
- INTERNAL = "internal", _("Internal (Thematics & Contracts)")
- SCOPED = "scoped", _("All qualified users (Internal & External)")
+ PRIVATE = "private", _("Privé (Créateur uniquement)")
+ RESTRICTED = "restricted", _("Partagé avec certains utilisateurs")
+ INTERNAL = "internal", _("Interne (sur base des thématiques et contrats)")
+ SCOPED = "scoped", _("Interne et externe (sur base des thématiques et contrats)")
class DocumentFolder(models.Model):
diff --git a/loko/documents/permissions.py b/loko/documents/permissions.py
index 91b5fd2..8165462 100644
--- a/loko/documents/permissions.py
+++ b/loko/documents/permissions.py
@@ -247,6 +247,25 @@ def user_is_documents_admin(user) -> bool:
return False
+def user_is_internal(user) -> bool:
+ if user is None or not getattr(user, "is_authenticated", False):
+ return False
+ if getattr(user, "is_superuser", False) or user_is_documents_admin(user):
+ return True
+ config = getattr(user, "config", None)
+ return bool(config and getattr(config, "is_intern", False))
+
+
+def user_can_share(user) -> bool:
+ """External users (even authors) cannot share documents or folders."""
+ return user_is_internal(user)
+
+
+def user_can_change_visibility(user) -> bool:
+ """External users (even authors) cannot change visibility."""
+ return user_is_internal(user)
+
+
def user_is_internal_manager(user) -> bool:
if user is None or not getattr(user, "is_authenticated", False):
return False
diff --git a/loko/documents/templates/documents/document_detail.html b/loko/documents/templates/documents/document_detail.html
index db3239f..7015bd2 100644
--- a/loko/documents/templates/documents/document_detail.html
+++ b/loko/documents/templates/documents/document_detail.html
@@ -5,6 +5,8 @@
{% block head %}
{{ block.super }}
+
+