From de41f47e6f1886de5330e9ed641ffb4ec9ab2ad2 Mon Sep 17 00:00:00 2001 From: Zahdi <ZahdiMohcine20@gmail.com> Date: Mon, 31 Mar 2025 23:39:49 +0200 Subject: [PATCH] [Update] Adding our names in our files --- .gitignore | 1 + archive/Invitation.php | 65 ---------- archive/InvitationRepository.php | 43 ------- archive/InvitationsController.php | 34 ----- config/packages/framework.yaml | 1 + src/Controller/GiftsPurchaseController.php | 68 ++++++++++ src/Controller/InvitationController.php | 16 +++ src/Controller/WishlistController.php | 2 +- src/Entity/Invitation.php | 1 + src/Entity/User.php | 8 ++ src/Entity/Wishlist.php | 3 +- src/Form/InvitationType.php | 2 + src/Form/WishlistType.php | 2 +- src/Repository/InvitationRepository.php | 26 +--- src/Repository/WishlistRepository.php | 27 +--- templates/invitation/index.html.twig | 5 +- templates/invitation/new.html.twig | 1 + templates/purchase_proof/index.html.twig | 1 + templates/purchase_proof/new.html.twig | 1 + templates/wishlist/edit.html.twig | 2 + templates/wishlist/index.html.twig | 140 +++++++++++++-------- templates/wishlist/new.html.twig | 7 +- templates/wishlist/show.html.twig | 1 + 23 files changed, 206 insertions(+), 251 deletions(-) delete mode 100644 archive/Invitation.php delete mode 100644 archive/InvitationRepository.php delete mode 100644 archive/InvitationsController.php create mode 100644 src/Controller/GiftsPurchaseController.php diff --git a/.gitignore b/.gitignore index 65da7121..43c8ba2d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ vendor/ docker/server migrations/ public/uploads +archive/ diff --git a/archive/Invitation.php b/archive/Invitation.php deleted file mode 100644 index dee35197..00000000 --- a/archive/Invitation.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -namespace App\Entity; - -use App\Repository\InvitationRepository; -use Doctrine\ORM\Mapping as ORM; - -#[ORM\Entity(repositoryClass: InvitationRepository::class)] -class Invitation -{ - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; - - #[ORM\Column] - private ?int $userId = null; - - #[ORM\Column] - private ?bool $state = null; - - #[ORM\Column] - private ?int $wishlistId = null; - - public function getId(): ?int - { - return $this->id; - } - - public function getUserId(): ?int - { - return $this->userId; - } - - public function setUserId(int $userId): static - { - $this->userId = $userId; - - return $this; - } - - public function isState(): ?bool - { - return $this->state; - } - - public function setState(bool $state): static - { - $this->state = $state; - - return $this; - } - - public function getWishlistId(): ?int - { - return $this->wishlistId; - } - - public function setWishlistId(int $wishlistId): static - { - $this->wishlistId = $wishlistId; - - return $this; - } -} diff --git a/archive/InvitationRepository.php b/archive/InvitationRepository.php deleted file mode 100644 index 8cea308c..00000000 --- a/archive/InvitationRepository.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -namespace App\Repository; - -use App\Entity\Invitation; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; -use Doctrine\Persistence\ManagerRegistry; - -/** - * @extends ServiceEntityRepository<Invitation> - */ -class InvitationRepository extends ServiceEntityRepository -{ - public function __construct(ManagerRegistry $registry) - { - parent::__construct($registry, Invitation::class); - } - - // /** - // * @return Invitation[] Returns an array of Invitation objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('i') - // ->andWhere('i.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('i.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Invitation - // { - // return $this->createQueryBuilder('i') - // ->andWhere('i.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } -} diff --git a/archive/InvitationsController.php b/archive/InvitationsController.php deleted file mode 100644 index 1b04522e..00000000 --- a/archive/InvitationsController.php +++ /dev/null @@ -1,34 +0,0 @@ - -<?php - - -use App\Repository\InvitationRepository; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Attribute\Route; - - -#[Route('/wishlist')] -class InvitationsController extends AbstractController{ - - #[Route(path:'', name:'')] - - public function createInvitation( Request $request , InvitationRepository $invitationRepository): Response { - $userId = $request->get("userId") ; - $state = $request->get("state") ; - $wishlistId = $request->get("wishlistId") ; - $invitationRepository->createInvitation($userId, $state, $wishlistId); - // return $this->render("") - - - } - - - -} - - - -?> - diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 8f51215d..04c465d4 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,6 +1,7 @@ # see https://symfony.com/doc/current/reference/configuration/framework.html framework: secret: '%env(APP_SECRET)%' + http_method_override: true # Note that the session will be started ONLY if you read or write from it. session: true diff --git a/src/Controller/GiftsPurchaseController.php b/src/Controller/GiftsPurchaseController.php new file mode 100644 index 00000000..dc80bf4d --- /dev/null +++ b/src/Controller/GiftsPurchaseController.php @@ -0,0 +1,68 @@ +<?php + +// Created by Mohcine Zahdi and Othmane Mounouar + + +namespace App\Controller; + +use Doctrine\ORM\EntityManagerInterface; +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\Attribute\Route; + +#[Route('/gifts_purchase')] +final class GiftsPurchaseController extends AbstractController +{ + + + #[Route('/new', name: 'app_gifts_purchase_new', methods: ['GET', 'POST'])] + public function craeteGiftsPurchase(Request $request, EntityManagerInterface $entityManager): Response + { + $user = $this->getUser() ; + $data = json_decode($request->getContent(),true) ; + $wishlist_id = $data["wishlist_id"] ?? null ; + if (!$wishlist_id) { + return new JsonResponse(["error"=>"Missing wishlist_id"] , Response::HTTP_BAD_REQUEST) ; + } + if (!$user) { + return $this->createAccessDeniedException('User is not connected'); + } + return new JsonResponse(["joint_creation_URL"=> GiftsPurchaseController::generateGiftsPurchaseURL($wishlist_id)] , Response::HTTP_CREATED); + + } + + + private function generateGiftsPurchaseURL(int $wishlist_id): string { + $secretKey = 'a_se_iu_çs/*-'; + $hash = hash_hmac('sha256', (string) $wishlist_id, $secretKey); + $token = base64_encode($wishlist_id . '|' . $hash); + $serverIp = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1'; + return sprintf('http://%s:8000/login?gifts_purchase=%s', $serverIp, rtrim(strtr($token, '+/', '-_'), '=')); + } + + + public static function verifyGiftsPurchaseToken(string $token): ?int { + $secretKey = 'a_se_iu_çs/*-'; + $token = strtr($token, '-_', '+/'); + $token = base64_decode($token); + if (!$token) { + return null; + } + $parts = explode('|', $token); + if (count($parts) !== 2) { + return null; + } + [$wishlist_id, $hash] = $parts; + $expectedHash = hash_hmac('sha256', $wishlist_id, $secretKey); + if (!hash_equals($expectedHash, $hash)) { + return null; + } + return (int) $wishlist_id; + } + + + +} + diff --git a/src/Controller/InvitationController.php b/src/Controller/InvitationController.php index df5e06ac..db555037 100644 --- a/src/Controller/InvitationController.php +++ b/src/Controller/InvitationController.php @@ -1,5 +1,7 @@ <?php +// Created by Mohcine Zahdi and Othmane Mounouar + namespace App\Controller; use App\Entity\Invitation; @@ -91,6 +93,20 @@ final class InvitationController extends AbstractController } + #[Route("/reject_invitation/{id}" , name: 'app_reject_invitation' , methods:['POST', 'GET'])] + public function rejectInvitation(Invitation $invitation , EntityManagerInterface $entityManager){ + + $user = $this->getUser(); + if ($user) { + $user->rejectInvitation($invitation->getId()); + $entityManager->flush(); + return $this->redirectToRoute('app_invitation_index', [], Response::HTTP_SEE_OTHER); + }else { + return $this->createAccessDeniedException("Vous ne pouvez pas accèder à cette API sans authentification!") ; + } + } + + diff --git a/src/Controller/WishlistController.php b/src/Controller/WishlistController.php index 288a93b7..57c63e8e 100644 --- a/src/Controller/WishlistController.php +++ b/src/Controller/WishlistController.php @@ -1,5 +1,5 @@ <?php -// Edited by Mohcine Zahdi and Othmane Mounouar +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Controller; use App\Entity\Wishlist; diff --git a/src/Entity/Invitation.php b/src/Entity/Invitation.php index 739d442b..76d7e95d 100644 --- a/src/Entity/Invitation.php +++ b/src/Entity/Invitation.php @@ -1,5 +1,6 @@ <?php +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Entity; use App\Repository\InvitationRepository; diff --git a/src/Entity/User.php b/src/Entity/User.php index a3d2c2ac..c9102c16 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -224,4 +224,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface } + public function rejectInvitation(int $invitation_id) { + for ($i = 0 ; $i < sizeof($this->invitations) ; $i++ ){ + if ($this->invitations[$i]->getId() == $invitation_id) { + unset($this->invitations[$i]) ; + } + } + } + } diff --git a/src/Entity/Wishlist.php b/src/Entity/Wishlist.php index d915309c..c1696910 100644 --- a/src/Entity/Wishlist.php +++ b/src/Entity/Wishlist.php @@ -1,5 +1,5 @@ <?php -// Edited by Mohcine Zahdi and Othmane Mounouar +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Entity; use App\Repository\WishlistRepository; @@ -7,7 +7,6 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -use phpDocumentor\Reflection\Types\Array_; #[ORM\Entity(repositoryClass: WishlistRepository::class)] class Wishlist diff --git a/src/Form/InvitationType.php b/src/Form/InvitationType.php index 88900e31..f07bbfb0 100644 --- a/src/Form/InvitationType.php +++ b/src/Form/InvitationType.php @@ -1,4 +1,6 @@ <?php +// Created by Mohcine Zahdi and Othmane Mounouar + namespace App\Form; diff --git a/src/Form/WishlistType.php b/src/Form/WishlistType.php index 062da4d7..b503ae80 100644 --- a/src/Form/WishlistType.php +++ b/src/Form/WishlistType.php @@ -1,5 +1,5 @@ <?php -// Edited by Mohcine Zahdi +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Form; use App\Entity\Wishlist; diff --git a/src/Repository/InvitationRepository.php b/src/Repository/InvitationRepository.php index 8cea308c..a7625cc6 100644 --- a/src/Repository/InvitationRepository.php +++ b/src/Repository/InvitationRepository.php @@ -1,5 +1,6 @@ <?php +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Repository; use App\Entity\Invitation; @@ -16,28 +17,5 @@ class InvitationRepository extends ServiceEntityRepository parent::__construct($registry, Invitation::class); } - // /** - // * @return Invitation[] Returns an array of Invitation objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('i') - // ->andWhere('i.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('i.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Invitation - // { - // return $this->createQueryBuilder('i') - // ->andWhere('i.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } + } diff --git a/src/Repository/WishlistRepository.php b/src/Repository/WishlistRepository.php index f767cdc6..54550666 100644 --- a/src/Repository/WishlistRepository.php +++ b/src/Repository/WishlistRepository.php @@ -1,5 +1,5 @@ <?php -// Edited by Mohcine Zahdi and Othmane Mounouar +// Created by Mohcine Zahdi and Othmane Mounouar namespace App\Repository; use App\Entity\Wishlist; @@ -98,28 +98,5 @@ class WishlistRepository extends ServiceEntityRepository } return $rankings; } - // /** - // * @return Wishlist[] Returns an array of Wishlist objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('w') - // ->andWhere('w.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('w.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Wishlist - // { - // return $this->createQueryBuilder('w') - // ->andWhere('w.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } + } diff --git a/templates/invitation/index.html.twig b/templates/invitation/index.html.twig index 1172c234..34c5ef5b 100644 --- a/templates/invitation/index.html.twig +++ b/templates/invitation/index.html.twig @@ -1,5 +1,8 @@ +{# Created by Mohcine Zahdi and Othmane Mounouar #} + {% extends 'base.html.twig' %} + {% block title %}Wishlist Invitations{% endblock %} {% block body %} @@ -36,7 +39,7 @@ </div> <div class="wishlist-actions"> <button title="Accept"><span><a href="{{path('app_accept_invitation', {'id': invitation.id})}}" >✔</a></span></button> - <button title="Delete"><span>🗑️</span></button> + <button title="Reject"><span><a href="{{path('app_reject_invitation' , {'id': invitation.id})}}">🗑️</a></span></button> </div> <div class="wishlist-items"> <div class="user-icon" style="width: 50px; height: 50px;"></div> diff --git a/templates/invitation/new.html.twig b/templates/invitation/new.html.twig index 40c305be..dfdf9be6 100644 --- a/templates/invitation/new.html.twig +++ b/templates/invitation/new.html.twig @@ -1,3 +1,4 @@ + {% extends 'base.html.twig' %} {% block title %}New Invitation{% endblock %} diff --git a/templates/purchase_proof/index.html.twig b/templates/purchase_proof/index.html.twig index 78a3a987..c6647403 100644 --- a/templates/purchase_proof/index.html.twig +++ b/templates/purchase_proof/index.html.twig @@ -1,3 +1,4 @@ +{# Created by Firas Bouzazi and Mohammed Oun #} {% extends 'base.html.twig' %} {% block title %}Hello PurchaseProofController!{% endblock %} diff --git a/templates/purchase_proof/new.html.twig b/templates/purchase_proof/new.html.twig index 6e02dd9d..787959aa 100644 --- a/templates/purchase_proof/new.html.twig +++ b/templates/purchase_proof/new.html.twig @@ -1,3 +1,4 @@ +{# Created by Firas Bouzazi and Mohammed Oun #} {% extends 'base.html.twig' %} {% block title %}Add Purchase Proof{% endblock %} diff --git a/templates/wishlist/edit.html.twig b/templates/wishlist/edit.html.twig index 198acf1a..fcef07fe 100644 --- a/templates/wishlist/edit.html.twig +++ b/templates/wishlist/edit.html.twig @@ -1,3 +1,5 @@ +{# Created by Mohcine Zahdi and Othmane Mounouar #} + {% extends 'base.html.twig' %} {% block title %}Edit Wishlist{% endblock %} diff --git a/templates/wishlist/index.html.twig b/templates/wishlist/index.html.twig index 6af50c0a..16c2e975 100644 --- a/templates/wishlist/index.html.twig +++ b/templates/wishlist/index.html.twig @@ -1,14 +1,22 @@ +{# Created by Mohcine Zahdi and Othmane Mounouar #} {% extends 'base.html.twig' %} + + {% block title %} My Wishlists {% endblock %} + + {% block body %} +{% block stylesheets %} + +{{parent()}} <style> -/* Popup box styling */ + .popup-box { - display: none; /* Hidden by default */ - position: fixed; /* Fixed relative to the viewport */ + display: none; + position: fixed; z-index: 1000; background-color: #fff; padding: 15px; @@ -18,7 +26,6 @@ width: 220px; } -/* Close button styling */ .popup-box .close { position: absolute; top: 5px; @@ -27,7 +34,6 @@ cursor: pointer; } -/* Center content inside the popup */ .popup-box .content { text-align: center; margin-top: 20px; @@ -60,8 +66,15 @@ body { } -a { +a { + text-decoration:none ; +} +button > a { + color : #000000; +} + +.add-wishlist-btn > a { color : #EDF3F4 ; } @@ -72,6 +85,8 @@ a:hover{ .wishlist > a { color: #00B8DE; } +{% endblock %} + </style> <header> @@ -98,9 +113,9 @@ a:hover{ <p class="wishlist-footer">{{ wishlist.deadline | date('F j, Y') }}</p> <!-- Share Button --> - <button type="button" class="share-btn" data-wishlist-id="{{ wishlist.id }}">↗</button> + <button title="Share wishlist" type="button" class="share-btn" data-wishlist-id="{{ wishlist.id }}">↗</button> - <button title="Edit title"><a href="{{ path('app_wishlist_edit', { 'id': wishlist.id }) }}">✏</a></button> + <button title="Edit wishlist"><a href="{{ path('app_wishlist_edit', { 'id': wishlist.id }) }}">✏</a></button> <form action="{{ path('app_wishlist_delete', {'id': wishlist.id}) }}" method="POST" style="display: inline-block"> <input type="hidden" name="_method" value="DELETE"> <input type="hidden" name="_token" value="{{ csrf_token('delete' ~ wishlist.id) }}"> @@ -127,42 +142,11 @@ document.addEventListener("DOMContentLoaded", function () { const closeBtn = popupBox.querySelector(".close"); let selectedWishlistId = null; - // When a share button is clicked, position and show the popup. - shareButtons.forEach(button => { - button.addEventListener("click", function () { - selectedWishlistId = this.dataset.wishlistId; - // Reset invite button text and stored URL. - const inviteButton = document.getElementById("invite-co-worker"); - inviteButton.textContent = "Invite a Co-Worker"; - inviteButton.removeAttribute("data-invite-url"); - - const rect = this.getBoundingClientRect(); - // Adjust popup position as needed. - popupBox.style.left = `${rect.left}px`; - popupBox.style.top = `${rect.top + rect.height + 8}px`; - popupBox.style.display = "block"; - }); - }); - - // Close popup when the close button is clicked. - closeBtn.addEventListener("click", function () { - popupBox.style.display = "none"; - }); - - // Optionally close the popup if clicking outside. - window.addEventListener("click", function (event) { - if (!popupBox.contains(event.target) && !event.target.classList.contains("share-btn")) { - popupBox.style.display = "none"; - } - }); - - // Invite button click: first click fetches URL, second click copies it. - document.getElementById("invite-co-worker").addEventListener("click", function () { - const inviteButton = this; - // Check if URL is already generated. - if (inviteButton.hasAttribute("data-invite-url")) { - // URL exists, copy it. - const url = inviteButton.getAttribute("data-invite-url"); + // Helper function to handle the URLs + function handleUrlButton(button, urlPath, dataAttr, buttonCopyText, successAlertText, failureAlertText) { + if (button.hasAttribute(dataAttr)) { + // If the URL exists, copy it. + const url = button.getAttribute(dataAttr); navigator.clipboard.writeText(url) .then(() => { alert("URL was copied to clipboard successfully!"); @@ -173,8 +157,8 @@ document.addEventListener("DOMContentLoaded", function () { alert("Failed to copy URL to clipboard."); }); } else { - // Fetch the invite URL. - fetch("{{ path('app_invitation_new') }}", { + // Fetch the URL otherwise + fetch(urlPath, { method: "POST", headers: { "Content-Type": "application/json" @@ -190,11 +174,11 @@ document.addEventListener("DOMContentLoaded", function () { .then(responseData => { if (responseData.joint_creation_URL) { // Save URL in a data attribute and change button text. - inviteButton.setAttribute("data-invite-url", responseData.joint_creation_URL); - inviteButton.textContent = "Copy Invite URL"; - alert("Invite URL generated. Click the button again to copy it."); + button.setAttribute(dataAttr, responseData.joint_creation_URL); + button.textContent = buttonCopyText; + alert(successAlertText); } else { - alert("Failed to generate invite link."); + alert(failureAlertText); } }) .catch(error => { @@ -202,14 +186,66 @@ document.addEventListener("DOMContentLoaded", function () { alert("Failed to send invitation."); }); } + } + + // Reset buttons state when a share button is clicked. + shareButtons.forEach(button => { + button.addEventListener("click", function () { + selectedWishlistId = this.dataset.wishlistId; + // Reset both buttons. + const inviteButton = document.getElementById("invite-co-worker"); + inviteButton.textContent = "Invite a Co-Worker"; + inviteButton.removeAttribute("data-invite-url"); + + const shareWishesButton = document.getElementById("share-wishes"); + shareWishesButton.textContent = "Share Your Wishes"; + shareWishesButton.removeAttribute("data-share-url"); + + const rect = this.getBoundingClientRect(); + popupBox.style.left = `${rect.left}px`; + popupBox.style.top = `${rect.top + rect.height + 8}px`; + popupBox.style.display = "block"; + }); + }); + + // Close popup when the close button is clicked. + closeBtn.addEventListener("click", function () { + popupBox.style.display = "none"; + }); + + // Close the popup if clicking outside. + window.addEventListener("click", function (event) { + if (!popupBox.contains(event.target) && !event.target.classList.contains("share-btn")) { + popupBox.style.display = "none"; + } }); - // Share Your Wishes action placeholder. + // Event listener for the Invite button. + document.getElementById("invite-co-worker").addEventListener("click", function () { + handleUrlButton( + this, + "{{ path('app_invitation_new') }}", + "data-invite-url", + "Copy Invite URL", + "Invite URL generated. Click the button again to copy it.", + "Failed to generate invite link." + ); + }); + + // Event listener for the Share Your Wishes button. document.getElementById("share-wishes").addEventListener("click", function () { - alert("Sharing your wishes! (Implement your sharing logic here)"); + handleUrlButton( + this, + "{{ path('app_gifts_purchase_new') }}", + "data-share-url", + "Copy Share URL", + "Share URL generated. Click the button again to copy it.", + "Failed to generate share link." + ); }); }); </script> + {% endblock %} diff --git a/templates/wishlist/new.html.twig b/templates/wishlist/new.html.twig index 32d5f9e3..5a5386cc 100644 --- a/templates/wishlist/new.html.twig +++ b/templates/wishlist/new.html.twig @@ -1,4 +1,4 @@ -{# templates/wishlist/new.html.twig #} +{# Created by Mohcine Zahdi and Othmane Mounouar #} {% extends 'base.html.twig' %} {% block title %}Create New Wishlist{% endblock %} @@ -8,8 +8,8 @@ <style> body { font-family: Arial, sans-serif; - background: #EDF3F4 ; - color: white; + background: #EDF3F4 !important ; + color: #00B8DE; text-align: center; padding: 20px; } @@ -92,6 +92,7 @@ width: 90%; } } + </style> {% endblock %} diff --git a/templates/wishlist/show.html.twig b/templates/wishlist/show.html.twig index de9582b2..ee440fcd 100644 --- a/templates/wishlist/show.html.twig +++ b/templates/wishlist/show.html.twig @@ -1,4 +1,5 @@ {% extends 'base.html.twig' %} +{# Created by Mohcine Zahdi and Othmane Mounouar #} {% block title %}{{ wishlist.name }}{% endblock %} -- GitLab