From 9e047039d92190f43d210ea3b6081fef141114b9 Mon Sep 17 00:00:00 2001
From: pfleu <fleutotp@gmail.com>
Date: Tue, 30 May 2023 18:22:28 +0200
Subject: [PATCH] MErge Labels

---
 src/Controller/ApiLabelController.php  |  8 ++--
 src/Controller/LabelController.php     | 51 ++++++++++++++++++++------
 src/Entity/Label.php                   |  7 +++-
 src/Form/LabelType.php                 |  3 ++
 src/Manager/LabelManager.php           | 46 +++++++++++++++++++++++
 src/Repository/LabelRepository.php     |  8 ++++
 templates/entry/_chooseLabel.html.twig |  2 +
 templates/label/edit.html.twig         | 38 +++++++++++++++++++
 8 files changed, 146 insertions(+), 17 deletions(-)
 create mode 100644 templates/label/edit.html.twig

diff --git a/src/Controller/ApiLabelController.php b/src/Controller/ApiLabelController.php
index a065ce3..41edfdf 100644
--- a/src/Controller/ApiLabelController.php
+++ b/src/Controller/ApiLabelController.php
@@ -9,6 +9,7 @@ use App\Entity\Headword;
 use App\Entity\Label;
 use App\Entity\Lexicon;
 use App\Entity\User;
+use App\Manager\LabelManager;
 use App\Manager\WiktionaryManager;
 use App\Repository\LabelRepository;
 use Doctrine\Persistence\ManagerRegistry;
@@ -399,16 +400,13 @@ class ApiLabelController extends AppBaseController
      * @OA\Tag(name="Labels")
      * @Security(name="OAuth2")
      */
-    public function merge(Request $request, WiktionaryManager$wiktionaryManager, Label $labelDeleted, Label $labelMerged): Response
+    public function merge(Request $request, WiktionaryManager$wiktionaryManager, Label $labelDeleted, Label $labelMerged, LabelManager $labelManager): Response
     {
         $diffs = $labelDeleted->compareWith($labelMerged);
         if ($diffs) {
             return $this->createJsonResponse(401, ['error' => sprintf("Fusion impossible : %s", implode(', ', $diffs))]);
         } else {
-            foreach ($labelDeleted->getHeadwords() as $headword) {
-                $headword->addLabel($labelMerged);
-            }
-            $this->doctrine->getManager()->remove($labelDeleted);
+            $labelManager->mergeLabels($labelDeleted, $labelMerged);
             $labelDeletedId = $labelDeleted->getId();
             $this->doctrine->getManager()->flush();
             $this->addSuccessMessage(sprintf("Fusion effectuée: Label %s appliqué aux mots du label %s. Label %s supprimé.", $labelDeletedId, $labelMerged->getId(), $labelDeletedId));
diff --git a/src/Controller/LabelController.php b/src/Controller/LabelController.php
index 24de601..3c5dd00 100644
--- a/src/Controller/LabelController.php
+++ b/src/Controller/LabelController.php
@@ -54,7 +54,7 @@ class LabelController extends AppBaseController
      *      requirements={"category"=App\Entity\Label::REQUIREMENTS_LABEL_CATEGORY, "masterType"=App\Entity\Label::REQUIREMENTS_MASTERS_TYPES})
      * @Security("is_granted('ROLE_TEACHER') or category != constant('App\\Entity\\Label::LABEL_CATEGORY_INSTITUTIONAL')")
      */
-    public function new(ManagerRegistry $doctrine, Request $request, $masterType, $category, LabelRepository $labelRepository): Response
+    public function new(ManagerRegistry $doctrine, Request $request, $masterType, $category, LabelRepository $labelRepository, LabelManager $labelManager): Response
     {
         $label = new Label($category);
         $label->setMaster($masterType);
@@ -69,6 +69,7 @@ class LabelController extends AppBaseController
             }
             $label->setGroup($group);
         }
+
         $form = $this->createForm(LabelType::class, $label, [
             'action' => $this->generateUrl('app_label_new', [
                 'masterType' => $masterType,
@@ -78,10 +79,17 @@ class LabelController extends AppBaseController
         ]);
         $form->handleRequest($request);
 
+        $identicalLabel = $labelManager->getIdenticalLabel($label);
+
         if ($form->isSubmitted() && $form->isValid()) {
-            $labelRepository->add($label, true);
+            if ($identicalLabel) {
+                $this->addFlash('warning', sprintf("Création impossible. Un label identique existe déjà."));
+            } else {
+                $this->em->flush();
+                $this->addFlash('success', "Le label a été ajouté");
 
-            return $this->render('closeModalAndReload.html.twig');
+                return $this->render('closeModalAndReload.html.twig');
+            }
         }
 
         return $this->render('genericModalForm.html.twig', [
@@ -214,22 +222,45 @@ class LabelController extends AppBaseController
      * @Route("/{id}/edit", name="app_label_edit", methods={"GET", "POST"})
      */
 //     * @IsGranted("LABEL_EDIT", subject="label")
-    public function edit(Request $request, Label $label, LabelRepository $labelRepository): Response
+    public function edit(Request $request, Label $label, LabelManager $labelManager): Response
     {
         $form = $this->createForm(LabelType::class, $label, [
             'action' => $this->generateUrl('app_label_edit', ['id' => $label->getId()])
         ]);
         $form->handleRequest($request);
 
+        $identicalLabel = $labelManager->getIdenticalLabel($label);
+
         if ($form->isSubmitted() && $form->isValid()) {
-            $labelRepository->add($label, true);
 
-            return $this->render('closeModalAndReload.html.twig');
+            if ($identicalLabel) {
+                // Si on a un identique et qu'on a forcé la validation, on fusionne notre label dans celui existant, $identicalLabel sera apposé sur les mots-vedettes qui possédaient $label
+                if ($form->get('forceValidation')->isClicked()) {
+                    $labelManager->mergeLabels($identicalLabel, $label);
+                    $this->em->flush();
+                    $this->addFlash('success', "Le label a été modifié et fusionné avec un label identique");
+
+                    return $this->render('closeModalAndReload.html.twig');
+
+                // Si on a un identique et qu'on a pas forcé la validation, on refuse de modifier le label et on affiche une demande de confirmation
+                } else {
+                    $displayForceValidation = true;
+                    $this->addFlash('warning', sprintf("Un label identique existe déjà. Voulez-vous fusionner votre label avec le label existant ?"));
+                }
+
+            // Si pas de label identique on modifie le label
+            } else {
+                $this->em->flush();
+                $this->addFlash('success', "Le label a été modifié");
+
+                return $this->render('closeModalAndReload.html.twig');
+            }
         }
 
-        return $this->render('genericModalForm.html.twig', [
-            'title' => "Modifier le label",
+        return $this->render('label/edit.html.twig', [
+            'label' => $label,
             'form' => $form->createView(),
+            'displayForceValidation' => $displayForceValidation ?? false,
         ]);
     }
 
@@ -264,8 +295,6 @@ class LabelController extends AppBaseController
 //     * @IsGranted("LABEL_EDIT", subject="label")
     public function addHeadwordDescription(WiktionaryManager $wiktionaryManager, Request $request, Label $label): Response
     {
-        $displayForceValidation = false;
-
         $form = $this->createForm(EnterWordType::class, null, [
             'action' => $this->generateUrl('app_label_add_headword', ['id' => $label->getId()])
         ]);
@@ -308,7 +337,7 @@ class LabelController extends AppBaseController
         return $this->render('label/addHeadwordToLabel.html.twig', [
             'title' => "Modifier le label",
             'form' => $form->createView(),
-            'displayForceValidation' => $displayForceValidation,
+            'displayForceValidation' => $displayForceValidation ?? false,
         ]);
     }
 
diff --git a/src/Entity/Label.php b/src/Entity/Label.php
index cadacbc..29de089 100644
--- a/src/Entity/Label.php
+++ b/src/Entity/Label.php
@@ -145,7 +145,7 @@ class Label
     private $updateRequests;
 
     /**
-     * @ORM\OneToMany(targetEntity=LabelVisibility::class, mappedBy="label")
+     * @ORM\OneToMany(targetEntity=LabelVisibility::class, mappedBy="label", cascade={"persist", "remove"})
      */
     private $labelVisibilities;
 
@@ -172,6 +172,11 @@ class Label
 //        throw new \LogicException("Cannot determine label masters type");
 //    }
 
+    public function isIdenticalTo(Label $label)
+    {
+        return empty($this->compareWith($label));
+    }
+
 // TODO reprendre ??
     public function compareWith(Label $label)
     {
diff --git a/src/Form/LabelType.php b/src/Form/LabelType.php
index bb539c0..f1ed381 100644
--- a/src/Form/LabelType.php
+++ b/src/Form/LabelType.php
@@ -61,6 +61,9 @@ class LabelType extends AbstractType
         }
 
         $builder
+            ->add('forceValidation', SubmitType::class, [
+                'label' => 'Confirmer',
+            ])
             ->add('submit', SubmitType::class, [
                 'label' => 'Enregistrer',
             ]);
diff --git a/src/Manager/LabelManager.php b/src/Manager/LabelManager.php
index a07067b..4950a88 100644
--- a/src/Manager/LabelManager.php
+++ b/src/Manager/LabelManager.php
@@ -37,6 +37,52 @@ class LabelManager
         $this->searchString = $searchString;
     }
 
+    public function mergeLabels(Label $labelDeleted, Label $labelMerged)
+    {
+        foreach ($labelDeleted->getHeadwords() as $headword) {
+            $headword->addLabel($labelMerged);
+        }
+        // pour chaque visibilité liée à labelDeleted, on regarde si on a une visibilité pour le même lexique dans labelMerged
+        // Si ce n'est pas le cas, on en ajoute une
+        foreach ($labelDeleted->getLabelVisibilities() as $labelVisibilityDeleted) {
+            $labelMergedHasSameLexiconVisibility = false;
+            foreach ($labelMerged->getLabelVisibilities() as $labelVisibilityMerged) {
+                if ($labelVisibilityDeleted->getLexicon() == $labelVisibilityMerged->getLexicon()) {
+                    $labelMergedHasSameLexiconVisibility = true; break;
+                }
+            }
+            if (false === $labelMergedHasSameLexiconVisibility) {
+                $labelVisibility = new LabelVisibility();
+                $labelVisibility->setLexicon($labelVisibilityDeleted->getLexicon());
+                $labelVisibility->setLabel($labelMerged);
+                $labelVisibility->setUser($labelVisibilityDeleted->getUser());
+                $this->doctrine->getManager()->persist($labelVisibility);
+            }
+        }
+        $this->doctrine->getManager()->remove($labelDeleted);
+    }
+
+    /**
+     * @param Label $label
+     * @return Label|null
+     */
+    public function getIdenticalLabel(Label $label)
+    {
+        $identicalLabels = $this->doctrine->getRepository(Label::class)->filter([
+            'user' => $label->getUser(),
+            'group' => $label->getGroup(),
+            'category' => $label->getCategory(),
+            'milestone' => $label->getMilestone(),
+            'name' => $label->getName(),
+            'graphyList' => $label->getGraphyList(),
+        ]);
+
+        $identicalLabel = $identicalLabels ? $identicalLabels[0] : null;
+
+        // On s'assure que le label identique n'est le label passé en paramètre (ça peut être le cas en edit si on a pas encore modifié label)
+        return $identicalLabel && $identicalLabel->getId() !== $label->getId() ? $identicalLabel : null;
+    }
+
     // vrai si le label est visible pour user dans un lexique donné
     public function isVisible(Label $label, Lexicon $lexicon, User $user)
     {
diff --git a/src/Repository/LabelRepository.php b/src/Repository/LabelRepository.php
index bb39428..77b167d 100644
--- a/src/Repository/LabelRepository.php
+++ b/src/Repository/LabelRepository.php
@@ -67,6 +67,14 @@ class LabelRepository extends ServiceEntityRepository
             $qb ->andWhere('l.user = :user')
                 ->setParameter('user', $filter['user']);
         }
+        if (!empty($filter['graphyList'])) {
+            $qb ->andWhere('l.graphyList = :graphyList')
+                ->setParameter('graphyList', $filter['graphyList']);
+        }
+        if (!empty($filter['name'])) {
+            $qb ->andWhere('UPPER(l.name) = UPPER(:name)')
+                ->setParameter('name', $filter['name']);
+        }
 
         if (!empty($filter['searchString'])) {
             $qb ->andWhere('l.name LIKE :searchString')
diff --git a/templates/entry/_chooseLabel.html.twig b/templates/entry/_chooseLabel.html.twig
index eaeaef2..e8a3a52 100644
--- a/templates/entry/_chooseLabel.html.twig
+++ b/templates/entry/_chooseLabel.html.twig
@@ -14,6 +14,8 @@
                     <a class="ajax-link" href="#" data-method="DELETE" data-url="{{ path('label_remove', {id: label.id}) }}" data-json="{{ {headwords_ids: [entry.headword.id]}|json_encode }}">
                         <i class="fa fa-lg fa-times-circle text-danger" style="margin-left: -8px; margin-right: 5px;"></i>
                     </a>
+                {% else %}
+                    <i>{{ "Aucun label visible dans ce lexique pour cette catégorie"|trans }}</i>
                 {% endfor %}
 
             </div>
diff --git a/templates/label/edit.html.twig b/templates/label/edit.html.twig
new file mode 100644
index 0000000..fba2363
--- /dev/null
+++ b/templates/label/edit.html.twig
@@ -0,0 +1,38 @@
+{% extends 'modal.html.twig' %}
+
+{% block modal_title %}
+    {% if label.id %}
+        {{ "Modifier un label"|trans }}
+    {% else %}
+        {{ "Ajouter un label"|trans }}
+    {% endif %}
+{% endblock %}
+
+{% block modal_body %}
+    <div class="row">
+        <div class="col-md-12">
+
+            {% include "flashes.html.twig" %}
+
+            {{ form_start(form) }}
+            {{ form_errors(form) }}
+            {% for child in form %}
+                {% if child.vars.name not in ['submit', 'forceValidation'] %}
+                    {{ form_row(child) }}
+                {% endif %}
+            {% endfor %}
+            <div class="row pt-3">
+                <div class="col-sm-4"></div>
+                <div class="col-sm-8">
+                    {% if displayForceValidation %}
+                        {{ form_widget(form.forceValidation) }}
+                    {% else %}
+                        {{ form_widget(form.submit) }}
+                    {% endif %}
+                    <button type="button" class="btn btn-light" data-bs-dismiss="modal">{{ 'Annuler'|trans }}</button>
+                </div>
+            </div>
+            {{ form_end(form, {render_rest: false}) }}
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
-- 
GitLab