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 }} + +