2024-07-18 13:40:49 +02:00
< ? php
namespace App\Controller ;
2024-08-03 17:55:39 +02:00
use App\Entity\Domain ;
2024-08-01 14:37:23 +02:00
use App\Entity\DomainEntity ;
use App\Entity\DomainEvent ;
2024-07-18 13:40:49 +02:00
use App\Entity\User ;
use App\Entity\WatchList ;
2024-08-01 14:37:23 +02:00
use App\Repository\WatchListRepository ;
2024-07-18 13:40:49 +02:00
use Doctrine\Common\Collections\Collection ;
use Doctrine\ORM\EntityManagerInterface ;
2024-08-01 14:37:23 +02:00
use Eluceo\iCal\Domain\Entity\Attendee ;
use Eluceo\iCal\Domain\Entity\Calendar ;
use Eluceo\iCal\Domain\Entity\Event ;
2024-08-03 17:55:39 +02:00
use Eluceo\iCal\Domain\Enum\EventStatus ;
2024-08-01 14:37:23 +02:00
use Eluceo\iCal\Domain\ValueObject\Category ;
use Eluceo\iCal\Domain\ValueObject\Date ;
use Eluceo\iCal\Domain\ValueObject\EmailAddress ;
use Eluceo\iCal\Domain\ValueObject\SingleDay ;
2024-08-03 17:55:39 +02:00
use Eluceo\iCal\Domain\ValueObject\Timestamp ;
2024-08-03 18:05:05 +02:00
use Eluceo\iCal\Presentation\Component\Property ;
use Eluceo\iCal\Presentation\Component\Property\Value\TextValue ;
2024-08-01 14:37:23 +02:00
use Eluceo\iCal\Presentation\Factory\CalendarFactory ;
2024-08-04 14:45:27 +02:00
use Psr\Log\LoggerInterface ;
2024-08-01 14:37:23 +02:00
use Sabre\VObject\EofException ;
use Sabre\VObject\InvalidDataException ;
use Sabre\VObject\ParseException ;
use Sabre\VObject\Reader ;
2024-07-18 13:40:49 +02:00
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ;
use Symfony\Component\HttpFoundation\Request ;
2024-08-01 14:37:23 +02:00
use Symfony\Component\HttpFoundation\Response ;
2024-08-04 22:00:20 +02:00
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException ;
2024-07-18 13:40:49 +02:00
use Symfony\Component\Routing\Attribute\Route ;
use Symfony\Component\Serializer\SerializerInterface ;
class WatchListController extends AbstractController
{
public function __construct (
2024-08-02 23:24:52 +02:00
private readonly SerializerInterface $serializer ,
2024-08-01 14:37:23 +02:00
private readonly EntityManagerInterface $em ,
2024-08-04 14:45:27 +02:00
private readonly WatchListRepository $watchListRepository ,
private readonly LoggerInterface $logger
2024-08-02 23:24:52 +02:00
) {
2024-07-18 13:40:49 +02:00
}
2024-08-14 23:23:32 +02:00
public function verifyLimitations ( WatchList $watchList )
2024-07-18 13:40:49 +02:00
{
2024-08-03 00:06:38 +02:00
/** @var User $user */
$user = $this -> getUser ();
$watchList -> setUser ( $user );
2024-07-29 16:01:32 +02:00
2024-08-04 22:00:20 +02:00
/*
* In the limited version , we do not want a user to be able to register the same domain more than once in their watchlists .
* This policy guarantees the equal probability of obtaining a domain name if it is requested by several users .
*/
if ( $this -> getParameter ( 'limited_features' )) {
2024-08-07 14:31:44 +02:00
if ( $watchList -> getDomains () -> count () >= ( int ) $this -> getParameter ( 'limit_max_watchlist_domains' )) {
$this -> logger -> notice ( 'User {username} tried to create a Watchlist. However, the maximum number of domains has been reached for this Watchlist' , [
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of domain names allowed in this Watchlist' );
}
$userWatchLists = $user -> getWatchLists ();
if ( $userWatchLists -> count () >= ( int ) $this -> getParameter ( 'limit_max_watchlist' )) {
$this -> logger -> notice ( 'User {username} tried to create a Watchlist. However, the maximum number of Watchlists has been reached.' , [
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of Watchlists allowed' );
}
2024-08-04 22:00:20 +02:00
/** @var Domain[] $trackedDomains */
2024-08-07 14:31:44 +02:00
$trackedDomains = $userWatchLists -> reduce ( fn ( array $acc , WatchList $watchList ) => [ ... $acc , ... $watchList -> getDomains () -> toArray ()], []);
2024-08-04 22:00:20 +02:00
/** @var Domain $domain */
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
if ( in_array ( $domain , $trackedDomains )) {
$this -> logger -> notice ( 'User {username} tried to create a watchlist with domain name {ldhName}. However, it is forbidden to register the same domain name twice with limited mode.' , [
'username' => $user -> getUserIdentifier (),
'ldhName' => $domain -> getLdhName (),
]);
throw new AccessDeniedHttpException ( 'It is forbidden to register the same domain name twice in your watchlists with limited mode.' );
}
}
}
2024-08-14 23:23:32 +02:00
}
/**
* @ throws \Exception
*/
#[Route(
path : '/api/watchlists' ,
name : 'watchlist_create' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'create' ,
],
methods : [ 'POST' ]
)]
public function createWatchList ( Request $request ) : WatchList
{
$watchList = $this -> serializer -> deserialize ( $request -> getContent (), WatchList :: class , 'json' , [ 'groups' => 'watchlist:create' ]);
$this -> verifyLimitations ( $watchList );
$user = $this -> getUser ();
$this -> logger -> info ( 'User {username} registers a Watchlist ({token}).' , [
'username' => $user -> getUserIdentifier (),
'token' => $watchList -> getToken (),
]);
$this -> em -> persist ( $watchList );
$this -> em -> flush ();
return $watchList ;
}
2024-08-04 22:00:20 +02:00
2024-08-14 23:23:32 +02:00
#[Route(
path : '/api/watchlists/{token}' ,
name : 'watchlist_update' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'update' ,
],
methods : [ 'PATCH' ]
)]
public function patchWatchList ( Request $request ) : WatchList
{
$watchList = $this -> serializer -> deserialize ( $request -> getContent (), WatchList :: class , 'json' , [ 'groups' => 'watchlist:create' ]);
$this -> verifyLimitations ( $watchList );
$user = $this -> getUser ();
$this -> logger -> info ( 'User {username} updates a Watchlist ({token}).' , [
2024-08-04 14:45:27 +02:00
'username' => $user -> getUserIdentifier (),
2024-08-04 22:00:20 +02:00
'token' => $watchList -> getToken (),
2024-08-04 14:45:27 +02:00
]);
2024-07-18 13:40:49 +02:00
$this -> em -> persist ( $watchList );
$this -> em -> flush ();
return $watchList ;
}
2024-08-01 14:37:23 +02:00
/**
* @ throws ParseException
* @ throws EofException
* @ throws InvalidDataException
2024-08-02 23:24:52 +02:00
* @ throws \Exception
2024-08-01 14:37:23 +02:00
*/
#[Route(
path : '/api/watchlists/{token}/calendar' ,
name : 'watchlist_calendar' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'calendar' ,
]
)]
public function getWatchlistCalendar ( string $token ) : Response
{
2024-08-02 01:06:49 +02:00
/** @var WatchList $watchList */
2024-08-02 23:24:52 +02:00
$watchList = $this -> watchListRepository -> findOneBy ([ 'token' => $token ]);
2024-08-01 14:37:23 +02:00
$calendar = new Calendar ();
2024-08-03 17:55:39 +02:00
/** @var Domain $domain */
2024-08-01 14:37:23 +02:00
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
$attendees = [];
/** @var DomainEntity $entity */
foreach ( $domain -> getDomainEntities () -> toArray () as $entity ) {
$vCard = Reader :: readJson ( $entity -> getEntity () -> getJCard ());
2024-08-03 00:06:38 +02:00
if ( isset ( $vCard -> EMAIL ) && isset ( $vCard -> FN )) {
$email = ( string ) $vCard -> EMAIL ;
if ( ! filter_var ( $email , FILTER_VALIDATE_EMAIL )) {
continue ;
}
$attendees [] = ( new Attendee ( new EmailAddress ( $email ))) -> setDisplayName (( string ) $vCard -> FN );
2024-08-02 23:24:52 +02:00
}
2024-08-01 14:37:23 +02:00
}
/** @var DomainEvent $event */
foreach ( $domain -> getEvents () -> toArray () as $event ) {
$calendar -> addEvent (( new Event ())
2024-08-03 17:55:39 +02:00
-> setLastModified ( new Timestamp ( $domain -> getUpdatedAt ()))
-> setStatus ( EventStatus :: CONFIRMED ())
2024-08-02 23:24:52 +02:00
-> setSummary ( $domain -> getLdhName () . ' (' . $event -> getAction () . ')' )
2024-08-01 14:37:23 +02:00
-> addCategory ( new Category ( $event -> getAction ()))
-> setAttendees ( $attendees )
-> setOccurrence ( new SingleDay ( new Date ( $event -> getDate ())))
);
}
}
2024-08-03 18:05:05 +02:00
$calendarResponse = ( new CalendarFactory ()) -> createCalendar ( $calendar );
$calendarName = $watchList -> getName ();
if ( null !== $calendarName ) {
$calendarResponse -> withProperty ( new Property ( 'X-WR-CALNAME' , new TextValue ( $calendarName )));
}
return new Response ( $calendarResponse , Response :: HTTP_OK , [
2024-08-02 23:24:52 +02:00
'Content-Type' => 'text/calendar; charset=utf-8' ,
2024-08-01 14:37:23 +02:00
]);
}
#[Route(
path : '/api/watchlists' ,
name : 'watchlist_get_all_mine' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'get_all_mine' ,
],
methods : [ 'GET' ]
)]
public function getWatchLists () : Collection
{
/** @var User $user */
$user = $this -> getUser ();
2024-08-02 23:24:52 +02:00
2024-08-01 14:37:23 +02:00
return $user -> getWatchLists ();
}
2024-08-02 23:24:52 +02:00
}