1 <?php 
declare(strict_types
=1); 
   4  * This file is part of the Rapsys AirBundle package. 
   6  * (c) Raphaël Gertz <symfony@rapsys.eu> 
   8  * For the full copyright and license information, please view the LICENSE 
   9  * file that was distributed with this source code. 
  12 namespace Rapsys\AirBundle\Controller
; 
  14 use Symfony\Bridge\Twig\Mime\TemplatedEmail
; 
  15 use Symfony\Component\Form\FormError
; 
  16 use Symfony\Component\HttpFoundation\Request
; 
  17 use Symfony\Component\HttpFoundation\Response
; 
  18 use Symfony\Component\Mailer\Exception\TransportExceptionInterface
; 
  19 use Symfony\Component\Mailer\MailerInterface
; 
  20 use Symfony\Component\Mime\Address
; 
  21 use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
; 
  23 use Rapsys\AirBundle\Entity\Civility
; 
  24 use Rapsys\AirBundle\Entity\Location
; 
  25 use Rapsys\AirBundle\Entity\Session
; 
  26 use Rapsys\AirBundle\Entity\Snippet
; 
  27 use Rapsys\AirBundle\Entity\User
; 
  28 use Rapsys\AirBundle\Pdf\DisputePdf
; 
  33 class DefaultController 
extends AbstractController 
{ 
  37          * @desc Display the about informations 
  39          * @param Request $request The request instance 
  40          * @return Response The rendered view 
  42         public function about(Request 
$request): Response 
{ 
  44                 $this->context
['title'] = $this->translator
->trans('About'); 
  47                 $this->context
['description'] = $this->translator
->trans('Libre Air about'); 
  50                 $this->context
['keywords'] = [ 
  51                         $this->translator
->trans('about'), 
  52                         $this->translator
->trans('Libre Air') 
  56                 $response = $this->render('@RapsysAir/default/about.html.twig', $this->context
); 
  57                 $response->setEtag(md5($response->getContent())); 
  58                 $response->setPublic(); 
  59                 $response->isNotModified($request); 
  68          * @desc Send a contact mail to configured contact 
  70          * @param Request $request The request instance 
  71          * @param MailerInterface $mailer The mailer instance 
  73          * @return Response The rendered view or redirection 
  75         public function contact(Request 
$request, MailerInterface 
$mailer): Response 
{ 
  77                 $this->context
['title'] = $this->translator
->trans('Contact'); 
  80                 $this->context
['description'] = $this->translator
->trans('Contact Libre Air'); 
  83                 $this->context
['keywords'] = [ 
  84                         $this->translator
->trans('contact'), 
  85                         $this->translator
->trans('Libre Air'), 
  86                         $this->translator
->trans('outdoor'), 
  87                         $this->translator
->trans('Argentine Tango'), 
  88                         $this->translator
->trans('calendar') 
  91                 //Create the form according to the FormType created previously. 
  92                 //And give the proper parameters 
  93                 $form = $this->createForm('Rapsys\AirBundle\Form\ContactType', null, [ 
  94                         'action' => $this->generateUrl('rapsys_air_contact'), 
  98                 if ($request->isMethod('POST')) { 
  99                         // Refill the fields in case the form is not valid. 
 100                         $form->handleRequest($request); 
 102                         if ($form->isValid()) { 
 104                                 $data = $form->getData(); 
 107                                 $message = (new TemplatedEmail()) 
 109                                         ->from(new Address($data['mail'], $data['name'])) 
 111                                         ->to(new Address($this->context
['contact']['mail'], $this->context
['contact']['title'])) 
 113                                         ->subject($data['subject']) 
 115                                         //Set path to twig templates 
 116                                         ->htmlTemplate('@RapsysAir/mail/contact.html.twig') 
 117                                         ->textTemplate('@RapsysAir/mail/contact.text.twig') 
 122                                                         'subject' => $data['subject'], 
 123                                                         'message' => strip_tags($data['message']), 
 127                                 //Try sending message 
 128                                 //XXX: mail delivery may silently fail 
 131                                         $mailer->send($message); 
 133                                         //Redirect on the same route with sent=1 to cleanup form 
 134                                         return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params')); 
 135                                 //Catch obvious transport exception 
 136                                 } catch(TransportExceptionInterface 
$e) { 
 137                                         if ($message = $e->getMessage()) { 
 138                                                 //Add error message mail unreachable 
 139                                                 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->context
['contact']['mail'], '%message%' => $this->translator
->trans($message)]))); 
 141                                                 //Add error message mail unreachable 
 142                                                 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to contact: %mail%', ['%mail%' => $this->context
['contact']['mail']]))); 
 149                 return $this->render('@RapsysAir/form/contact.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
); 
 155          * @desc Generate a dispute document 
 157          * @param Request $request The request instance 
 158          * @param MailerInterface $mailer The mailer instance 
 160          * @return Response The rendered view or redirection 
 162         public function dispute(Request 
$request, MailerInterface 
$mailer): Response 
{ 
 163                 //Prevent non-guest to access here 
 164                 $this->denyAccessUnlessGranted('ROLE_USER', null, $this->translator
->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator
->trans('User')])); 
 167                 $this->context
['title'] = $this->translator
->trans('Dispute'); 
 170                 $this->context
['description'] = $this->translator
->trans('Libre Air dispute'); 
 173                 $this->context
['keywords'] = [ 
 174                         $this->translator
->trans('dispute'), 
 175                         $this->translator
->trans('Libre Air'), 
 176                         $this->translator
->trans('outdoor'), 
 177                         $this->translator
->trans('Argentine Tango'), 
 178                         $this->translator
->trans('calendar') 
 181                 //Create the form according to the FormType created previously. 
 182                 //And give the proper parameters 
 183                 $form = $this->createForm('Rapsys\AirBundle\Form\DisputeType', ['court' => 'Paris', 'abstract' => 'Pour constater cette prétendue infraction, les agents verbalisateurs ont pénétré dans un jardin privatif, sans visibilité depuis la voie publique, situé derrière un batiment privé, pour ce faire ils ont franchi au moins un grillage de chantier ou des potteaux métalliques séparant le terrain privé de la voie publique de l\'autre côté du batiment.'], [ 
 184                         'action' => $this->generateUrl('rapsys_air_dispute'), 
 188                 if ($request->isMethod('POST')) { 
 189                         // Refill the fields in case the form is not valid. 
 190                         $form->handleRequest($request); 
 192                         if ($form->isValid()) { 
 194                                 $data = $form->getData(); 
 197                                 if (!empty($data['offense']) && $data['offense'] == 'gathering') { 
 199                                         $output = DisputePdf
::genGathering($data['court'], $data['notice'], $data['agent'], $data['service'], $data['abstract'], $this->translator
->trans($this->getUser()->getCivility()->getTitle()), $this->getUser()->getForename(), $this->getUser()->getSurname()); 
 201                                 } elseif (!empty($data['offense'] && $data['offense'] == 'traffic')) { 
 203                                         $output = DisputePdf
::genTraffic($data['court'], $data['notice'], $data['agent'], $data['service'], $data['abstract'], $this->translator
->trans($this->getUser()->getCivility()->getTitle()), $this->getUser()->getForename(), $this->getUser()->getSurname()); 
 204                                 //Unsupported offense 
 206                                         header('Content-Type: text/plain'); 
 211                                 //Send common headers 
 212                                 header('Content-Type: application/pdf'); 
 214                                 //Send remaining headers 
 215                                 header('Cache-Control: private, max-age=0, must-revalidate'); 
 216                                 header('Pragma: public'); 
 218                                 //Send content-length 
 219                                 header('Content-Length: '.strlen($output)); 
 228 #                               $message = (new TemplatedEmail()) 
 230 #                                       ->from(new Address($data['mail'], $data['name'])) 
 232 #                                       //XXX: remove the debug set in vendor/symfony/mime/Address.php +46 
 233 #                                       ->to(new Address($this->config['contact']['mail'], $this->config['contact']['title'])) 
 235 #                                       ->subject($data['subject']) 
 237 #                                       //Set path to twig templates 
 238 #                                       ->htmlTemplate('@RapsysAir/mail/contact.html.twig') 
 239 #                                       ->textTemplate('@RapsysAir/mail/contact.text.twig') 
 244 #                                                       'subject' => $data['subject'], 
 245 #                                                       'message' => strip_tags($data['message']), 
 249 #                               //Try sending message 
 250 #                               //XXX: mail delivery may silently fail 
 253 #                                       $mailer->send($message); 
 255 #                                       //Redirect on the same route with sent=1 to cleanup form 
 256 #                                       return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params')); 
 257 #                               //Catch obvious transport exception 
 258 #                               } catch(TransportExceptionInterface $e) { 
 259 #                                       if ($message = $e->getMessage()) { 
 260 #                                               //Add error message mail unreachable 
 261 #                                               $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->config['contact']['mail'], '%message%' => $this->translator->trans($message)]))); 
 263 #                                               //Add error message mail unreachable 
 264 #                                               $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%', ['%mail%' => $this->config['contact']['mail']]))); 
 271                 return $this->render('@RapsysAir/default/dispute.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
); 
 277          * @desc Display all granted sessions with an application or login form 
 279          * @param Request $request The request instance 
 280          * @return Response The rendered view 
 282         public function index(Request 
$request): Response 
{ 
 284                 $doctrine = $this->getDoctrine(); 
 287                 $this->context
['title'] = $this->translator
->trans('Argentine Tango in Paris'); 
 290                 $this->context
['description'] = $this->translator
->trans('Outdoor Argentine Tango session calendar in Paris'); 
 293                 $this->context
['keywords'] = [ 
 294                         $this->translator
->trans('Argentine Tango'), 
 295                         $this->translator
->trans('Paris'), 
 296                         $this->translator
->trans('outdoor'), 
 297                         $this->translator
->trans('calendar'), 
 298                         $this->translator
->trans('Libre Air') 
 302                 //XXX: only valid for home page 
 303                 $this->context
['facebook']['metas']['og:type'] = 'website'; 
 306                 $period = new \
DatePeriod( 
 307                         //Start from first monday of week 
 308                         new \
DateTime('Monday this week'), 
 309                         //Iterate on each day 
 310                         new \
DateInterval('P1D'), 
 311                         //End with next sunday and 4 weeks 
 313                                 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week' 
 318                 $calendar = $doctrine->getRepository(Session
::class)->fetchCalendarByDatePeriod($this->translator
, $period, null, $request->get('session'), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $request->getLocale()); 
 321                 //XXX: we want to display all active locations anyway 
 322                 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period); 
 325                 return $this->render('@RapsysAir/default/index.html.twig', ['calendar' => $calendar, 'locations' => $locations]+
$this->context
); 
 327                 //Set Cache-Control must-revalidate directive 
 328                 //TODO: add a javascript forced refresh after 1h ? or header refresh ? 
 329                 #$response->setPublic(true); 
 330                 #$response->setMaxAge(300); 
 331                 #$response->mustRevalidate(); 
 332                 ##$response->setCache(['public' => true, 'max_age' => 300]); 
 334                 //Return the response 
 339          * The organizer regulation page 
 341          * @desc Display the organizer regulation policy 
 343          * @param Request $request The request instance 
 344          * @return Response The rendered view 
 346         public function organizerRegulation(Request 
$request): Response 
{ 
 348                 $this->context
['title'] = $this->translator
->trans('Organizer regulation'); 
 351                 $this->context
['description'] = $this->translator
->trans('Libre Air organizer regulation'); 
 354                 $this->context
['keywords'] = [ 
 355                         $this->translator
->trans('organizer regulation'), 
 356                         $this->translator
->trans('Libre Air') 
 360                 $response = $this->render('@RapsysAir/default/organizer_regulation.html.twig', $this->context
); 
 363                 $response->setEtag(md5($response->getContent())); 
 364                 $response->setPublic(); 
 365                 $response->isNotModified($request); 
 372          * The terms of service page 
 374          * @desc Display the terms of service policy 
 376          * @param Request $request The request instance 
 377          * @return Response The rendered view 
 379         public function termsOfService(Request 
$request): Response 
{ 
 381                 $this->context
['title'] = $this->translator
->trans('Terms of service'); 
 384                 $this->context
['description'] = $this->translator
->trans('Libre Air terms of service'); 
 387                 $this->context
['keywords'] = [ 
 388                         $this->translator
->trans('terms of service'), 
 389                         $this->translator
->trans('Libre Air') 
 393                 $response = $this->render('@RapsysAir/default/terms_of_service.html.twig', $this->context
); 
 396                 $response->setEtag(md5($response->getContent())); 
 397                 $response->setPublic(); 
 398                 $response->isNotModified($request); 
 405          * The frequently asked questions page 
 407          * @desc Display the frequently asked questions 
 409          * @param Request $request The request instance 
 410          * @return Response The rendered view 
 412         public function frequentlyAskedQuestions(Request 
$request): Response 
{ 
 414                 $this->context
['title'] = $this->translator
->trans('Frequently asked questions'); 
 417                 $this->context
['description'] = $this->translator
->trans('Libre Air frequently asked questions'); 
 420                 $this->context
['keywords'] = [ 
 421                         $this->translator
->trans('frequently asked questions'), 
 422                         $this->translator
->trans('faq'), 
 423                         $this->translator
->trans('Libre Air') 
 427                 $response = $this->render('@RapsysAir/default/frequently_asked_questions.html.twig', $this->context
); 
 430                 $response->setEtag(md5($response->getContent())); 
 431                 $response->setPublic(); 
 432                 $response->isNotModified($request); 
 441          * @desc Display all user with a group listed as users 
 443          * @param Request $request The request instance 
 445          * @return Response The rendered view 
 447         public function userIndex(Request 
$request): Response 
{ 
 449                 $doctrine = $this->getDoctrine(); 
 452                 if ($this->isGranted('ROLE_ADMIN')) { 
 454                         $section = $this->translator
->trans('Libre Air users'); 
 457                         $this->context
['description'] = $this->translator
->trans('Libre Air user list'); 
 461                         $section = $this->translator
->trans('Libre Air organizers'); 
 464                         $this->context
['description'] = $this->translator
->trans('Libre Air organizers list'); 
 468                 $this->context
['keywords'] = [ 
 469                         $this->translator
->trans('users'), 
 470                         $this->translator
->trans('user list'), 
 471                         $this->translator
->trans('listing'), 
 472                         $this->translator
->trans('Libre Air') 
 476                 $title = $this->translator
->trans($this->config
['site']['title']).' - '.$section; 
 479                 $users = $doctrine->getRepository(User
::class)->findUserGroupedByTranslatedGroup($this->translator
); 
 482                 $period = new \
DatePeriod( 
 483                         //Start from first monday of week 
 484                         new \
DateTime('Monday this week'), 
 485                         //Iterate on each day 
 486                         new \
DateInterval('P1D'), 
 487                         //End with next sunday and 4 weeks 
 489                                 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week' 
 494                 if ($this->isGranted('ROLE_ADMIN')) { 
 496                         $this->context
['groups'] = $users; 
 499                         //Only display senior organizers 
 500                         $this->context
['users'] = $users[$this->translator
->trans('Senior')]; 
 504                 //XXX: we want to display all active locations anyway 
 505                 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period); 
 508                 return $this->render('@RapsysAir/user/index.html.twig', ['title' => $title, 'section' => $section, 'locations' => $locations]+
$this->context
); 
 512          * List all sessions for the user 
 514          * @desc Display all sessions for the user with an application or login form 
 516          * @param Request $request The request instance 
 517          * @param int $id The user id 
 519          * @return Response The rendered view 
 521         public function userView(Request 
$request, $id): Response 
{ 
 523                 $doctrine = $this->getDoctrine(); 
 526                 if (empty($user = $doctrine->getRepository(User
::class)->findOneById($id))) { 
 527                         throw $this->createNotFoundException($this->translator
->trans('Unable to find user: %id%', ['%id%' => $id])); 
 531                 $token = new UsernamePasswordToken($user, null, 'none', $user->getRoles()); 
 534                 $isGuest = $this->get('rapsys_user.access_decision_manager')->decide($token, ['ROLE_GUEST']); 
 536                 //Prevent access when not admin, user is not guest and not currently logged user 
 537                 if (!$this->isGranted('ROLE_ADMIN') && empty($isGuest) && $user != $this->getUser()) { 
 538                         throw $this->createAccessDeniedException($this->translator
->trans('Unable to access user: %id%', ['%id%' => $id])); 
 542                 $section = $user->getPseudonym(); 
 545                 $title = $this->translator
->trans($this->config
['site']['title']).' - '.$section; 
 548                 $this->context
['description'] = $this->translator
->trans('%pseudonym% outdoor Argentine Tango session calendar', [ '%pseudonym%' => $user->getPseudonym() ]); 
 551                 $this->context
['keywords'] = [ 
 552                         $user->getPseudonym(), 
 553                         $this->translator
->trans('outdoor'), 
 554                         $this->translator
->trans('Argentine Tango'), 
 555                         $this->translator
->trans('calendar') 
 559                 $period = new \
DatePeriod( 
 560                         //Start from first monday of week 
 561                         new \
DateTime('Monday this week'), 
 562                         //Iterate on each day 
 563                         new \
DateInterval('P1D'), 
 564                         //End with next sunday and 4 weeks 
 566                                 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week' 
 571                 //TODO: highlight with current session route parameter 
 572                 $calendar = $doctrine->getRepository(Session
::class)->fetchUserCalendarByDatePeriod($this->translator
, $period, $isGuest?$id:null, $request->get('session'), $request->getLocale()); 
 575                 //XXX: we want to display all active locations anyway 
 576                 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period, $id); 
 578                 //Create user form for admin or current user 
 579                 if ($this->isGranted('ROLE_ADMIN') || $user == $this->getUser()) { 
 580                         //Create SnippetType form 
 581                         $userForm = $this->createForm('Rapsys\AirBundle\Form\RegisterType', $user, [ 
 583                                 'action' => $this->generateUrl('rapsys_air_user_view', ['id' => $id]), 
 584                                 //Set the form attribute 
 585                                 'attr' => [ 'class' => 'col' ], 
 587                                 'civility_class' => Civility
::class, 
 589                                 'mail' => $this->isGranted('ROLE_ADMIN'), 
 594                         //Init user to context 
 595                         $this->context
['forms']['user'] = $userForm->createView(); 
 598                         if ($request->isMethod('POST')) { 
 599                                 //Refill the fields in case the form is not valid. 
 600                                 $userForm->handleRequest($request); 
 602                                 //Handle invalid form 
 603                                 if (!$userForm->isSubmitted() || !$userForm->isValid()) { 
 605                                         return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+
$this->context
); 
 609                                 $data = $userForm->getData(); 
 612                                 $manager = $doctrine->getManager(); 
 615                                 $manager->persist($data); 
 617                                 //Flush to get the ids 
 621                                 $this->addFlash('notice', $this->translator
->trans('User %id% updated', ['%id%' => $id])); 
 623                                 //Extract and process referer 
 624                                 if ($referer = $request->headers
->get('referer')) { 
 625                                         //Create referer request instance 
 626                                         $req = Request
::create($referer); 
 629                                         $path = $req->getPathInfo(); 
 631                                         //Get referer query string 
 632                                         $query = $req->getQueryString(); 
 635                                         $path = str_replace($request->getScriptName(), '', $path); 
 637                                         //Try with referer path 
 640                                                 $oldContext = $this->router
->getContext(); 
 642                                                 //Force clean context 
 643                                                 //XXX: prevent MethodNotAllowedException because current context method is POST in onevendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php+42 
 644                                                 $this->router
->setContext(new RequestContext()); 
 646                                                 //Retrieve route matching path 
 647                                                 $route = $this->router
->match($path); 
 650                                                 $this->router
->setContext($oldContext); 
 656                                                 $name = $route['_route']; 
 658                                                 //Remove route and controller from route defaults 
 659                                                 unset($route['_route'], $route['_controller']); 
 661                                                 //Check if user view route 
 662                                                 if ($name == 'rapsys_air_user_view' && !empty($route['id'])) { 
 664                                                         $route['id'] = $data->getId(); 
 668                                                         $route['user'] = $data->getId(); 
 672                                                 return $this->redirectToRoute($name, $route); 
 674                                         } catch(MethodNotAllowedException
|ResourceNotFoundException 
$e) { 
 675                                                 //Unset referer to fallback to default route 
 680                                 //Redirect to cleanup the form 
 681                                 return $this->redirectToRoute('rapsys_air', ['user' => $data->getId()]); 
 685                 //Create snippet forms for role_guest 
 686                 if ($this->isGranted('ROLE_ADMIN') || ($this->isGranted('ROLE_GUEST') && $user == $this->getUser())) { 
 687                         //Fetch all user snippet 
 688                         $snippets = $doctrine->getRepository(Snippet
::class)->findByLocaleUserId($request->getLocale(), $id); 
 690                         //Rekey by location id 
 691                         $snippets = array_reduce($snippets, function($carry, $item){$carry
[$item
->getLocation()->getId()] = $item
; return $carry
;}, []); 
 693                         //Init snippets to context 
 694                         $this->context
['forms']['snippets'] = []; 
 696                         //Iterate on locations 
 697                         foreach($locations as $locationId => $location) { 
 699                                 $snippet = new Snippet(); 
 702                                 $snippet->setLocale($request->getLocale()); 
 705                                 $snippet->setUser($user); 
 707                                 //Set default location 
 708                                 $snippet->setLocation($doctrine->getRepository(Location
::class)->findOneById($locationId)); 
 710                                 //With existing snippet 
 711                                 if (!empty($snippets[$locationId])) { 
 712                                         $snippet = $snippets[$locationId]; 
 713                                         $action = $this->generateUrl('rapsys_air_snippet_edit', ['id' => $snippet->getId()]); 
 716                                         $action = $this->generateUrl('rapsys_air_snippet_add', ['location' => $locationId]); 
 719                                 //Create SnippetType form 
 720                                 $form = $this->container
->get('form.factory')->createNamed('snipped_'.$request->getLocale().'_'.$locationId, 'Rapsys\AirBundle\Form\SnippetType', $snippet, [ 
 723                                         //Set the form attribute 
 727                                 //Add form to context 
 728                                 $this->context
['forms']['snippets'][$locationId] = $form->createView(); 
 733                 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+
$this->context
);