From 347b5997357fc76294cd9115facfa7a90d4ca511 Mon Sep 17 00:00:00 2001
From: pfleu <fleutotp@gmail.com>
Date: Sun, 28 May 2023 23:16:11 +0200
Subject: [PATCH] =?UTF-8?q?Page=20entr=C3=A9e:=20log=20sp=C3=A9cifique=20l?=
 =?UTF-8?q?ors=20de=20la=20modification=20d'une=20entr=C3=A9e.=20Ajout=20d?=
 =?UTF-8?q?'un=20bloc=20"historique"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 config/packages/twig.yaml                  |  1 +
 public/assets/css/app.css                  |  7 ++
 src/Controller/EntryController.php         | 24 +++++++
 src/Entity/Entry.php                       |  1 +
 src/Entity/Log.php                         | 19 ++++++
 src/Manager/LogManager.php                 | 76 ++++++++++++++++++++++
 src/Repository/LogRepository.php           |  9 ++-
 templates/entry/_entryAttributes.html.twig |  4 +-
 templates/entry/_lexiconsTabs.html.twig    |  2 +-
 templates/entry/show.html.twig             | 13 +++-
 templates/log/_formattedLog.html.twig      | 11 ++++
 translations/messages.fr.yaml              |  3 +
 12 files changed, 165 insertions(+), 5 deletions(-)
 create mode 100644 src/Manager/LogManager.php
 create mode 100644 templates/log/_formattedLog.html.twig

diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml
index b2d93a2..d25a41f 100644
--- a/config/packages/twig.yaml
+++ b/config/packages/twig.yaml
@@ -6,6 +6,7 @@ twig:
         label_manager: '@App\Manager\LabelManager'
         lexicon_manager: '@App\Manager\LexiconManager'
         entry_manager: '@App\Manager\EntryManager'
+        log_manager: '@App\Manager\LogManager'
 
 when@test:
     twig:
diff --git a/public/assets/css/app.css b/public/assets/css/app.css
index 021f16e..d4507e8 100644
--- a/public/assets/css/app.css
+++ b/public/assets/css/app.css
@@ -290,4 +290,11 @@ td:hover .fa.text-light-green, p:hover .fa.text-light-green, h4:hover .fa.text-l
 
 .border-auto {
     border: 2px solid rgba(0,0,0, 0.25);
+}
+
+.grey-panel {
+    background-color: darkgrey;
+    border: 2px solid black;
+    font-size: 0.8rem;
+    padding: 20px;
 }
\ No newline at end of file
diff --git a/src/Controller/EntryController.php b/src/Controller/EntryController.php
index 9d49862..99749e5 100644
--- a/src/Controller/EntryController.php
+++ b/src/Controller/EntryController.php
@@ -188,6 +188,7 @@ class EntryController extends AppBaseController
         if ($form->isSubmitted() && $form->isValid()) {
             $propertyAccessor->setValue($attributes, $blockId, $form->getData());
             $entry->setAttributes($attributes);
+            $this->addLog($entry, 'edit', $blockCategory);
             $this->em->flush();
 
             return $this->render('closeModalAndReload.html.twig');
@@ -222,6 +223,7 @@ class EntryController extends AppBaseController
             $parentBlock[$blockCategory][] = $form->getData();
             $propertyAccessor->setValue($attributes, $blockId, $parentBlock);
             $entry->setAttributes($attributes);
+            $this->addLog($entry, 'add', $blockCategory);
             $this->em->flush();
 
             return $this->render('closeModalAndReload.html.twig');
@@ -269,6 +271,7 @@ class EntryController extends AppBaseController
                 ]
             ];
             $entry->setAttributes($attributes);
+            $this->addLog($entry, 'add', Entry::ATTR_PART_OF_SPEECH);
             $this->em->flush();
 
             return $this->render('closeModalAndReload.html.twig');
@@ -287,6 +290,7 @@ class EntryController extends AppBaseController
      */
     public function deleteBlock(Request $request, Entry $entry, $blockId): Response
     {
+        $blockCategory = $request->get('blockCategory');
         $attributes = $entry->getAttributes();
         $propertyAccessor = PropertyAccess::createPropertyAccessor();
         preg_match("/(.*)\[([^]]+)\]$/", $blockId, $matches);
@@ -297,9 +301,29 @@ class EntryController extends AppBaseController
         unset($parentBlock[$childId]);
         $propertyAccessor->setValue($attributes, $parentId, $parentBlock);
         $entry->setAttributes($attributes);
+        $this->addLog($entry, 'delete', $blockCategory);
         $this->em->flush();
 
         return $this->redirectToRoute('app_entry_show', ['id' => $entry->getId()]);
     }
 
+    public function addLog(Entry $entry, $type, $blockCategory)
+    {
+        if (in_array($blockCategory, [
+            Entry::ATTR_DEFINITION,
+            Entry::ATTR_EXAMPLE,
+            Entry::ATTR_PRONUNCIATION,
+            Entry::ATTR_PART_OF_SPEECH,
+        ])) {
+
+            $log = new Log();
+            $log->setCreatedBy($this->getUser());
+            $log->setContent($type);
+            $log->setCategory(Log::CATEGORY_UPDATE_ENTRY);
+            $log->setBlockCategory($blockCategory);
+
+            $entry->addLog($log);
+        }
+    }
+
 }
diff --git a/src/Entity/Entry.php b/src/Entity/Entry.php
index 5442bfa..ab772bc 100644
--- a/src/Entity/Entry.php
+++ b/src/Entity/Entry.php
@@ -25,6 +25,7 @@ class Entry
     const ATTR_DEFINITION = 'Definitions';
     const ATTR_COMMENT = 'Comments';
     const ATTR_EXAMPLE = 'Examples';
+    const ATTR_PART_OF_SPEECH = 'PartOfSpeech';
 
     const PART_OF_SPEECH_LIST = [
         "N"      => "N",
diff --git a/src/Entity/Log.php b/src/Entity/Log.php
index 6ca7020..49d1015 100644
--- a/src/Entity/Log.php
+++ b/src/Entity/Log.php
@@ -16,6 +16,7 @@ class Log
 {
     const CATEGORY_UPDATE_COMMENT    = 'update_comment';
     const CATEGORY_UPDATE_DISCUSSION = 'update_discussion';
+    const CATEGORY_UPDATE_ENTRY      = 'update_entry';
 
     const LOG_LIST_CATEGORIES = [
         self::CATEGORY_UPDATE_COMMENT, self::CATEGORY_UPDATE_DISCUSSION
@@ -46,6 +47,12 @@ class Log
      */
     private $category;
 
+    /**
+     * @ORM\Column(type="string", length=80)
+     * @Groups({"log:read"})
+     */
+    private $blockCategory;
+
     /**
      * @Groups({"log:read"})
      * @ORM\Column(type="text", nullable=true)
@@ -153,4 +160,16 @@ class Log
 
         return $this;
     }
+
+    public function getBlockCategory(): ?string
+    {
+        return $this->blockCategory;
+    }
+
+    public function setBlockCategory(string $blockCategory): self
+    {
+        $this->blockCategory = $blockCategory;
+
+        return $this;
+    }
 }
diff --git a/src/Manager/LogManager.php b/src/Manager/LogManager.php
new file mode 100644
index 0000000..a04750d
--- /dev/null
+++ b/src/Manager/LogManager.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Manager;
+
+use App\Entity\Entry;
+use App\Entity\Group;
+use App\Entity\Headword;
+use App\Entity\Label;
+use App\Entity\LabelVisibility;
+use App\Entity\Lexicon;
+use App\Entity\Log;
+use App\Entity\User;
+use App\Languages\LanguagesIso;
+use Doctrine\Persistence\ManagerRegistry;
+use JsonSchema\Validator;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Annotation\Route;
+use Symfony\Component\Serializer\SerializerAwareInterface;
+use Symfony\Component\Serializer\SerializerInterface;
+
+class LogManager
+{
+    /**
+     * @var ManagerRegistry
+     */
+    protected $doctrine;
+
+    public function __construct(ManagerRegistry $doctrine)
+    {
+        $this->doctrine = $doctrine;
+    }
+
+
+    // Retourne les 5 derniers logs visibles par un user classés par date de création DESC
+    public function getLastLogsForEntry(Entry $entry, User $user)
+    {
+        return $this->doctrine->getRepository(Log::class)->search([
+            'user' => $user,
+            'entry' => $entry,
+            'limit' => 5,
+        ]);
+    }
+
+    // Retourne les logs visibles dans un lexique pour un user (pour une catégorie si renseignée)
+    public function getVisibleLabelsInLexicon(Lexicon $lexicon, User $user, $category = null)
+    {
+        if ($lexicon->isZero()) {
+            return $this->doctrine->getRepository(Label::class)->queryVisiblesByUser($user, $category)->getQuery()->getresult();
+        }
+        return $this->doctrine->getRepository(Label::class)->getVisiblesByUserInLexicon($user, $lexicon, $category);
+    }
+
+    // Retourne les labels de l'entrée visibles pour un user (en fonction de leur visibilité dans le lexique et des permissions si non public) (pour une catégorie si renseignée)
+    public function getEntryVisibleLabels(Entry $entry, User $user, $category = null)
+    {
+        $labelsVisiblesInLexicon = $this->getVisibleLabelsInLexicon($entry->getLexicon(), $user, $category);
+
+        return array_intersect($entry->getHeadword()->getLabelsForCategory($category), $labelsVisiblesInLexicon);
+    }
+
+    public function filter($filter)
+    {
+        if ($this->searchString) {
+            $filter['searchString'] = $this->searchString;
+        }
+        return $this->doctrine->getRepository(Label::class)->filter($filter);
+    }
+
+    // Retourne le nb total de mot-vedettes portant le label
+    // Si $user est renseigné, Retourne le nb total de mot-vedettes portant le label ayant au moins une entrée dans un lexique de l'utilisateur
+    public function getHeadwordsNbWithLabel(Label $label, User $user = null)
+    {
+        return $this->doctrine->getRepository(Headword::class)->countHavingLabel($label, $user);
+    }
+}
diff --git a/src/Repository/LogRepository.php b/src/Repository/LogRepository.php
index 9b2b564..cfcb183 100644
--- a/src/Repository/LogRepository.php
+++ b/src/Repository/LogRepository.php
@@ -23,7 +23,8 @@ class LogRepository extends ServiceEntityRepository
 
     public function search($filter)
     {
-        $qb = $this->createQueryBuilder('l');
+        $qb = $this->createQueryBuilder('l')
+        ->orderBy('l.createdAt', 'DESC');
 
         if ($filter['date'] ?? null) {
             $qb->andWhere('DATE(l.createdAt) = DATE(:date)')
@@ -55,6 +56,12 @@ class LogRepository extends ServiceEntityRepository
             ;
         }
 
+        if ($filter['limit'] ?? null) {
+            $qb->setMaxResults($filter['limit'])
+            ;
+        }
+
+
         return $qb->getQuery()->getResult();
 
     }
diff --git a/templates/entry/_entryAttributes.html.twig b/templates/entry/_entryAttributes.html.twig
index c10b388..34af44a 100644
--- a/templates/entry/_entryAttributes.html.twig
+++ b/templates/entry/_entryAttributes.html.twig
@@ -35,7 +35,7 @@
         <h4>
             {{ item.PartOfSpeech|trans }}
             {{ _self.actions(entry, itemKey~'[Sense]', ['add'], "Ajouter une définition à cette nature", 'Definitions') }}
-            {{ _self.actions(entry, itemKey, ['delete']) }}
+            {{ _self.actions(entry, itemKey, ['delete'], '', 'Definitions') }}
         </h4>
 
         <div class="ms-1 ps-4 left-border-blue bg-light-grey">
@@ -108,7 +108,7 @@
         {% endif %}
 
         {% if 'delete' in operations %}
-            <a href="#" data-href="{{ path('app_entry_delete_block', {id: entry.id, blockId: key}) }}"
+            <a href="#" data-href="{{ path('app_entry_delete_block', {id: entry.id, blockId: key, blockCategory: category}) }}"
                data-confirm="{{ "Confirmer la suppression ?"|trans }}" data-bs-toggle="modal" data-bs-target="#confirm-dialog"><i class="fa fa-trash fa-fixed-sized text-grey" title="{{ key }}"></i></a>
         {% endif %}
 
diff --git a/templates/entry/_lexiconsTabs.html.twig b/templates/entry/_lexiconsTabs.html.twig
index e83979d..ebefbdf 100644
--- a/templates/entry/_lexiconsTabs.html.twig
+++ b/templates/entry/_lexiconsTabs.html.twig
@@ -1,4 +1,4 @@
-<ul class="nav nav-tabs mt-3">
+<ul class="nav nav-tabs mt-3 justify-content-end">
     {% for lexicon in app.user.myLexicons %}
         {% set entryWithSameHeadwordInThisLexicon = lexicon.getEntryForHeadword(entry.headword) %}
 
diff --git a/templates/entry/show.html.twig b/templates/entry/show.html.twig
index 0cbc9ac..cb5ed87 100644
--- a/templates/entry/show.html.twig
+++ b/templates/entry/show.html.twig
@@ -7,7 +7,8 @@
 {% block body %}
 
     <div class="row justify-content-center m-lg-5 m-sm-3">
-        <div class="col-md-12">
+
+        <div class="col">
 
             {% if entry.lexicon.newWords %}
                 <h1>
@@ -53,6 +54,16 @@
             {% endif %}
 
         </div>
+
+        <div class="col-3 align-self-end">
+            <div id="history" class="grey-panel">
+                <h5>{{ "Historique des modifications"|trans }}</h5>
+                {% for log in log_manager.lastLogsForEntry(entry, app.user) %}
+                    <p>{% include "log/_formattedLog.html.twig" %}</p>
+                {% endfor %}
+            </div>
+        </div>
+
     </div>
 
 {% endblock %}
diff --git a/templates/log/_formattedLog.html.twig b/templates/log/_formattedLog.html.twig
new file mode 100644
index 0000000..e1369a5
--- /dev/null
+++ b/templates/log/_formattedLog.html.twig
@@ -0,0 +1,11 @@
+{% if log.category == constant('App\\Entity\\Log::CATEGORY_UPDATE_ENTRY') %}
+
+    {{ log.createdAt|date('d/m/Y') }} :
+    {{ log.createdBy }}
+    {% if log.content == 'add' %}{{ 'a ajouté'|trans }}{% endif %}
+    {% if log.content == 'edit' %}{{ 'a modifié'|trans }}{% endif %}
+    {% if log.content == 'add' %}{{ 'a supprimé'|trans }}{% endif %}
+    {{ 'un bloc de type'|trans }}
+    {{ log.blockCategory }}
+
+{% endif %}
\ No newline at end of file
diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml
index 5aa9135..d690e39 100644
--- a/translations/messages.fr.yaml
+++ b/translations/messages.fr.yaml
@@ -1,7 +1,9 @@
 "Your password reset request": "Réinitialisation de votre mot de passe"
+
 "personal" : "Personnel"
 "group" : "Groupe"
 "public" : "Public"
+
 "N": "Nom"
 "V": "Verbe"
 "Interj": "Interjection"
@@ -14,6 +16,7 @@
 "general": "général"
 "institutional": "institutionnel"
 "milestone": "échéance"
+
 "Definitions": "Définition"
 "Pronunciations": "Prononciation"
 "Examples": "Exemple"
-- 
GitLab