Les parents avaient accès au lien "Emploi du temps" dans la navigation,
mais le dashboard n'affichait aucune donnée réelle : la section EDT
restait un placeholder vide ("L'emploi du temps sera disponible...").
Cette implémentation connecte le dashboard parent aux vrais endpoints API
(GET /api/me/children/{childId}/schedule/day|week/{date} et le résumé
multi-enfants), affiche le ScheduleWidget avec le prochain cours mis en
évidence (AC1), permet de cliquer sur chaque enfant dans le résumé pour
voir son EDT détaillé (AC2), et met en cache les endpoints parent dans le
Service Worker pour le mode offline (AC5).
Le handler backend est optimisé pour ne résoudre que l'enfant demandé
(via childId optionnel dans la query) au lieu de tous les enfants à chaque
appel, et les fonctions utilitaires dupliquées (formatSyncDate, timezone)
sont factorisées.
100 lines
2.6 KiB
PHP
100 lines
2.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Scolarite\Infrastructure\Security;
|
|
|
|
use App\Administration\Domain\Model\User\Role;
|
|
use App\Administration\Infrastructure\Security\SecurityUser;
|
|
|
|
use function in_array;
|
|
|
|
use Override;
|
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
|
|
|
/**
|
|
* Voter pour les autorisations sur l'emploi du temps.
|
|
*
|
|
* Seuls ADMIN et SUPER_ADMIN peuvent gérer l'EDT.
|
|
* PROF, VIE_SCOLAIRE et ELEVE peuvent le consulter.
|
|
*
|
|
* @extends Voter<string, null>
|
|
*/
|
|
final class ScheduleSlotVoter extends Voter
|
|
{
|
|
public const string VIEW = 'SCHEDULE_VIEW';
|
|
public const string CREATE = 'SCHEDULE_CREATE';
|
|
public const string EDIT = 'SCHEDULE_EDIT';
|
|
public const string DELETE = 'SCHEDULE_DELETE';
|
|
|
|
private const array SUPPORTED_ATTRIBUTES = [
|
|
self::VIEW,
|
|
self::CREATE,
|
|
self::EDIT,
|
|
self::DELETE,
|
|
];
|
|
|
|
#[Override]
|
|
protected function supports(string $attribute, mixed $subject): bool
|
|
{
|
|
return in_array($attribute, self::SUPPORTED_ATTRIBUTES, true);
|
|
}
|
|
|
|
#[Override]
|
|
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
|
|
{
|
|
$user = $token->getUser();
|
|
|
|
if (!$user instanceof SecurityUser) {
|
|
return false;
|
|
}
|
|
|
|
$roles = $user->getRoles();
|
|
|
|
return match ($attribute) {
|
|
self::VIEW => $this->canView($roles),
|
|
self::CREATE, self::EDIT, self::DELETE => $this->canManage($roles),
|
|
default => false,
|
|
};
|
|
}
|
|
|
|
/** @param string[] $roles */
|
|
private function canView(array $roles): bool
|
|
{
|
|
return $this->hasAnyRole($roles, [
|
|
Role::SUPER_ADMIN->value,
|
|
Role::ADMIN->value,
|
|
Role::PROF->value,
|
|
Role::VIE_SCOLAIRE->value,
|
|
Role::ELEVE->value,
|
|
Role::PARENT->value,
|
|
]);
|
|
}
|
|
|
|
/** @param string[] $roles */
|
|
private function canManage(array $roles): bool
|
|
{
|
|
return $this->hasAnyRole($roles, [
|
|
Role::SUPER_ADMIN->value,
|
|
Role::ADMIN->value,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param string[] $userRoles
|
|
* @param string[] $allowedRoles
|
|
*/
|
|
private function hasAnyRole(array $userRoles, array $allowedRoles): bool
|
|
{
|
|
foreach ($userRoles as $role) {
|
|
if (in_array($role, $allowedRoles, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|