<?php
/**
* ApiPublicationController.php.
*/
namespace App\Controller\Api;
use App\Entity\Entrance;
use App\Entity\MessageTarget;
use App\Entity\PublicMessage;
use App\Entity\PublicPage;
use App\Entity\Residence;
use App\Entity\ThinClient;
use App\Entity\Video;
use App\Service\FrontService;
use App\Service\ThinClientService;
use App\Utils\BrowserNotificationSender;
use App\Utils\MediaManager;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Persistence\ManagerRegistry;
use FOS\RestBundle\Controller\AbstractFOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\Annotations\Delete;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\View\View;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* Api controller for public pages and public messages management.
*/
#[Route(path: '/api')]
class ApiPublicationController extends AbstractFOSRestController
{
public function __construct(private readonly ManagerRegistry $doctrine)
{
}
/**
* @Rest\View()
* @Rest\Put("/messages")
*/
public function editPublicMessage(Request $request, ThinClientService $thinClientService)
{
$msgRequest = json_decode($request->getContent(), true);
$em = $this->doctrine->getManager();
$publicMessage = $this->doctrine
->getRepository(PublicMessage::class)
->find($msgRequest['message_id']);
if (empty($publicMessage)) {
return \FOS\RestBundle\View\View::create(['message' => 'Message not found'], Response::HTTP_NOT_FOUND);
}
$publicMessage->setSubject($msgRequest['subject']);
$publicMessage->setContent($msgRequest['content']);
$publicMessage->setImportant($msgRequest['important']);
$inComingStartDisplayDate = $msgRequest['start_display_date'];
$publicMessage->setStartDisplayDate(new \DateTime("@$inComingStartDisplayDate"));
if ($msgRequest['stop_display_date']) {
$inComingStopDisplayDate = $msgRequest['stop_display_date'];
$publicMessage->setStopDisplayDate(new \DateTime("@$inComingStopDisplayDate"));
}
// --- réinitialisation des messages targets
foreach ($publicMessage->getMessageTargets() as $mt) {
$entrance = $mt->getEntrance();
$residence = $mt->getResidence();
if (null !== $entrance) {
if (!in_array($entrance->getId(), $msgRequest['message_targets']['entrances'])) {
// --- V2 :
$entrance->refreshUpdateToken();
// --- V3 :
$thinClientService->updateEntranceToken($entrance, 'messages');
$thinClientService->updateEntranceToken($entrance, 'launcher_grid');
$em->persist($entrance);
}
} elseif (null !== $residence) {
if (!in_array($residence->getId(), $msgRequest['message_targets']['residences'])) {
// --- V2 :
$residence->refreshAllUpdateToken();
}
// --- V3 :
$thinClientService->updateResidenceToken($mt->getResidence(), 'messages');
$thinClientService->updateResidenceToken($mt->getResidence(), 'launcher_grid');
$em->persist($residence);
}
$publicMessage->removeMessageTarget($mt);
$em->remove($mt);
}
foreach ($msgRequest['message_targets']['residences'] as $residenceId) {
$residence = $this->doctrine
->getRepository(Residence::class)
->find($residenceId);
$messageTarget = new MessageTarget();
$messageTarget->setPublicMessage($publicMessage);
$messageTarget->setResidence($residence);
$publicMessage->addMessageTarget($messageTarget);
// --- V2 :
$residence->refreshAllUpdateToken();
// --- V3 :
$thinClientService->updateResidenceToken($residence, 'messages');
$thinClientService->updateResidenceToken($residence, 'launcher_grid');
$em->persist($residence);
$em->persist($messageTarget);
}
foreach ($msgRequest['message_targets']['entrances'] as $entranceId) {
$entrance = $this->doctrine
->getRepository(Entrance::class)
->find($entranceId);
$messageTarget = new MessageTarget();
$messageTarget->setPublicMessage($publicMessage);
$messageTarget->setEntrance($entrance);
$publicMessage->addMessageTarget($messageTarget);
// --- V2 :
$entrance->refreshUpdateToken();
// --- V3 :
$thinClientService->updateEntranceToken($entrance, 'messages');
$thinClientService->updateEntranceToken($entrance, 'launcher_grid');
$em->persist($entrance);
$em->persist($messageTarget);
}
$em->persist($publicMessage);
$em->flush();
$response = [];
$response['message'] = $publicMessage;
return new View($response, Response::HTTP_CREATED);
}
/**
* @Get("/publications/{typeProperty}/{propertyId}")
*
* @param $typeProperty string Property type : 'entrance' or 'residence'
* @param $propertyId integer Property identifier
* @throws \Exception
*/
#[IsGranted('ROLE_VIEW_PROPERTIES')]
public function getPublicationsForProperty($typeProperty, $propertyId, MediaManager $mediaManager, CacheManager $imagineCacheManager, LoggerInterface $logger): View
{
try {
if (!in_array($typeProperty, ['entrance', 'residence'])) {
throw new \Exception("Invalid typeProperty (must be 'entrance', 'residence')", 1);
}
$property = $this->doctrine->getRepository('\App\Entity\\'.ucfirst((string) $typeProperty))->find($propertyId);
if (null === $property) {
return new View("This {$typeProperty} does not exist", Response::HTTP_NOT_FOUND);
}
$publicPages = $property->getPublicationsToDisplay('publicPages');
foreach ($publicPages as $page) {
$page->setPreviewUrl($mediaManager->getServerUrl($page->getPreviewName()));
$thumb = $imagineCacheManager->getBrowserPath($page->getPreviewName(), 'page_preview_medium');
$page->setPreviewThumb($thumb);
}
$publicMessages = $property->getPublicationsToDisplay('publicMessages');
$socialAds = $property->getPublicationsToDisplay('socialAds');
return new View([
'publicMessages' => $publicMessages,
'socialAds' => $socialAds,
'publicPages' => $publicPages,
], Response::HTTP_OK);
} catch (\Exception $e) {
$logger->error($e->getMessage(), $e->getTrace());
return new View("Echec : {$e->getMessage()}", Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
/**
* Return the current important publicMessage of the attached entrance.
*
* @Get("/important/message")
*/
#[IsGranted('ROLE_FRONT_ACCESS')]
public function getEntranceImportantMessage(): View
{
try {
if (!$this->getUser()->hasGroup('GROUP_RESIDENT')) {
throw new \Exception('Only residents can access this endpoint', 1);
}
$entrance = $this->getUser()->getApartment()->getEntrance();
$messagePrios = $entrance->getPublicationsToDisplay('publicMessages')->filter(fn(PublicMessage $publicMessage) => $publicMessage->getImportant());
$messagePrio = false;
if ($messagePrios->count() > 0) {
$messagePrio = $messagePrios->first();
$messagePrio->setSender(null); // Pas terrible, mais ça évite de faire tout un micmac de groupes de sérialisation
}
return new View($messagePrio, Response::HTTP_OK);
} catch (\Exception $e) {
return new View('Echec : '.$e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
/**
* Return current user uploaded videos.
*
* @Get("/videos")
*/
#[IsGranted('ROLE_PUBLICPAGE_VIDEO')]
public function getUserVideos(): View
{
$repo = $this->doctrine->getManager()->getRepository(Video::class);
$videos = $repo->findBy(['uploader' => $this->getUser()]);
return new View($videos);
}
/**
* @Post("/delete/{typePublication}/{idPublication}")
*
* @param $typePublication string Publication type : 'publicMessage', 'publicPage' or 'socialad'
* @param $idPublication integer Publication identifier
*/
#[IsGranted('ROLE_FRONT_ACCESS')]
public function deletePublication($typePublication, $idPublication, BrowserNotificationSender $notificationSender, FrontService $frontService, ThinClientService $thinClientService, \Symfony\Contracts\Translation\TranslatorInterface $translator): View
{
try {
if (!in_array($typePublication, ['publicMessage', 'publicPage', 'socialad'])) {
throw new \Exception("Invalid typePublication (must be 'publicMessage' or 'publicPage");
}
if ('socialad' === $typePublication) {
return $frontService->deleteSocialAd($idPublication, $this->getUser());
}
if (!$this->getUser()->hasRole('ROLE_PUBLICPAGE')) {
return new View('Denied access', Response::HTTP_FORBIDDEN);
}
if (empty($idPublication) || ($idPublication != intval($idPublication))) {
throw new \Exception('Veuillez spécifier la publication à supprimer');
}
$targetPublication = $this->doctrine->getRepository('\App\Entity\\'.ucfirst((string) $typePublication))->find($idPublication);
if (null === $targetPublication) {
throw new \Exception('Publication introuvable');
}
if ('publicPage' === $typePublication) {
if ($targetPublication->getCreator() !== $this->getUser() && !$this->getUser()->hasRole('ROLE_PUBLICPAGE')) {
throw new \Exception("Seul l'auteur d'une publication peut la supprimer!", 1);
}
}
if ('publicMessage' === $typePublication) {
if ($targetPublication->getSender() !== $this->getUser() && !$this->getUser()->hasRole('ROLE_PUBLICMESSAGE')) {
throw new \Exception("Seul l'auteur d'une publication peut la supprimer!", 1);
}
}
$em = $this->doctrine->getManager();
// Update-token of message target
if ('publicMessage' == $typePublication) {
$dataType = 'messages';
} elseif ('publicPage' == $typePublication) {
$dataType = 'pages';
}
$targetResidences = new ArrayCollection();
foreach ($targetPublication->getMessageTargets() as $mt) {
$entrance = $mt->getEntrance();
$residence = $mt->getResidence();
if (null !== $entrance) {
$entrance->refreshUpdateToken();
$em->persist($entrance);
// --- V3 :
$thinClientService->updateEntranceToken($entrance, $dataType);
if ('messages' == $dataType) {
$thinClientService->updateEntranceToken($entrance, 'launcher_grid');
}
if (!$targetResidences->contains($mt->getEntrance()->getResidence())) {
$targetResidences->add($mt->getEntrance()->getResidence());
}
} elseif (null !== $residence) {
$residence->refreshAllUpdateToken();
$em->persist($residence);
// --- V3 :
$thinClientService->updateResidenceToken($residence, $dataType);
if ('messages' == $dataType) {
$thinClientService->updateResidenceToken($residence, 'launcher_grid');
}
if (!$targetResidences->contains($mt->getResidence())) {
$targetResidences->add($mt->getResidence());
}
}
}
/* OK --> delete publication and associations */
foreach ($targetPublication->getMessageTargets() as $messageTarget) {
$em->remove($messageTarget);
}
foreach ($targetPublication->getModerationEvents() as $moderationEvent) {
$em->remove($moderationEvent);
}
$em->remove($targetPublication);
$em->flush();
// --- Notification management :
$landlords = new ArrayCollection();
foreach ($targetResidences as $targetResidence) {
foreach ($targetResidence->getAffectedLandlordsByRole('ROLE_MODERATOR') as $landlord) {
$landlords->add($landlord);
}
}
$notificationSender->sendMultipleNotifications(
$landlords,
BrowserNotificationSender::TYPE_PUBLIC_CONTENT_DELETED,
$this->getUser()
);
return new View($translator->trans('publications.deleted'), Response::HTTP_ACCEPTED);
} catch (\Exception $e) {
return new View("Echec : {$e->getMessage()}", Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
/**
* @Delete("videos/delete/{id}")
*
* @param int $id id de l'entité vidéo
*/
#[IsGranted('ROLE_PUBLICPAGE_VIDEO')]
public function deleteVideo($id, MediaManager $mm): View
{
$video = $this->doctrine->getRepository(Video::class)->find($id);
if (null === $video) {
return new View('This video does not exist', Response::HTTP_NOT_FOUND);
}
$em = $this->doctrine->getManager();
$mm->deleteObject($video->getVideoName());
if (Video::STATUS_UPLOADING === $video->getVideoStatus() && null !== $video->getUploadId()) {
$mm->abortMultipartUpload($video->getVideoName(), $video->getUploadId());
} elseif (Video::STATUS_READY !== $video->getVideoStatus()) {
$mm->deleteObject($video->getVideoName());
} else {
$mm->deleteObject($video->getVideoName());
}
$em->remove($video);
$em->flush();
return new View('Video has been deleted', Response::HTTP_OK);
}
/**
* Return publication.
*
* @Get("/get/publication/{id}/{token}")
*
* @param int $id id publication
* @param string $token thinclient token
*/
public function getPublicationById($id, $token, MediaManager $mediaManager)
{
$em = $this->doctrine->getManager();
/** @var Thinclient $thinclient */
$thinclient = $em->getRepository(ThinClient::class)->findOneBySoftwareId($token);
if (null === $thinclient) {
return new View(['Le client n\'existe pas'], Response::HTTP_NOT_FOUND);
}
/** @var PublicPage $publication */
$publication = $em->getRepository(PublicPage::class)->findOneBy(['id' => $id]);
if (null === $publication) {
return new View(['La publication n\'existe pas'], Response::HTTP_NOT_FOUND);
}
return $mediaManager->generateObjectResponse($publication->getPreviewName());
}
}