Espace documentaire — détails techniques
Description et fonctionnalités de ce module : Espace documentaire.
Architecture des connecteurs NextCloud / lool et de la chaîne d'édition en ligne :
NextCloud & édition en ligne.
Couverture de tests
10 scénario(s) e2e couvrent ce module — dossier apps/open-ent-e2e/src/modules/12_espace_documentaire.
10 test(s) e2e dans 10 scénario(s) — voir le détail
Espace documentaire 01_acces_au_module.spec.ts
- accès au module
Espace documentaire 02_navigation_et_chargement_des_documents.spec.ts
- navigation et chargement des documents
Espace documentaire 03_creation_dossier.spec.ts
- création d'un dossier personnel
Espace documentaire 04_import_document.spec.ts
- import d'un document
Espace documentaire 05_quota_diagnostic.spec.ts
- affichage et diagnostic du quota utilisateur
Espace documentaire 06_documents_synchronises_acces.spec.ts
- accès aux documents synchronisés (connecteur NextCloud) et dépliage par défaut
Espace documentaire 07_partage_nominatif.spec.ts
- partager un document synchronisé avec un autre utilisateur de l'ENT
Espace documentaire 08_edition_en_ligne.spec.ts
- ouverture d'un document bureautique en édition en ligne (OnlyOffice / Collabora)
Espace documentaire 09_blocage_extensions_dangereuses.spec.ts
- import natif d'un fichier .exe refusé (extension bloquée par défaut)
Espace documentaire 10_edition_document_natif_lool.spec.ts
- connecteur
lool: fournisseur WOPI joignable, discovery abouti, ouverture d'un document natif de l'ENT dans l'éditeur
➡️ Statut dans le dashboard E2E · Résultats détaillés
Tests API de non-régression (dépôt open-ent-mods, tests/extension-blocking.spec.ts, suite
distincte de celle ci-dessus) : blocage d'une extension interdite à l'upload natif et NextCloud,
valeurs par défaut/héritées de la config desktop NextCloud, isolation entre établissements,
création automatique du dossier synchronisé.
Publication d'un document sur le portail public
Parcours utilisateur : Publier un document sur le portail public. Consommation côté site WordPress : Portails publics — détails techniques. Pas encore couvert par les tests e2e.
| Route | Accès | Rôle |
|---|---|---|
PUT /workspace/document/:id/portal-publish | @SecuredAction("workspace.document.publish", RESOURCE) | Publie le document |
DELETE /workspace/document/:id/portal-publish | idem | Retire la publication |
GET /workspace/pub/document/:id | anonyme | Sert le fichier publié (getFile(request, null, true)) |
GET /workspace/pub/structure/:structureId/documents | anonyme | Liste les documents publiés des utilisateurs de l'établissement |
Le contrôle de droit n'est pas un droit de partage par document : WorkspaceResourcesProvider
route les méthodes portalPublish / portalUnpublish vers authorizeAdmlOfDocumentOwner, qui
autorise le SUPER_ADMIN, ou l'ADMIN_LOCAL dont le périmètre couvre la structure du
propriétaire du document (le document n'ayant pas de structureId, la structure est résolue en
Neo4j depuis owner). Côté client, le ng-if de la barre d'actions
(delegates/actions/portalPublish.ts, canPortalPublish()) n'est qu'un confort d'affichage.
Publier écrit dans la collection Mongo documents : $set { public: true, portalPublication: { publishedBy, publishedAt } } et $unset { protected } — dépublier retire les deux champs. Le
drapeau public est le même que celui de changeVisibility (visibilité historique du workspace) ;
portalPublication est ce qui distingue une publication de portail d'un document rendu public par
un autre chemin, et c'est sur son existence que filtre la liste par établissement.
listPortalPublications résout d'abord en Neo4j les utilisateurs de la structure
((s:Structure)<-[:DEPENDS]-(:ProfileGroup)<-[:IN]-(u:User)), puis interroge Mongo sur
owner ∈ ids ∧ public = true ∧ portalPublication existe, trié par portalPublication.publishedAt
décroissant, projeté sur name, owner, ownerName, metadata, portalPublication et complété d'une
url relative /workspace/pub/document/<id>.
L'URL renvoyée par portalPublish (et affichée dans la lightbox portalPublish.html) est un
chemin relatif : c'est l'appelant qui préfixe l'origine de l'ENT — ce que fait la page
WordPress via OPENENT_API_BASE.
Blocage des extensions de fichiers dangereuses
Réglage stocké dans la collection Mongo config du connecteur NextCloud (_id: "uniqueId" pour
le national, _id: <structureId> pour une surcharge d'établissement) — précédence de résolution :
établissement > national > valeur par défaut codée en dur. Même précédence pour le dossier
synchronisé (préfixe + UAI) et la bande passante.
| Point d'entrée | Fichier | Mécanisme |
|---|---|---|
| Upload NextCloud direct | connectors/nextcloud/backend/.../DefaultDocumentsService.java (checkExtensionAllowed, uploadStreamedFile) | Vérifie avant l'écriture WebDAV ; rejette extension.forbidden |
| Déplacement/copie workspace → NextCloud | idem, sendWorkspaceFileToNC | Même vérification, avant l'écriture |
| Upload natif (espace documentaire ENT) | libs/entcore/workspace/.../WorkspaceController.java (addDocument) | Vérifie après écriture disque (contrainte de l'API Storage) ; supprime le fichier via storage.removeFile si refusé |
| Résolution national/établissement | fr.openent.nextcloud.helper.DesktopConfigHelper | Fusion des documents Mongo national + local, avec indicateurs overrides par champ |
Le module workspace (bibliothèque entcore, pas de dépendance vers le connecteur nextcloud)
duplique volontairement la liste par défaut et la requête Mongo — pas de bibliothèque partagée
entre les deux artefacts Maven.
Écran d'administration : GET/PUT /nextcloud/desktop/config (national) et
GET/PUT /nextcloud/desktop/config/structure/:structureid (établissement, protégé par
appartenance — 403 si l'établissement demandé n'est pas parmi ceux de l'utilisateur, sauf
super-admin). GET /nextcloud/desktop/my-structures liste les établissements administrés par
l'utilisateur connecté (pour le sélecteur, affiché uniquement s'il en gère plusieurs).
Le dossier synchronisé par défaut (ENT_PARTAGE_UAI_<UAI>, ou ENT_PARTAGE_UAI_<NOM_MAJUSCULE>
si l'établissement n'a pas d'UAI) est créé côté serveur NextCloud via MKCOL WebDAV au premier
appel à la racine de l'espace synchronisé (DocumentsController#listFiles, path vide) —
best-effort, un statut 405 (dossier déjà existant) n'est pas traité comme une erreur.
Ce dossier est un vrai dossier NextCloud, distinct du mécanisme de partage nominatif
(shareWithUser, OCS POST /ocs/v2.php/apps/files_sharing/api/v1/shares) : un fichier partagé
reste physiquement chez son propriétaire et n'est monté qu'à la racine de l'espace du
destinataire (comportement natif NextCloud, table oc_share) — jamais à l'intérieur de son dossier
ENT_PARTAGE_UAI_X.
Vérifier l'existence du dossier en dev (Docker)
Environnement de dev local (dev/nextcloud/docker-compose.yml), conteneur nextcloud-nextcloud-1.
Aucun accès de ce type n'existe côté console d'admin NextCloud (l'admin applicatif ne voit jamais
les fichiers personnels des comptes) — seul un accès système au conteneur permet de vérifier.
# 1. Retrouver l'identifiant technique NextCloud (UUID) d'un utilisateur ENT
docker exec -u www-data nextcloud-nextcloud-1 php occ user:list
# 2. Lister la racine de son espace — doit contenir le dossier ENT_PARTAGE_UAI_<UAI>
docker exec -u www-data nextcloud-nextcloud-1 ls -la "/var/www/html/data/<UUID>/files/"
# 3. Lister le contenu du dossier synchronisé lui-même (vide si l'utilisateur n'y a rien déposé)
docker exec -u www-data nextcloud-nextcloud-1 ls -la "/var/www/html/data/<UUID>/files/ENT_PARTAGE_UAI_<UAI>/"
# 4. Vérifier un partage nominatif : le fichier partagé apparaît à la racine du destinataire
# (target-path), jamais dans son dossier ENT_PARTAGE_UAI_X — la source reste chez l'émetteur
docker exec -u www-data nextcloud-nextcloud-1 php occ share:list --owner "<UUID-émetteur>" --output json
Le dossier synchronisé n'est créé qu'à la première ouverture de la section "Documents
synchronisés" par l'utilisateur (path vide de listFiles) — un compte qui n'a jamais ouvert cette
section n'a pas encore ce dossier côté serveur, ce n'est pas une anomalie.
Alerte d'occupation : le seuil, et où il est résolu
L'alerte existait de longue date ; seul son réglage par établissement manquait.
-
Émission :
DefaultQuotaService#incrementStorageposeu.alertSizesur leUserBooket renvoie un drapeaunotifysur la seule transition — franchir le seuil notifie une fois, pas à chaque dépôt.notifySmallAmountOfFreeSpaceémet alors la notification timelineworkspace.storage(« Votre espace de stockage est bientôt plein »,view-src/notify/workspace/storage.html). La messagerie a la sienne, sur le même principe. -
Résolution du seuil : dans la requête d'incrément elle-même, à partir des structures de la personne :
OPTIONAL MATCH (:User {id: {userId}})-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s:Structure)
WITH u, oldAlert, COALESCE(MIN(s.storageAlertThreshold), {threshold}) as alertThresholdMIN()ignore lesnull: sans surcharge, on retombe sur le seuil de plate-forme (alertStorage, 80 %). Pour une personne rattachée à plusieurs établissements, le plus bas l'emporte — on prévient au plus tôt plutôt qu'au plus tard. -
Pourquoi pas un cache dénormalisé sur le
UserBook: il ne serait jamais recalculé pour les comptes créés après coup — la maladie du compteurnbUsersdes groupes. Et un saut de graphe sur un chemin qui fait déj à une analyse antivirus est du bruit. -
API :
GET|PUT|DELETE /workspace/quota/alert-threshold/:structureId(QuotaController), enAUTHENTICATEDavec contrôle manuel super-admin / ADML de la structure — même parti pris que le contrôleur des horaires, pour éviter d'ajouter un workflow à l'app-registry. Le seuil est borné à]0,100[côté serveur, 50–95 dans l'écran : à 0 tout le monde est en alerte en permanence, à 100 l'alerte n'arrive jamais avant le refus d'écriture. -
Effet dans le temps : la propriété
s.storageAlertThresholdest lue au prochain calcul d'occupation. Aucune reprise de l'existant, aucune alerte rétroactive.
neo4jPlugin n'est pas concernéeincrementStorage a une seconde branche qui délègue à une extension Neo4j non managée. Elle n'est
pas utilisée ici (neo4jPlugin: false) et n'a pas été modifiée : sur une plate-forme qui l'activerait,
le seuil par établissement serait sans effet.
Conformité
Évaluation au référentiel Open ENT NG (module workspace).
| Maillon de la chaîne qualité | Référence |
|---|---|
| 🎯 Fonctionnalités attendues | fiche fonctionnelle |
| 🧪 Tests réalisés | couverture e2e |
| ✅ Tests de conformité | tableau de conformité |