2. Relatiebeheer
Deze sectie beschrijft de kern van relatiebeheer tussen accounts, inclusief relatietypen, uitnodigingen, actieve relaties en gebeurtenissenhistorie.
2.1 RelationshipTypes
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| RelationshipTypes | Relatiebeheer | Definitie van beschikbare relatietypen zoals vriendschap, docent-leerling en ouder/voogd-leerling. | RelationshipInvitations, UserRelationships |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| Code | nvarchar(50) | - | N | N | - | J | N | J | Technische code, bijvoorbeeld Friendship, TeacherStudent, GuardianStudent. |
| Name | nvarchar(100) | - | N | N | - | J | N | N | Leesbare naam van het relatietype. |
| IsActive | bit | 1 | N | N | - | N | N | J | Maakt type actief of verborgen. |
Validaties / constraints
- Code is uniek. Alleen vooraf gedefinieerde relatietypen zijn toegestaan.
Business rules
- Relatietypen hebben elk eigen schermgedrag, rechten en ontkoppelregels. Voor OefenHub zijn dit minimaal Friendship, TeacherStudent, GuardianStudent, TeacherTeacher en AdminAdmin.
- AdminAdmin is een systeemrelatie tussen twee gebruikers met een actieve beheerdersrol; deze relatie ondersteunt beheercontext, directe samenwerking en het kunnen sturen van privéberichten zonder aparte handmatige vriendschapskoppeling.
Lifecycle / gedrag
- Relatietypen worden gedeactiveerd in plaats van verwijderd.
Designkeuzes
- Losse tabel houdt typegedrag configureerbaar en expliciet.
Foreign keys op databaseniveau
- Geen harde foreign keys op databaseniveau binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
- De velden Code functioneren als code-, enum- of sleutelwaarden en verwijzen bewust niet via een harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
2.2 RelationshipInvitations
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| RelationshipInvitations | Relatiebeheer | Registreert uitnodigingen voor nieuwe relaties, inclusief doel-e-mailadres, rolcontext, status en afhandeling. | Users, Roles, RelationshipTypes, UserRelationships, SystemMessages |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| RelationshipTypeId | uniqueidentifier | - | N | J | RelationshipTypes.Id | N | N | J | Type van de relatie. |
| FromUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Soft link naar Users.Id voor de uitnodigende gebruiker; geen harde database-FK vanwege de modulegrens met identity. |
| FromRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | J | Soft link naar Roles.Id voor de rolcontext van de uitnodiger; geen harde database-FK vanwege de modulegrens met authorization. |
| ToEmail | nvarchar(320) | - | N | N | - | N | N | J | Doel-e-mailadres van de uitnodiging. |
| ToUserId | uniqueidentifier | null | N | N | Users.Id | N | J | J | Optionele soft link naar Users.Id wanneer het doelaccount al bekend is; geen harde database-FK. |
| TargetRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | J | Soft link naar Roles.Id voor de verwachte doelrol; geen harde database-FK. |
| Status | nvarchar(20) | Pending | N | N | - | N | N | J | Functionele waarden: Pending, Accepted, Rejected, Expired. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Aanmaakmoment. |
| RespondedAtUtc | datetime2 | null | N | N | - | N | J | N | Moment van acceptatie of afwijzing. |
| RespondedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | N | Soft link naar Users.Id voor de gebruiker die reageerde; geen harde database-FK. |
| ResolvedRelationshipId | uniqueidentifier | null | N | J | UserRelationships.Id | N | J | J | Relatie die ontstond uit acceptatie. |
| CleanupAfterUtc | datetime2 | null | N | N | - | N | J | J | Moment waarop afgehandelde uitnodiging uit het openstaande overzicht verdwijnt. |
| IsVisibleInPendingOverview | bit | 1 | N | N | - | N | N | J | Tijdelijke zichtbaarheid in overzicht openstaande uitnodigingen. |
Validaties / constraints
- Openstaande actieve uitnodiging op dezelfde combinatie (RelationshipTypeId, FromUserId, FromRoleId, ToEmail, TargetRoleId) mag maar één keer voorkomen.
- Status is beperkt tot Pending, Accepted, Rejected, Expired.
Business rules
- Relatievormen lopen in beginsel via uitnodiging en acceptatie.
- Bij multi-role gebruikers kiest de uitnodiger expliciet vanuit welke rol de uitnodiging wordt verstuurd. Uitzondering hierop is AdminAdmin: deze relatie wordt systeemmatig aangemaakt tussen actieve beheerders en gebruikt geen uitnodigings- of acceptatieflow.
- Afgehandelde uitnodigingen worden niet meer als openstaande uitnodiging getoond. Accepted, Rejected en Expired blijven historisch bewaard in RelationshipInvitations en RelationshipEvents, maar verdwijnen uit de openstaande lijsten.
Lifecycle / gedrag
- TickerQ plant cleanup-taken in om afgehandelde uitnodigingen na een configureerbare periode uit het openstaande overzicht te verwijderen. Onbeantwoorde uitnodigingen kunnen daarnaast na een configureerbare openstaande termijn automatisch verlopen en status Expired krijgen.
- Historie in de tabel blijft behouden.
Designkeuzes
- Uitnodiging is bewust los gemodelleerd van de uiteindelijke relatie. Daardoor kunnen uitnodigingen bestaan zonder bestaande userrelatie of zelfs zonder reeds bestaand doelaccount.
- AdminAdmin gebruikt deze tabel bewust niet, omdat het een systeemgegenereerde beheerrelatie betreft en geen door eindgebruikers gestarte uitnodiging.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: RelationshipTypeId -> RelationshipTypes.Id; ResolvedRelationshipId -> UserRelationships.Id.
Functionele / logische verwijzingen zonder harde FK
FromUserId,ToUserIdenRespondedByUserIdzijn soft links naarUsers.Id.FromRoleIdenTargetRoleIdzijn soft links naarRoles.Id. Deze verwijzingen worden applicatief gevalideerd via identity-/authorizationcontracten en krijgen geen harde database-FK. De veldenStatusenToEmailblijven inhoudelijke of technische contextwaarden zonder harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
2.3 UserRelationships
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| UserRelationships | Relatiebeheer | Vastgelegde relatie tussen twee gebruikers binnen een specifiek relatietype en rol-context-combinatie. | Users, Roles, RelationshipTypes, RelationshipInvitations, RelationshipEvents, TeacherStudentLevelAccess |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| RelationshipTypeId | uniqueidentifier | - | N | J | RelationshipTypes.Id | N | N | J | Type van de relatie. |
| FromUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Soft link naar Users.Id voor de eerste zijde van de relatie; geen harde database-FK. |
| FromRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | J | Soft link naar Roles.Id voor de rolcontext van de eerste zijde; geen harde database-FK. |
| ToUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Soft link naar Users.Id voor de tweede zijde van de relatie; geen harde database-FK. |
| ToRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | J | Soft link naar Roles.Id voor de rolcontext van de tweede zijde; geen harde database-FK. |
| InitiatedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | N | Soft link naar Users.Id voor de initiërende gebruiker; geen harde database-FK. |
| InitiatedFromRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | N | Soft link naar Roles.Id voor de initiërende rolcontext; geen harde database-FK. |
| InitiatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Startmoment van de relatie. |
| AcceptedAtUtc | datetime2 | null | N | N | - | N | J | N | Moment waarop uitnodiging geaccepteerd werd. |
| IsActive | bit | 1 | N | N | - | N | N | J | Actieve of ontkoppelde relatie. |
| DeactivatedAtUtc | datetime2 | null | N | N | - | N | J | N | Moment van ontkoppeling. |
| DeactivatedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | N | Soft link naar Users.Id voor de gebruiker die de relatie beëindigde; geen harde database-FK. |
| DeactivationReason | nvarchar(1000) | null | N | N | - | N | J | N | Optionele toelichting op de ontkoppeling. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakdatum van het record. |
| UpdatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Laatste wijzigingsdatum. |
Validaties / constraints
- Er mag maximaal één actieve relatie bestaan per combinatie RelationshipTypeId + FromUserId + FromRoleId + ToUserId + ToRoleId.
- Spiegelbeeldrelaties vereisen een eenduidige normalisatie of aanvullende business rule in de applicatielaag.
Business rules
- Dezelfde twee gebruikers mogen meerdere actieve relaties hebben, mits het verschillende relatietypen of rol-context-combinaties betreffen. Daardoor kan een gebruiker bijvoorbeeld tegelijk als docent en als ouder/voogd met dezelfde leerling gekoppeld zijn.
- Ook kan tussen twee docenten een actieve TeacherTeacher-relatie bestaan zonder dat dit toegang geeft tot elkaars leerlingen of resultaten.
- Voor twee actieve beheerders wordt daarnaast automatisch een AdminAdmin-relatie vastgelegd; deze relatie geldt als geldige koppeling voor onderlinge privéberichten.
- Relatiebeheer kent geen aparte blokkeerfunctie of blokkeerlijst. Een beëindigde relatie kan alleen opnieuw ontstaan via een nieuwe geldige uitnodiging of een expliciete systeem-/beheerflow.
- Het datamodel introduceert geen functioneel hard maximum op het aantal relaties per relatietype. Eventuele technische limieten voor performance of misbruikpreventie horen als expliciete domein- of beheerregel te worden vastgelegd en mogen niet impliciet uit het schema volgen.
Lifecycle / gedrag
- Relaties worden niet hard verwijderd.
- Ontkoppelen betekent IsActive = false plus auditinformatie over actor, tijdstip en reden.
- Een AdminAdmin-relatie is zichtbaar in de UI, maar zolang beide gebruikers een actieve beheerdersrol hebben mag deze niet handmatig worden ontkoppeld; de ontkoppelactie wordt dan uitgeschakeld met toelichting. Zodra één van beide gebruikers geen beheerder meer is, blijft de relatie bestaan maar wordt handmatig ontkoppelen weer toegestaan.
Designkeuzes
- Relatie is los gemodelleerd van uitnodigingen en los van docentniveau-autorisaties. Dat houdt de tabel zuiver als kern van relatie-identiteit. AdminAdmin wordt bewust als apart relatietype gemodelleerd en niet omgezet naar Friendship, zodat de semantiek van leerlingvriendschap en oefeningdelen zuiver blijft terwijl een bestaand communicatiekanaal open kan blijven.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: RelationshipTypeId -> RelationshipTypes.Id.
Functionele / logische verwijzingen zonder harde FK
FromUserId,ToUserId,InitiatedByUserIdenDeactivatedByUserIdzijn soft links naarUsers.Id.FromRoleId,ToRoleIdenInitiatedFromRoleIdzijn soft links naarRoles.Id. Zij behouden de functionele relatiecontext zonder harde database-FK naar identity of authorization.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
2.4 RelationshipEvents
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| RelationshipEvents | Relatiebeheer | Audit- en gebeurtenissenlog voor relatie- en uitnodigingsgerelateerde acties. | UserRelationships, RelationshipInvitations, Users, Roles |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| RelationshipId | uniqueidentifier | null | N | J | UserRelationships.Id | N | J | J | Optionele verwijzing naar actieve of historische relatie. |
| InvitationId | uniqueidentifier | null | N | J | RelationshipInvitations.Id | N | J | J | Optionele verwijzing naar een uitnodiging. |
| EventType | nvarchar(50) | - | N | N | - | N | N | J | Bijvoorbeeld invitation_sent, invitation_accepted, relationship_deactivated, unlink_requested. |
| ActorUserId | uniqueidentifier | null | N | N | Users.Id | N | J | J | Soft link naar Users.Id voor de actor van de gebeurtenis; geen harde database-FK. |
| ActorRoleId | uniqueidentifier | null | N | N | Roles.Id | N | J | N | Soft link naar Roles.Id voor de actorrol; geen harde database-FK. |
| TargetUserId | uniqueidentifier | null | N | N | Users.Id | N | J | J | Optionele soft link naar Users.Id voor het doel van de actie; geen harde database-FK. |
| OccurredAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Moment van optreden. |
| Summary | nvarchar(250) | - | N | N | - | N | N | N | Korte samenvatting voor logging en beheer. |
| DetailsJson | nvarchar(max) | null | N | N | - | N | J | N | Uitbreidbare aanvullende details. |
Validaties / constraints
- Minimaal één van RelationshipId of InvitationId moet gevuld zijn.
- EventType wordt centraal gestandaardiseerd.
Business rules
- RelationshipEvents vormt de technische audittrail en is geen vervanging voor systeemnotificaties; notificaties kunnen uit events worden afgeleid.
- Ook systeemgegenereerde acties rond AdminAdmin, zoals automatische creatie bij roltoekenning en het weer ontkoppelbaar worden na verlies van een beheerdersrol aan één zijde, moeten via events herleidbaar zijn.
Lifecycle / gedrag
- Events worden niet aangepast of verwijderd, behalve in uitzonderlijke technische herstelprocedures.
Designkeuzes
- Een aparte eventlaag vereenvoudigt historie, debugging en afleiding van notificaties.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: RelationshipId -> UserRelationships.Id; InvitationId -> RelationshipInvitations.Id.
Functionele / logische verwijzingen zonder harde FK
ActorUserIdenTargetUserIdzijn soft links naarUsers.Id.ActorRoleIdis een soft link naarRoles.Id. Deze velden houden de gebeurtenis herleidbaar zonder harde database-FK naar identity of authorization. De veldenEventTypeenDetailsJsonzijn inhoudelijke contextwaarden zonder harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
2.5 Relatieflows, uitnodigingen en ontkoppeling
Deze aanvullende regels zijn afgeleid uit de volledige set generieke relatie-usecases.
2.5.1 Relatietypen en rolrichting
| Relatietype | Functionele richting | Technische rolrichting | Belangrijk gevolg |
|---|---|---|---|
Friendship | Leerling nodigt leerling uit als vriend. | FromRole = Student, TargetRole = Student. | Ondersteunt toegestane privécommunicatie en oefeningdelen binnen OefenHub. |
GuardianStudent | Leerling nodigt ouder/voogd uit. | FromRole = Student, TargetRole = Guardian. | Ondersteunt ouder/voogd-inzage en live meekijken volgens autorisatie, maar geen oefenen namens de leerling. |
AdminAdmin | Systeemrelatie tussen actieve beheerders. | Beheerder ↔ beheerder. | Wordt systeemmatig beheerd en gebruikt geen uitnodigingsflow. |
2.5.2 Uitnodigingen naar onbekende e-mailadressen
RelationshipInvitations.ToEmailblijft verplicht en bewaart het e-mailadres waarop de uitnodiging is gericht.ToUserIdis optioneel en blijft null wanneer het e-mailadres nog niet bij een OefenHub-account hoort.- Onbekende e-mailadressen worden eerst teruggekoppeld aan de uitnodigende gebruiker; de gebruiker kiest daarna expliciet of hiervoor een externe OefenHub-uitnodigingsmail wordt verstuurd.
- Zonder expliciete externe-mailbevestiging wordt voor een onbekend e-mailadres geen
RelationshipInvitations-record aangemaakt. - Bij een externe uitnodiging wordt duidelijk gemaakt dat de volledige naam van de uitnodigende gebruiker in de uitnodigingsmail wordt gedeeld. Dit akkoord wordt vastgelegd met
ExternalInviteConsentGivenenExternalInviteConsentAtUtc. - Een openstaande uitnodiging naar een onbekend e-mailadres kan gedurende 7 dagen worden gekoppeld aan een nieuw geregistreerd account met hetzelfde genormaliseerde e-mailadres.
- Na registratie wordt de uitnodiging gekoppeld aan
Users.Iden wordt pas daarna eenSystemMessages-record voor de ontvanger gemaakt.
2.5.3 Verwerken van uitnodigingen
- Inkomende uitnodigingen worden uitsluitend via
SystemMessagesaangeboden; de relatiepagina toont geen aparte lijst met inkomende uitnodigingen. Voor bestaande OefenHub-ontvangers registreert de uitnodigingsflow na succesvolle aanmaak van het systeembericht eenRelationshipEvents.EventType = InvitationSystemMessageCreated. - Accepteren gebeurt direct vanuit de systeemberichtcontext en vereist geen bevestigingspopup.
- Afwijzen vereist wel een bevestigingspopup.
- Bij acceptatie wordt een actief
UserRelationships-record aangemaakt en wordtRelationshipInvitations.ResolvedRelationshipIdgevuld. - Bij afwijzing of verlopen ontstaat geen
UserRelationships-record. - De uitnodiger krijgt een systeembericht of gelijkwaardige notificatie bij acceptatie én afwijzing.
- Afgehandelde uitnodigingen worden niet meer als openstaand item weergegeven; historie blijft via
RelationshipInvitationsenRelationshipEventsbeschikbaar. Voor externe uitnodigingen kan de detailsweergave daarnaast veilige maildeliverysamenvattingen ophalen via het mail-readmodel op basis vanMailSendAttempts.SourceEntityType = RelationshipInvitationenSourceEntityId = RelationshipInvitations.Id. - Herinneren van een openstaande uitnodiging gebruikt dezelfde bronstatusregels: alleen
Pendingverzoeken zijn herinnerbaar en de beheerbare cooldownadmin.SystemSettings.RelationshipInvitationReminderCooldownHoursmoet verstreken zijn. Intrekken zet de uitnodiging opWithdrawn, verbergt deze uit openstaande overzichten en legtInvitationWithdrawnvast.
2.5.4 Duplicaat- en conflictregels
- Er mag geen nieuwe uitnodiging worden aangemaakt wanneer al een actieve identieke relatie bestaat.
- Er mag geen nieuwe uitnodiging worden aangemaakt wanneer al een openstaande identieke uitnodiging bestaat.
- Kruislings openstaande uitnodigingen tussen dezelfde gebruikers en dezelfde rolcontext mogen niet tot twee openstaande uitnodigingen leiden.
- Duplicaatcontrole gebruikt het relatietype, de rolcontexten, gebruikers-id's indien bekend en de genormaliseerde e-mailwaarde.
- Dezelfde domeinregel geldt server-side voor identieke én spiegelbeeldige uitnodigingen. Frontend-zichtbaarheid of browserstate is geen afdoende bescherming tegen conflicterende uitnodigingen.
2.5.5 Ontkoppelen
- Relaties worden niet hard verwijderd.
- Direct ontkoppelen betekent
UserRelationships.IsActive = falseplusDeactivatedAtUtc,DeactivatedByUserIden waar relevantDeactivationReason. - Iedere ontkoppelactie start functioneel met een bevestigingsmodal.
- Sommige relatievormen kunnen functioneel via een ontkoppelverzoek verlopen. In dat geval wordt eerst een
RelationshipEvents-record zoalsunlink_requestedvastgelegd en wordt de relatie pas gedeactiveerd wanneer de formele afhandeling dat toestaat. - Ontkoppeling blokkeert nieuwe relatie-afhankelijke acties zoals nieuwe privéberichten, nieuwe shares of live meekijken, maar verwijdert bestaande historie niet.
- Een beheerder kan binnen beheercontext een relatie geforceerd ontkoppelen wanneer dit functioneel is toegestaan. Deze actie blijft een soft-deactivatie van
UserRelationships, vereist een reden, legt actor en rolcontext vast, registreert een relatie-event of beheerlogregel en kan systeemcommunicatie aan betrokken gebruikers veroorzaken.
2.5.6 Gestandaardiseerde RelationshipEvents.EventType-waarden
Voor de uitgewerkte relatieflows worden ten minste de volgende eventtypen verwacht:
| EventType | Betekenis |
|---|---|
invitation_sent | Relatie-uitnodiging aangemaakt. |
external_invitation_sent | Uitnodiging naar nog onbekend e-mailadres verstuurd. |
InvitationSystemMessageCreated | Interne uitnodiging aan bestaande OefenHub-gebruiker is veilig als systeembericht aangeboden. Wordt getoond in relatieverzoekdetails. |
InvitationSystemMessageFailed | Interne uitnodigingsnotificatie kon niet veilig worden klaargezet; het verzoek wordt ingetrokken/verborgen zodat geen openstaande uitnodiging zichtbaar blijft. |
InvitationMailQueued | Externe uitnodigingsmail is veilig aan de interne mailqueue/TickerQ-planning aangeboden. Wordt getoond in relatieverzoekdetails. |
InvitationMailQueueFailed | Externe uitnodigingsmail kon na aanmaak niet veilig worden gequeued; het externe verzoek wordt ingetrokken/verborgen zodat geen openstaande uitnodiging zichtbaar blijft. |
InvitationReminderSystemMessageCreated | Herinnering voor een pending interne relatie-uitnodiging is veilig als systeembericht aangeboden. |
InvitationReminderMailQueued | Herinneringsmail voor een pending externe relatie-uitnodiging is veilig aan de interne mailqueue/TickerQ-planning aangeboden. |
InvitationReminderNotificationFailed | Herinnering kon niet veilig worden aangeboden; het relatieverzoek blijft pending, maar de gebruiker krijgt veilige foutfeedback. |
InvitationClaimed | Pending externe uitnodiging is na registratie gekoppeld aan een geverifieerd OefenHub-account. |
InvitationDeclinedBecauseRoleIncompatible | Geclaimde uitnodiging is tijdens onboarding afgewezen omdat de gekozen rolcontext niet past. |
invitation_accepted | Ontvanger heeft de uitnodiging geaccepteerd. |
invitation_rejected | Ontvanger heeft de uitnodiging afgewezen. |
invitation_expired | Uitnodiging is verlopen. |
relationship_created | Actieve relatie is ontstaan uit acceptatie of systeemactie. |
relationship_deactivated | Actieve relatie is ontkoppeld. |
unlink_requested | Ontkoppelverzoek is vastgelegd zonder directe deactivatie. |
2.6 Relaties binnen berichten-, profiel- en accountcontext
2.6.1 Relaties als voorwaarde voor privéberichten
- Een privébericht of nieuwe privéberichtthread mag alleen worden aangemaakt wanneer op verzendmoment server-side een actieve relatie of systeemrelatie bestaat die privécommunicatie toestaat.
- Frontend-zichtbaarheid van een ontvanger is geen autorisatie. De relatie- en rolcontext wordt opnieuw gecontroleerd bij het verzenden.
- Ontkoppeling van een relatie verwijdert bestaande privéberichten niet, maar blokkeert nieuwe relatie-afhankelijke communicatie wanneer er geen andere geldige relatiecontext resteert.
- Een
AdminAdmin-relatie geldt als systeemrelatie voor privécommunicatie tussen actieve beheerders, zonder dat hiervoor een vriendschapsrelatie nodig is.
2.6.2 Profiel- en niveaucontext
- Het wijzigen van profielgegevens, voorkeuren of toegankelijkheidsinstellingen wijzigt geen relaties.
- Het kiezen van een actief niveau in het profiel geeft geen nieuwe niveau-autorisatie; toegestane niveaus worden afgeleid uit bestaande relatie- en toegangstabellen.
- Bij accountverwijdering of anonimisering worden actieve relaties, uitnodigingen en afgeleide toegangscontexten administratief opgeschoond volgens de account-lifecycleflow, zonder historische relatie- en eventinformatie hard te verwijderen.
2.7 Accountprovisioning en relatie-afhankelijkheden
2.7.1 Accountprovisioning en relatie-uitnodigingen
Uitnodigingen naar nog onbekende e-mailadressen blijven in RelationshipInvitations bewaard met ToEmail gevuld en ToUserId = null. Pas nadat accountprovisioning een intern Users.Id heeft opgeleverd, mag OefenHub openstaande uitnodigingen met hetzelfde genormaliseerde e-mailadres koppelen door ToUserId te vullen.
Alleen uitnodigingen die nog Pending, geldig en inhoudelijk passend zijn mogen worden gekoppeld. Verlopen, afgewezen, geaccepteerde, ingetrokken of conflicterende uitnodigingen worden door login of provisioning niet opnieuw geactiveerd. De koppeling is OefenHub-applicatielogica en staat los van de externe identity provider.
2.7.2 Accountanonimisering en relatiecontext
Bij accountverwijdering of anonimisering worden actieve relaties van of naar het account administratief beëindigd of niet langer autoriserend gemaakt. Relaties worden niet hard verwijderd wanneer historische reconstructie of audit nodig blijft.
Openstaande relatie-uitnodigingen die door of naar het geanonimiseerde account lopen, mogen niet langer accepteerbaar zijn. Bestaande RelationshipEvents blijven historisch beschikbaar, maar de zichtbare identiteit van de verwijderde gebruiker wordt alleen nog als geanonimiseerde identiteit getoond.
2.8 Ouder-/voogd- en beheerderregels voor relaties
| Onderwerp | Aanscherping |
|---|---|
| Ouder-/voogdrelatie als autorisatiebron | Ouder-/voogdfunctionaliteit gebruikt een actieve GuardianStudent-relatie als primaire autorisatiebron voor kinderenoverzicht, kindinformatie, resultaten, geschiedenis en live meekijken. |
| Ouder-/voogdontkoppeling | Ontkoppelen vanuit ouder-/voogdcontext is een soft-deactivatie van de relatie. Bestaande oefenruns, geschiedenis en auditregels blijven bestaan. |
| Systeemcommunicatie bij ontkoppeling | Na ontkoppeling wordt het kind via systeemcommunicatie geïnformeerd. Deze communicatie verandert de relatie niet opnieuw en creëert geen nieuwe relatie-uitnodiging. |
| Sortering en naamweergave | Sortering en naamweergave in het kinderenoverzicht zijn presentatievoorkeuren en veranderen geen relatie, autorisatie of zichtbare dataset. |
| Beheerder-docentondersteuning | Beheerderflows voor docentondersteuning mogen docent-docentcontext, collaborators en leerlingtoegang supportmatig inspecteren of corrigeren, maar relatievorming zelf blijft bronhoudend in het relatiedomein. |
| Geforceerde docent-docenttoegang | Een beheerder kan een docent-docentcontext alleen forceren binnen een expliciete supportflow, met auditregistratie en zonder stilzwijgende leerling-, resultaat- of live-meekijktoegang. |
2.9 Ouder-/voogdrelatie bij resultaten en live-inzage
| Onderwerp | Aanscherping |
|---|---|
| Primaire autorisatiebron | Voor kinderenoverzicht, kindinformatie, resultaten, geschiedenis, PDF-export en live meekijken is een actieve GuardianStudent-relatie vereist. |
| Server-side hercontrole | Iedere raadpleeg-, filter-, detail-, export- en liveactie controleert de relatie opnieuw server-side. Clientstate, ChildUserId, RunId, filters of oude routes verruimen nooit de toegang. |
| Alle niveaus | De ouder-/voogdrelatie geeft resultaatinzage over alle historische niveaus van het gekoppelde kind, zolang de relatie actief is. Dit is bewust anders dan docentresultaatinzage. |
| Ontkoppeling | Ontkoppelen is soft-deactivatie van UserRelationships en verwijdert geen kindaccount, oefenruns, geschiedenis, resultaten of auditregels. |
| Communicatie na ontkoppeling | Systeemcommunicatie aan het kind is informatief. Het openen van het bericht herstelt de relatie niet en creëert geen nieuwe uitnodiging. |
Aanvulling - Externe uitnodigingen claimen na registratie
Externe relatie-uitnodigingen worden bewaard als pending uitnodigingen met een genormaliseerd e-mailadres zolang de ontvanger nog geen intern OefenHub-account heeft. Na provisioning van een account met geverifieerd e-mailadres worden alle pending externe uitnodigingen voor dat e-mailadres geclaimd.
Aanbevolen datacontract:
| Onderdeel | Betekenis |
|---|---|
RelationshipInvitations.ToUserId of gelijkwaardige ontvangerkoppeling | Wordt gevuld zodra de externe uitnodiging aan een intern account is gekoppeld. |
| Genormaliseerd e-mailadres | Blijft nodig om claimbare externe uitnodigingen te vinden zolang ToUserId nog leeg is. |
RelationshipEvents.InvitationClaimed | Audit-event dat de externe uitnodiging aan een interne gebruiker is gekoppeld. |
RelationshipEvents.InvitationDeclinedBecauseRoleIncompatible | Audit-event wanneer een geclaimde uitnodiging door rolkeuze niet meer toepasbaar is. |
Claimen accepteert de relatie niet automatisch. Acceptatie maakt pas later een actieve UserRelationship aan. Herinneringen gebruiken na claimen het interne systeemberichtkanaal in plaats van externe mail.