Loading api/config/routes.yaml 0 → 100644 +7 −0 Changes for api/config/routes.yaml: 7 added lines, 0 removed lines. Original line number Diff line number Diff line historiser_item: path: /{resource}/{id}/historiser controller: Unicaen\Utilisateur\Controller\HistoriqueController methods: [GET] requirements: id: '\d+' defaults: { _format: json } No newline at end of file api/config/services.yaml +3 −0 Changes for api/config/services.yaml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line services: # Unicaen\Utilisateur\Metadata\HistoriqueMetadataDecorator: # decorates: 'api_platform.metadata.resource.metadata_collection_factory' # arguments: ['@Unicaen\Utilisateur\Metadata\HistoriqueMetadataDecorator.inner'] _defaults: autowire: true autoconfigure: true Loading api/src/Controller/HistoriqueController.php 0 → 100644 +53 −0 Changes for api/src/Controller/HistoriqueController.php: 53 added lines, 0 removed lines. Original line number Diff line number Diff line <?php // src/History/Action/HistoriserGenericController.php namespace Unicaen\Utilisateur\Controller; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\IriConverterInterface; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class HistoriqueController { public function __construct( private readonly ResourceMetadataCollectionFactoryInterface $factory, private readonly IriConverterInterface $iriConverter, ) {} public function __invoke(string $resource, int|string $id, Request $request): JsonResponse { // Trouve la classe à partir du "short name" (ex: utilisateur => App\Entity\Utilisateur) $class = $this->resolveClassFromShortName($resource); // Vérifie que c’est bien une ressource API $meta = $this->factory->create($class)[0] ?? null; if (!$meta instanceof ApiResource) { throw new NotFoundHttpException('Not an ApiResource'); } // Charge l’item depuis l’IRI (astuce) $iri = sprintf('/%s/%s', strtolower($resource), $id); $item = $this->iriConverter->getResourceFromIri($iri); if (method_exists($item, 'markHistorised')) { $item->markHistorised(); } // Laisse ton DataPersister/Processor sauver, ou fais un flush ici si tu veux. return new JsonResponse(['status' => 'ok', 'resource' => $resource, 'id' => $id]); } private function resolveClassFromShortName(string $short): string { $short = preg_replace('/-+/', ' ', strtolower($short)); $short = str_replace(' ', '', ucwords($short)); // user-account => UserAccount $fqcn = "Unicaen\\Utilisateur\\{$short}"; if (!class_exists($fqcn)) { throw new NotFoundHttpException("Unknown resource: $short"); } return $fqcn; } } api/src/Entity/AbstractUser.php +3 −1 Changes for api/src/Entity/AbstractUser.php: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; use Unicaen\Utilisateur\Dto\UtilisateurInscriptionInput; use Unicaen\Utilisateur\Dto\UtilisateurMeResource; use Unicaen\Utilisateur\Model\HistoriqueAwareInterface; use Unicaen\Utilisateur\Model\RoleInterface; use Unicaen\Utilisateur\Model\UnicaenUserInterface; use Unicaen\Utilisateur\Repository\UtilisateurRepository; Loading @@ -30,8 +31,9 @@ use Unicaen\Utilisateur\State\UtilisateurMeStateProvider; use Unicaen\Utilisateur\State\UtilisateurPasswordHasherProcessor; #[ORM\MappedSuperclass] abstract class AbstractUser implements UnicaenUserInterface, PasswordAuthenticatedUserInterface abstract class AbstractUser implements UnicaenUserInterface, PasswordAuthenticatedUserInterface, HistoriqueAwareInterface { use HistoriqueAwareTrait; const PASSWORD_LDAP = 'ldap'; const PASSWORD_SHIB = 'shib'; Loading api/src/Entity/HistoriqueAwareTrait.php 0 → 100644 +177 −0 Changes for api/src/Entity/HistoriqueAwareTrait.php: 177 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Unicaen\Utilisateur\Entity; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use DateTime; use Unicaen\Utilisateur\Dto\UtilisateurMeResource; use Unicaen\Utilisateur\Model\HistoriqueAwareInterface; use Unicaen\Utilisateur\Model\UnicaenUserInterface as UserInterface; use Unicaen\Utilisateur\State\UtilisateurMeStateProvider; trait HistoriqueAwareTrait { protected ?DateTime $histoCreation = null; protected ?DateTime $histoModification = null; protected ?DateTime $histoDestruction = null; protected ?UserInterface $histoCreateur = null; protected ?UserInterface $histoModificateur = null; protected ?UserInterface $histoDestructeur = null; public function setHistoCreation(?DateTime $histoCreation): HistoriqueAwareInterface { $this->histoCreation = $histoCreation; return $this; } public function getHistoCreation(): ?DateTime { return $this->histoCreation; } public function setHistoDestruction(?DateTime $histoDestruction): HistoriqueAwareInterface { $this->histoDestruction = $histoDestruction; return $this; } public function getHistoDestruction(): ?DateTime { return $this->histoDestruction; } public function setHistoModification(?DateTime $histoModification): HistoriqueAwareInterface { $this->histoModification = $histoModification; return $this; } public function getHistoModification(): ?DateTime { return $this->histoModification; } public function setHistoModificateur(?UserInterface $histoModificateur = null): HistoriqueAwareInterface { $this->histoModificateur = $histoModificateur; return $this; } public function getHistoModificateur(): ?UserInterface { return $this->histoModificateur; } public function setHistoDestructeur(?UserInterface $histoDestructeur = null): HistoriqueAwareInterface { $this->histoDestructeur = $histoDestructeur; return $this; } public function getHistoDestructeur(): ?UserInterface { return $this->histoDestructeur; } public function setHistoCreateur(?UserInterface $histoCreateur = null): HistoriqueAwareInterface { $this->histoCreateur = $histoCreateur; return $this; } public function getHistoCreateur(): ?UserInterface { return $this->histoCreateur; } /** Historisation / Restauration **********************************************************************************/ /** * @param UserInterface|null $destructeur Auteur de la suppression ; si null, peut être renseigné * automatiquement (cf. HistoriqueListener) si la classe implémente * HistoriqueAwareInterface * @param DateTime|null $dateDestruction Date éventuelle de la suppression * @return HistoriqueAwareInterface * @see HistoriqueAwareInterface */ public function historiser(?UserInterface $destructeur = null, ?DateTime $dateDestruction = null): HistoriqueAwareInterface { if ($destructeur) { $this->setHistoDestructeur($destructeur); } if (empty($dateDestruction)) { try { $dateDestruction = new DateTime(); } catch (Exception $e) { throw new RuntimeException("Impossible d'instancier un DateTime!", null, $e); } } $this->setHistoDestruction($dateDestruction); return $this; } public function dehistoriser(): HistoriqueAwareInterface { $this->setHistoDestructeur(); $this->setHistoDestruction(null); return $this; } /** * @param DateTime|null $dateObs Date d'observation éventuelle * @return bool */ public function estHistorise(DateTime $dateObs = null): bool { return !$this->estNonHistorise($dateObs); } /** * @param DateTime|null $dateObs Date d'observation éventuelle * @return bool */ public function estNonHistorise(DateTime $dateObs = null): bool { if (empty($dateObs)) { $dateObs = new DateTime(); } $dObs = $dateObs->format('Y-m-d'); $dDeb = $this->getHistoCreation()?->format('Y-m-d'); $dFin = $this->getHistoDestruction()?->format('Y-m-d'); if ($dDeb && !($dDeb <= $dObs)) return false; if ($dFin && !($dObs < $dFin)) return false; return true; } public function derniereModification(): string { if ($this->getHistoDestruction() !== null) { return "Historisation " . $this->getHistoDestruction()->format('d/m/Y à H:i:s') . " par " . $this->getHistoDestructeur()->getDisplayName(); } if ($this->getHistoModification() !== null) { return "Modification " . $this->getHistoModification()->format('d/m/Y à H:i:s') . " par " . $this->getHistoModificateur()->getDisplayName(); } if ($this->getHistoCreation() !== null) { return "Création " . $this->getHistoCreation()->format('d/m/Y à H:i:s') . " par " . $this->getHistoCreateur()->getDisplayName(); } return "Aucune donnée d'historisation"; } public function getDateTime(): ?string { return $this->getHistoCreation()?->format('d/m/Y à H:i'); } public function getDate(): ?string { return $this->getHistoCreation()?->format('d/m/Y'); } } No newline at end of file Loading
api/config/routes.yaml 0 → 100644 +7 −0 Changes for api/config/routes.yaml: 7 added lines, 0 removed lines. Original line number Diff line number Diff line historiser_item: path: /{resource}/{id}/historiser controller: Unicaen\Utilisateur\Controller\HistoriqueController methods: [GET] requirements: id: '\d+' defaults: { _format: json } No newline at end of file
api/config/services.yaml +3 −0 Changes for api/config/services.yaml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line services: # Unicaen\Utilisateur\Metadata\HistoriqueMetadataDecorator: # decorates: 'api_platform.metadata.resource.metadata_collection_factory' # arguments: ['@Unicaen\Utilisateur\Metadata\HistoriqueMetadataDecorator.inner'] _defaults: autowire: true autoconfigure: true Loading
api/src/Controller/HistoriqueController.php 0 → 100644 +53 −0 Changes for api/src/Controller/HistoriqueController.php: 53 added lines, 0 removed lines. Original line number Diff line number Diff line <?php // src/History/Action/HistoriserGenericController.php namespace Unicaen\Utilisateur\Controller; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\IriConverterInterface; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class HistoriqueController { public function __construct( private readonly ResourceMetadataCollectionFactoryInterface $factory, private readonly IriConverterInterface $iriConverter, ) {} public function __invoke(string $resource, int|string $id, Request $request): JsonResponse { // Trouve la classe à partir du "short name" (ex: utilisateur => App\Entity\Utilisateur) $class = $this->resolveClassFromShortName($resource); // Vérifie que c’est bien une ressource API $meta = $this->factory->create($class)[0] ?? null; if (!$meta instanceof ApiResource) { throw new NotFoundHttpException('Not an ApiResource'); } // Charge l’item depuis l’IRI (astuce) $iri = sprintf('/%s/%s', strtolower($resource), $id); $item = $this->iriConverter->getResourceFromIri($iri); if (method_exists($item, 'markHistorised')) { $item->markHistorised(); } // Laisse ton DataPersister/Processor sauver, ou fais un flush ici si tu veux. return new JsonResponse(['status' => 'ok', 'resource' => $resource, 'id' => $id]); } private function resolveClassFromShortName(string $short): string { $short = preg_replace('/-+/', ' ', strtolower($short)); $short = str_replace(' ', '', ucwords($short)); // user-account => UserAccount $fqcn = "Unicaen\\Utilisateur\\{$short}"; if (!class_exists($fqcn)) { throw new NotFoundHttpException("Unknown resource: $short"); } return $fqcn; } }
api/src/Entity/AbstractUser.php +3 −1 Changes for api/src/Entity/AbstractUser.php: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Validator\Constraints as Assert; use Unicaen\Utilisateur\Dto\UtilisateurInscriptionInput; use Unicaen\Utilisateur\Dto\UtilisateurMeResource; use Unicaen\Utilisateur\Model\HistoriqueAwareInterface; use Unicaen\Utilisateur\Model\RoleInterface; use Unicaen\Utilisateur\Model\UnicaenUserInterface; use Unicaen\Utilisateur\Repository\UtilisateurRepository; Loading @@ -30,8 +31,9 @@ use Unicaen\Utilisateur\State\UtilisateurMeStateProvider; use Unicaen\Utilisateur\State\UtilisateurPasswordHasherProcessor; #[ORM\MappedSuperclass] abstract class AbstractUser implements UnicaenUserInterface, PasswordAuthenticatedUserInterface abstract class AbstractUser implements UnicaenUserInterface, PasswordAuthenticatedUserInterface, HistoriqueAwareInterface { use HistoriqueAwareTrait; const PASSWORD_LDAP = 'ldap'; const PASSWORD_SHIB = 'shib'; Loading
api/src/Entity/HistoriqueAwareTrait.php 0 → 100644 +177 −0 Changes for api/src/Entity/HistoriqueAwareTrait.php: 177 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Unicaen\Utilisateur\Entity; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use DateTime; use Unicaen\Utilisateur\Dto\UtilisateurMeResource; use Unicaen\Utilisateur\Model\HistoriqueAwareInterface; use Unicaen\Utilisateur\Model\UnicaenUserInterface as UserInterface; use Unicaen\Utilisateur\State\UtilisateurMeStateProvider; trait HistoriqueAwareTrait { protected ?DateTime $histoCreation = null; protected ?DateTime $histoModification = null; protected ?DateTime $histoDestruction = null; protected ?UserInterface $histoCreateur = null; protected ?UserInterface $histoModificateur = null; protected ?UserInterface $histoDestructeur = null; public function setHistoCreation(?DateTime $histoCreation): HistoriqueAwareInterface { $this->histoCreation = $histoCreation; return $this; } public function getHistoCreation(): ?DateTime { return $this->histoCreation; } public function setHistoDestruction(?DateTime $histoDestruction): HistoriqueAwareInterface { $this->histoDestruction = $histoDestruction; return $this; } public function getHistoDestruction(): ?DateTime { return $this->histoDestruction; } public function setHistoModification(?DateTime $histoModification): HistoriqueAwareInterface { $this->histoModification = $histoModification; return $this; } public function getHistoModification(): ?DateTime { return $this->histoModification; } public function setHistoModificateur(?UserInterface $histoModificateur = null): HistoriqueAwareInterface { $this->histoModificateur = $histoModificateur; return $this; } public function getHistoModificateur(): ?UserInterface { return $this->histoModificateur; } public function setHistoDestructeur(?UserInterface $histoDestructeur = null): HistoriqueAwareInterface { $this->histoDestructeur = $histoDestructeur; return $this; } public function getHistoDestructeur(): ?UserInterface { return $this->histoDestructeur; } public function setHistoCreateur(?UserInterface $histoCreateur = null): HistoriqueAwareInterface { $this->histoCreateur = $histoCreateur; return $this; } public function getHistoCreateur(): ?UserInterface { return $this->histoCreateur; } /** Historisation / Restauration **********************************************************************************/ /** * @param UserInterface|null $destructeur Auteur de la suppression ; si null, peut être renseigné * automatiquement (cf. HistoriqueListener) si la classe implémente * HistoriqueAwareInterface * @param DateTime|null $dateDestruction Date éventuelle de la suppression * @return HistoriqueAwareInterface * @see HistoriqueAwareInterface */ public function historiser(?UserInterface $destructeur = null, ?DateTime $dateDestruction = null): HistoriqueAwareInterface { if ($destructeur) { $this->setHistoDestructeur($destructeur); } if (empty($dateDestruction)) { try { $dateDestruction = new DateTime(); } catch (Exception $e) { throw new RuntimeException("Impossible d'instancier un DateTime!", null, $e); } } $this->setHistoDestruction($dateDestruction); return $this; } public function dehistoriser(): HistoriqueAwareInterface { $this->setHistoDestructeur(); $this->setHistoDestruction(null); return $this; } /** * @param DateTime|null $dateObs Date d'observation éventuelle * @return bool */ public function estHistorise(DateTime $dateObs = null): bool { return !$this->estNonHistorise($dateObs); } /** * @param DateTime|null $dateObs Date d'observation éventuelle * @return bool */ public function estNonHistorise(DateTime $dateObs = null): bool { if (empty($dateObs)) { $dateObs = new DateTime(); } $dObs = $dateObs->format('Y-m-d'); $dDeb = $this->getHistoCreation()?->format('Y-m-d'); $dFin = $this->getHistoDestruction()?->format('Y-m-d'); if ($dDeb && !($dDeb <= $dObs)) return false; if ($dFin && !($dObs < $dFin)) return false; return true; } public function derniereModification(): string { if ($this->getHistoDestruction() !== null) { return "Historisation " . $this->getHistoDestruction()->format('d/m/Y à H:i:s') . " par " . $this->getHistoDestructeur()->getDisplayName(); } if ($this->getHistoModification() !== null) { return "Modification " . $this->getHistoModification()->format('d/m/Y à H:i:s') . " par " . $this->getHistoModificateur()->getDisplayName(); } if ($this->getHistoCreation() !== null) { return "Création " . $this->getHistoCreation()->format('d/m/Y à H:i:s') . " par " . $this->getHistoCreateur()->getDisplayName(); } return "Aucune donnée d'historisation"; } public function getDateTime(): ?string { return $this->getHistoCreation()?->format('d/m/Y à H:i'); } public function getDate(): ?string { return $this->getHistoCreation()?->format('d/m/Y'); } } No newline at end of file