Skip to main content

Espace documentaire — détails techniques

Fiche fonctionnelle

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.

RouteAccèsRôle
PUT /workspace/document/:id/portal-publish@SecuredAction("workspace.document.publish", RESOURCE)Publie le document
DELETE /workspace/document/:id/portal-publishidemRetire la publication
GET /workspace/pub/document/:idanonymeSert le fichier publié (getFile(request, null, true))
GET /workspace/pub/structure/:structureId/documentsanonymeListe 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éeFichierMécanisme
Upload NextCloud directconnectors/nextcloud/backend/.../DefaultDocumentsService.java (checkExtensionAllowed, uploadStreamedFile)Vérifie avant l'écriture WebDAV ; rejette extension.forbidden
Déplacement/copie workspace → NextCloudidem, sendWorkspaceFileToNCMê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/établissementfr.openent.nextcloud.helper.DesktopConfigHelperFusion 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#incrementStorage pose u.alertSize sur le UserBook et renvoie un drapeau notify sur la seule transition — franchir le seuil notifie une fois, pas à chaque dépôt. notifySmallAmountOfFreeSpace émet alors la notification timeline workspace.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 alertThreshold

    MIN() ignore les null : 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 compteur nbUsers des 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), en AUTHENTICATED avec 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.storageAlertThreshold est lue au prochain calcul d'occupation. Aucune reprise de l'existant, aucune alerte rétroactive.

La branche neo4jPlugin n'est pas concernée

incrementStorage 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 attenduesfiche fonctionnelle
🧪 Tests réaliséscouverture e2e
✅ Tests de conformitétableau de conformité