From bed3a9b17a4db8fa096e2829bcd707d42ff98df8 Mon Sep 17 00:00:00 2001 From: pfleu <fleutotp@gmail.com> Date: Fri, 16 Dec 2022 15:22:37 +0100 Subject: [PATCH] =?UTF-8?q?API=20:=20Ajout=20logs=20automatiques=20d'updat?= =?UTF-8?q?e=20des=20champs=20comments=20et=20discussion=20sur=20Entry=20e?= =?UTF-8?q?t=20Lexicon.=20Ajout=20entit=C3=A9=20Trace=20et=20requ=C3=AAte?= =?UTF-8?q?=20de=20cr=C3=A9ation=20de=20trace.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/ApiBaseController.php | 4 +- src/Controller/ApiEntryController.php | 8 +- src/Controller/ApiTraceController.php | 75 +++++++++++++ src/Entity/Entry.php | 60 +++++++++- src/Entity/Headword.php | 1 - src/Entity/Lexicon.php | 76 ++++++++++++- src/Entity/Log.php | 151 ++++++++++++++++++++++++++ src/Entity/LoggableTrait.php | 28 +++++ src/Entity/Trace.php | 130 ++++++++++++++++++++++ src/Entity/User.php | 41 +++++++ 10 files changed, 566 insertions(+), 8 deletions(-) create mode 100644 src/Controller/ApiTraceController.php create mode 100644 src/Entity/Log.php create mode 100644 src/Entity/LoggableTrait.php create mode 100644 src/Entity/Trace.php diff --git a/src/Controller/ApiBaseController.php b/src/Controller/ApiBaseController.php index f148177..aa31272 100644 --- a/src/Controller/ApiBaseController.php +++ b/src/Controller/ApiBaseController.php @@ -320,7 +320,9 @@ class ApiBaseController extends AbstractController */ public function mergeEntries(Entry $targetEntry, Entry $sourceEntry) { - $targetEntry->setComments($targetEntry->getComments() . "\n\nCommentaires ajoutés suite à une fusion:\n" . $sourceEntry->getComments()); + if ($sourceEntry->getComments() && $sourceEntry->getComments() != $targetEntry->getComments()) { + $targetEntry->setCommentsBy($targetEntry->getComments() . "\n\nCommentaires ajoutés suite à une fusion:\n" . $sourceEntry->getComments(), $this->getUser()); + } $sourceAttributes = $sourceEntry->getAttributes(); $targetAttributes = $targetEntry->getAttributes(); diff --git a/src/Controller/ApiEntryController.php b/src/Controller/ApiEntryController.php index 22fb564..00c6593 100644 --- a/src/Controller/ApiEntryController.php +++ b/src/Controller/ApiEntryController.php @@ -185,7 +185,7 @@ class ApiEntryController extends ApiBaseController } /** - * Copie une sélection d'entrée vers des lexiques cibles. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire + * Copie une sélection d'entrée vers des lexiques cibles. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire * * @Route("/copy", name="api_copy_entries", methods={"POST"}) * @@ -270,7 +270,7 @@ class ApiEntryController extends ApiBaseController switch ($data['merge']) { case Lexicon::MERGE_OVERWRITE: // On remplace l'entrée cible si elle existe $targetEntry->setAttributes($entry->getAttributes()); - $targetEntry->setComments($entry->getComments()); + $targetEntry->setCommentsBy($entry->getComments(), $this->getUser()); $overwritten[] = $targetEntry->getId(); break; case Lexicon::MERGE_IGNORE: // On ne fait rien si l'entrée cible existe @@ -394,8 +394,8 @@ class ApiEntryController extends ApiBaseController return $this->createJsonResponse(401, ['error' => $message]); } else { $entry->setAttributes($data['attributes']); - if ($data['comment'] ?? null) { - $entry->setComments($entry->getComments() . "\n" . $data['comment']); + if ($data['comment'] ?? null && $data['comment'] != $entry->getComments()) { + $entry->setCommentsBy($entry->getComments() . "\n" . $data['comment'], $this->getUser()); } $this->doctrine->getManager()->flush(); } diff --git a/src/Controller/ApiTraceController.php b/src/Controller/ApiTraceController.php new file mode 100644 index 0000000..7e23ec5 --- /dev/null +++ b/src/Controller/ApiTraceController.php @@ -0,0 +1,75 @@ +<?php + +namespace App\Controller; + +use App\Entity\Entry; +use App\Entity\Graphy; +use App\Entity\Headword; +use App\Entity\Label; +use App\Entity\Lexicon; +use App\Entity\Trace; +use App\Manager\WiktionaryManager; +use App\Repository\LabelRepository; +use Doctrine\Persistence\ManagerRegistry; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Nelmio\ApiDocBundle\Annotation\Security; +use OpenApi\Annotations as OA; +use Nelmio\ApiDocBundle\Annotation\Model; +use Symfony\Component\Serializer\SerializerInterface; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + +/** + * @Route("/api/trace") + */ +class ApiTraceController extends ApiBaseController +{ + /** + * + * @Route("/create", name="api_trace_create", methods={"POST"}) + * + * @OA\Response(response=200, description="success", @OA\JsonContent(type="string")) + * @OA\Response(response=401, description="error", @OA\JsonContent(type="string")) + * @OA\Response(response=403, description="error", @OA\JsonContent(type="string")) + * @OA\Response(response=500, description="error", @OA\JsonContent(type="string")) + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * required={"origin", "action"}, + * @OA\Property(property="origin", type="string", example="Prisms"), + * @OA\Property(property="action", type="string", example="partie annulée"), + * @OA\Property(property="additional_info", type="object"), + * ) + * ) + * @OA\Tag(name="Traces") + * @Security(name="OAuth2") + */ + public function createTrace(Request $request): Response + { + $data = json_decode($request->getContent(), true); + if (!$data) { + return $this->createJsonResponse(401, ['error' => sprintf("Json non valide")]); + } + if ($missingFields = $this->getMissingFields($data, ['origin', 'action'])) { + return $this->createJsonResponse(401, ['error' => sprintf("Veuillez fournir une valeur pour: %s", implode(', ', $missingFields))]); + } + + $trace = new Trace(); + $trace->setCreatedBy($this->getUser()); + $trace->setAction($data['action']); + $trace->setOrigin($data['origin']); + if ($data['additional_info'] ?? null) { + $trace->setAdditionalInfo($data['additional_info']); + } + $this->doctrine->getManager()->persist($trace); + $this->doctrine->getManager()->flush(); + + $this->success[] = sprintf("Trace id %s créée.", $trace->getId()); + + return $this->createJsonResponse(200); + } +} diff --git a/src/Entity/Entry.php b/src/Entity/Entry.php index dcd073a..945fcf5 100644 --- a/src/Entity/Entry.php +++ b/src/Entity/Entry.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\EntryRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; use OpenApi\Annotations as OA; @@ -19,6 +21,8 @@ use OpenApi\Annotations as OA; */ class Entry { + use LoggableTrait; + /** * @ORM\Id * @ORM\GeneratedValue @@ -39,6 +43,12 @@ class Entry */ private $comments; + /** + * @Groups({"entry:read"}) + * @ORM\Column(type="text", nullable=true) + */ + private $discussion; + /** * @Groups({"entry:read"}) * @ORM\Column(type="string", length=100) @@ -69,8 +79,14 @@ class Entry */ private $lexicon; + /** + * @ORM\OneToMany(targetEntity=Log::class, mappedBy="lexicon", cascade={"remove", "persist"}) + */ + private $logs; + public function __construct() { + $this->logs = new ArrayCollection(); } public function __toString() @@ -124,7 +140,7 @@ class Entry return $this->comments; } - public function setComments(?string $comments): self + private function setComments(?string $comments): self { $this->comments = $comments; @@ -190,4 +206,46 @@ class Entry return $this; } + + public function getDiscussion(): ?string + { + return $this->discussion; + } + + private function setDiscussion(?string $discussion): self + { + $this->discussion = $discussion; + + return $this; + } + + /** + * @return Collection<int, Log> + */ + public function getLogs(): Collection + { + return $this->logs; + } + + public function addLog(Log $log): self + { + if (!$this->logs->contains($log)) { + $this->logs[] = $log; + $log->setLexicon($this); + } + + return $this; + } + + public function removeLog(Log $log): self + { + if ($this->logs->removeElement($log)) { + // set the owning side to null (unless already changed) + if ($log->getLexicon() === $this) { + $log->setLexicon(null); + } + } + + return $this; + } } diff --git a/src/Entity/Headword.php b/src/Entity/Headword.php index 5fff1a7..0a901ef 100644 --- a/src/Entity/Headword.php +++ b/src/Entity/Headword.php @@ -2,7 +2,6 @@ namespace App\Entity; -use ApiPlatform\Core\Annotation\ApiResource; use App\Repository\HeadwordRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; diff --git a/src/Entity/Lexicon.php b/src/Entity/Lexicon.php index 80fe185..8d4b5e8 100644 --- a/src/Entity/Lexicon.php +++ b/src/Entity/Lexicon.php @@ -2,7 +2,6 @@ namespace App\Entity; -use ApiPlatform\Core\Annotation\ApiResource; use App\Repository\LexiconRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -15,6 +14,9 @@ use Symfony\Component\Serializer\Annotation\Groups; */ class Lexicon { + use LoggableTrait; + + const TYPE_USER = 'User'; const TYPE_GROUP = 'Group'; const TYPE_ZERO = 'Zero'; @@ -57,6 +59,18 @@ class Lexicon */ private $category; + /** + * @Groups({"lexicon:read"}) + * @ORM\Column(type="text", nullable=true) + */ + private $comments; + + /** + * @Groups({"lexicon:read"}) + * @ORM\Column(type="text", nullable=true) + */ + private $discussion; + /** * @ORM\OneToOne(targetEntity=User::class, mappedBy="lexicon") * @Groups({"lexicon:read", "lexicon:write"}) @@ -79,6 +93,11 @@ class Lexicon */ private $graphyLists; + /** + * @ORM\OneToMany(targetEntity=Log::class, mappedBy="lexicon", cascade={"remove", "persist"}) + */ + private $logs; + public function __toString() { switch ($this->getCategory()) { @@ -93,6 +112,7 @@ class Lexicon { $this->entries = new ArrayCollection(); $this->graphyLists = new ArrayCollection(); + $this->logs = new ArrayCollection(); } public function isZero() @@ -259,4 +279,58 @@ class Lexicon return $this; } + + public function getComments(): ?string + { + return $this->comments; + } + + private function setComments(?string $comments): self + { + $this->comments = $comments; + + return $this; + } + + public function getDiscussion(): ?string + { + return $this->discussion; + } + + private function setDiscussion(?string $discussion): self + { + $this->discussion = $discussion; + + return $this; + } + + /** + * @return Collection<int, Log> + */ + public function getLogs(): Collection + { + return $this->logs; + } + + public function addLog(Log $log): self + { + if (!$this->logs->contains($log)) { + $this->logs[] = $log; + $log->setLexicon($this); + } + + return $this; + } + + public function removeLog(Log $log): self + { + if ($this->logs->removeElement($log)) { + // set the owning side to null (unless already changed) + if ($log->getLexicon() === $this) { + $log->setLexicon(null); + } + } + + return $this; + } } diff --git a/src/Entity/Log.php b/src/Entity/Log.php new file mode 100644 index 0000000..32cc159 --- /dev/null +++ b/src/Entity/Log.php @@ -0,0 +1,151 @@ +<?php + +namespace App\Entity; + +use App\Repository\LexiconRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * @ORM\Entity(repositoryClass=LexiconRepository::class) + * @ORM\HasLifecycleCallbacks + */ +class Log +{ + const CATEGORY_UPDATE = 'Update'; + + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + + /** + * @Groups({"log:read"}) + * @ORM\Column(type="datetime_immutable") + */ + private $createdAt; + + /** + * @ORM\ManyToOne(targetEntity=User::class, inversedBy="logs") + * @Groups({"log:read"}) + */ + private $createdBy; + + /** + * @ORM\Column(type="string", length=80) + * @Groups({"log:read"}) + */ + private $category; + + /** + * @Groups({"log:read"}) + * @ORM\Column(type="text", nullable=true) + */ + private $content; + + /** + * @ORM\ManyToOne(targetEntity=Lexicon::class, inversedBy="logs") + */ + private $lexicon; + + /** + * @ORM\ManyToOne(targetEntity=Entry::class, inversedBy="logs") + */ + private $entry; + + public function __toString() + { + return $this->getCategory() . ' du ' . $this->getCreatedAt()->format('d/m/Y à h:i:s'); + } + + public function __construct() + { + $this->createdAt = new \DateTimeImmutable(); + } + + public function isUpdate() + { + return $this->getCategory() === self::CATEGORY_UPDATE; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getCreatedAt(): ?\DateTimeImmutable + { + return $this->createdAt; + } + + public function setCreatedAt(\DateTimeImmutable $createdAt): self + { + $this->createdAt = $createdAt; + + return $this; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(?string $content): self + { + $this->content = $content; + + return $this; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + + return $this; + } + + public function getLexicon(): ?Lexicon + { + return $this->lexicon; + } + + public function setLexicon(?Lexicon $lexicon): self + { + $this->lexicon = $lexicon; + + return $this; + } + + public function getEntry(): ?Entry + { + return $this->entry; + } + + public function setEntry(?Entry $entry): self + { + $this->entry = $entry; + + return $this; + } + + public function getCategory(): ?string + { + return $this->category; + } + + public function setCategory(string $category): self + { + $this->category = $category; + + return $this; + } +} diff --git a/src/Entity/LoggableTrait.php b/src/Entity/LoggableTrait.php new file mode 100644 index 0000000..cfa4267 --- /dev/null +++ b/src/Entity/LoggableTrait.php @@ -0,0 +1,28 @@ +<?php + + +namespace App\Entity; + + +trait LoggableTrait +{ + public function setCommentsBy($comments, User $user) + { + $log = new Log(); + $log->setCreatedBy($user); + $log->setContent($this->getComments()); + $this->setComments($comments); + + return $this; + } + + public function setDiscussionBy($discussions, User $user) + { + $log = new Log(); + $log->setCreatedBy($user); + $log->setContent($this->getDiscussions()); + $this->setDiscussions($discussions); + + return $this; + } +} \ No newline at end of file diff --git a/src/Entity/Trace.php b/src/Entity/Trace.php new file mode 100644 index 0000000..e866f9d --- /dev/null +++ b/src/Entity/Trace.php @@ -0,0 +1,130 @@ +<?php + +namespace App\Entity; + +use App\Repository\LexiconRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; +use OpenApi\Annotations as OA; + +/** + * @ORM\Entity(repositoryClass=LexiconRepository::class) + * @ORM\HasLifecycleCallbacks + */ +class Trace +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + + /** + * @Groups({"trace:read"}) + * @ORM\Column(type="datetime_immutable") + */ + private $createdAt; + + /** + * @ORM\ManyToOne(targetEntity=User::class, inversedBy="traces") + * @Groups({"trace:read"}) + */ + private $createdBy; + + /** + * @Groups({"trace:read"}) + * @ORM\Column(type="string", nullable=true) + */ + private $origin; + + /** + * @Groups({"trace:read"}) + * @ORM\Column(type="string", nullable=true) + */ + private $action; + + /** + * @ORM\Column(type="json", nullable=true) + * @Groups({"trace:read"}) + * @OA\Property(type="json") + */ + private $additionalInfo = []; + + public function __toString() + { + return $this->getAction() . ' par ' . $this->getCreatedBy() . ' depuis ' . $this->getOrigin() . ' le ' . $this->getCreatedAt()->format('d/m/Y à h:i:s'); + } + + public function __construct() + { + $this->createdAt = new \DateTimeImmutable(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getCreatedAt(): ?\DateTimeImmutable + { + return $this->createdAt; + } + + public function setCreatedAt(\DateTimeImmutable $createdAt): self + { + $this->createdAt = $createdAt; + + return $this; + } + + public function getOrigin(): ?string + { + return $this->origin; + } + + public function setOrigin(?string $origin): self + { + $this->origin = $origin; + + return $this; + } + + public function getAction(): ?string + { + return $this->action; + } + + public function setAction(?string $action): self + { + $this->action = $action; + + return $this; + } + + public function getAdditionalInfo(): ?array + { + return $this->additionalInfo; + } + + public function setAdditionalInfo(?array $additionalInfo): self + { + $this->additionalInfo = $additionalInfo; + + return $this; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index e5c9d03..063a7e8 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -197,6 +197,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface */ private $groupMemberships; + /** + * @ORM\OneToMany(targetEntity=Log::class, mappedBy="createdBy", cascade={"remove"}) + */ + private $logs; + + /** + * @ORM\OneToMany(targetEntity=Trace::class, mappedBy="createdBy", cascade={"remove"}) + */ + private $traces; + public function __toString() { return $this->getPseudo(); @@ -217,6 +227,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface $this->groupMemberships = new ArrayCollection(); $this->labels = new ArrayCollection(); $this->createdGraphyLists = new ArrayCollection(); + $this->logs = new ArrayCollection(); } /** @@ -773,4 +784,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + + /** + * @return Collection<int, Log> + */ + public function getLogs(): Collection + { + return $this->logs; + } + + public function addLog(Log $log): self + { + if (!$this->logs->contains($log)) { + $this->logs[] = $log; + $log->setCreatedBy($this); + } + + return $this; + } + + public function removeLog(Log $log): self + { + if ($this->logs->removeElement($log)) { + // set the owning side to null (unless already changed) + if ($log->getCreatedBy() === $this) { + $log->setCreatedBy(null); + } + } + + return $this; + } } -- GitLab