Skip to main content

Messagerie — détails techniques

Fiche fonctionnelle

Description et fonctionnalités de ce module : Messagerie.

Couverture de tests

6 scénario(s) e2e couvrent ce module — dossier apps/open-ent-e2e/src/modules/26_messagerie.

6 test(s) e2e dans 6 scénario(s) — voir le détail

Messagerie 01_acces_au_module.spec.ts

  • accès au module

Messagerie 02_envoi_message.spec.ts

  • envoi de message

Messagerie — conversation enseignant ↔ élève 03_conversation_prof_eleve.spec.ts

  • échange en 3 messages sur un devoir non rendu

Messagerie — signaler un abus 04_signalement_abus.spec.ts

  • signaler un message reçu comme abusif (modération ADML dans le dashboard)

Messagerie — horaires (lecture seule) 05_horaires_lecture_seule.spec.ts

  • hors plage, l'élève voit le bandeau de fermeture et le bouton « Envoyer » est désactivé

Messagerie — envoi différé 06_envoi_differe.spec.ts

  • programmer un envoi de message (composer → Programmer l'envoi → date/heure → dossier Programmés)

➡️ Statut dans le dashboard E2E · Résultats détaillés

Restrictions de communication (exclusion élève · coupure justice)

Deux mesures d'encadrement ciblées, administrées depuis le dashboard et appliquées côté serveur.

Exclusion temporaire d'un élève

  • Stockage : collection Mongo messaging.exclusions, un document par exclusion { _id: "<structureId>:<userId>", userId, userName, structureId, blockedUntil (epoch ms), reason, createdBy, createdByName, createdAt }.
  • Backend conversation : util StudentMessagingExclusions (cache rafraîchi, isExcluded(userId, now)) + StudentMessagingExclusionsController. L'envoi est refusé dans ConversationController#send (403 conversation.error.messaging.excluded) et le worker ScheduledMessageSender retient les messages programmés d'un élève exclu.
  • Backend chat-nats : util miroir StudentMessagingExclusions (même collection, lecture seule) ; blocage dans ChatWebSocketHandler (trame {event:error, code:"messaging-excluded"}).
  • API (routes AUTHENTICATED + contrôle manuel) : GET|POST /conversation/messaging-exclusions/:structureId, DELETE /conversation/messaging-exclusions/:structureId/:userId, GET /conversation/messaging-exclusions (statut/bandeau).

Coupure de communication ciblée (justice)

  • Graphe : relation Neo4j bidirectionnelle (:User)-[:BLOCKED_COMMUNICATION {reason, createdBy, structureId, createdAt}]-(:User).
  • Backend communication (DefaultCommunicationService) : la clause AND NOT (n)-[:BLOCKED_COMMUNICATION]-(m) est ajoutée aux requêtes de visibilité (visibleUsersForShare, visibleUsers) et verify() renvoie canCommunicate=false pour une paire bloquée. Les deux personnes disparaissent mutuellement des destinataires (l'envoi est alors refusé par VisiblesFilter).
  • API (routes AUTHENTICATED + contrôle manuel) : GET|POST /communication/block/:structureId, DELETE /communication/block/:structureId/:userA/:userB.

Sécurité & délégation

Les contrôleurs autorisent SUPER_ADMIN, l'ADML de la structure, ou un personnel possédant l'action workflow communication.restriction.manage (déléguée par le chef via les rôles d'application). Côté dashboard, l'accès est ouvert par les sujets CASL messaging-exclusion / communication-block (super-admin + ADML), et /api/me expose canManageCommunicationRestrictions pour les personnels délégués.

Horaires d'utilisation : le mécanisme partagé

Les horaires ne valaient que pour les deux messageries ; ils portent désormais la liste des espaces auxquels ils s'appliquent.

  • Stockage : collection Mongo messaging.hours, inchangée — un document _id: "global" pour le défaut de plate-forme, un document _id: <structureId> par surcharge { enabled, days: [1..7], start: "HH:mm", end: "HH:mm", scopes: [...], updatedAt, updatedBy }. Le nom de la collection et les chemins /conversation/messaging-hours* sont conservés : les renommer perdrait les horaires déjà enregistrés, pour un gain cosmétique.
  • Compatibilité ascendante par le défaut : un document sans champ scopes ne vaut que pour messaging. C'est ce qui rend la montée de version sans effet sur l'existant — un horaire ne s'étend au blog ou au forum que si la case est cochée.
  • Lecture : org.entcore.common.utils.SpaceOpeningHours (entcore common) — cache rafraîchi périodiquement, isWriteAllowed(scope, profile, structures). Le chemin d'écriture d'un message ou d'un billet ne doit pas dépendre d'un aller-retour Mongo. init() est idempotent : plusieurs modules du même processus l'appellent.
  • Application : org.entcore.common.utils.OpeningHoursGuard#ifWriteAllowed(eb, request, scope, handler), à poser à l'entrée d'une route d'écriture. Refus en 403 opening.hours.closed avec l'horaire applicable, de quoi afficher « fermé jusqu'à telle heure » plutôt qu'une erreur muette. La lecture n'est jamais gardée.
  • Administration : le module conversation reste la source d'administration (MessagingHoursController) ; sa classe MessagingHours n'est plus qu'une façade qui délègue à SpaceOpeningHours avec la portée messaging, signatures inchangées. chat-nats garde sa propre copie de lecture et n'a rien à changer : son comportement est le défaut.

Deux règles invariantes d'un espace à l'autre : seuls les élèves sont restreints, et une personne rattachée à plusieurs établissements est autorisée dès qu'au moins un est ouvert — on ne pénalise personne pour un rattachement qu'elle n'a pas choisi.

Conformité

Évaluation au référentiel Open ENT NG (module conversation).

Maillon de la chaîne qualitéRéférence
🎯 Fonctionnalités attenduesfiche fonctionnelle
🧪 Tests réaliséscouverture e2e
✅ Tests de conformitétableau de conformité