]> Raphaël G. Git Repositories - airbundle/blob - Controller/DefaultController.php
Add strict
[airbundle] / Controller / DefaultController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys AirBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\AirBundle\Controller;
13
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
22 use Rapsys\AirBundle\Entity\Location;
23 use Rapsys\AirBundle\Entity\Session;
24 use Rapsys\AirBundle\Pdf\DisputePdf;
25
26 /**
27 * {@inheritdoc}
28 */
29 class DefaultController extends AbstractController {
30 /**
31 * The about page
32 *
33 * @desc Display the about informations
34 *
35 * @param Request $request The request instance
36 * @return Response The rendered view
37 */
38 public function about(Request $request): Response {
39 //Set page
40 $this->context['title'] = $this->translator->trans('About');
41
42 //Set description
43 $this->context['description'] = $this->translator->trans('Libre Air about');
44
45 //Set keywords
46 $this->context['keywords'] = [
47 $this->translator->trans('about'),
48 $this->translator->trans('Libre Air')
49 ];
50
51 //Render template
52 $response = $this->render('@RapsysAir/default/about.html.twig', $this->context);
53 $response->setEtag(md5($response->getContent()));
54 $response->setPublic();
55 $response->isNotModified($request);
56
57 //Return response
58 return $response;
59 }
60
61 /**
62 * The contact page
63 *
64 * @desc Send a contact mail to configured contact
65 *
66 * @param Request $request The request instance
67 * @param MailerInterface $mailer The mailer instance
68 *
69 * @return Response The rendered view or redirection
70 */
71 public function contact(Request $request, MailerInterface $mailer): Response {
72 //Set page
73 $this->context['title'] = $this->translator->trans('Contact');
74
75 //Set description
76 $this->context['description'] = $this->translator->trans('Contact Libre Air');
77
78 //Set keywords
79 $this->context['keywords'] = [
80 $this->translator->trans('contact'),
81 $this->translator->trans('Libre Air'),
82 $this->translator->trans('outdoor'),
83 $this->translator->trans('Argentine Tango'),
84 $this->translator->trans('calendar')
85 ];
86
87 //Create the form according to the FormType created previously.
88 //And give the proper parameters
89 $form = $this->createForm('Rapsys\AirBundle\Form\ContactType', null, [
90 'action' => $this->generateUrl('rapsys_air_contact'),
91 'method' => 'POST'
92 ]);
93
94 if ($request->isMethod('POST')) {
95 // Refill the fields in case the form is not valid.
96 $form->handleRequest($request);
97
98 if ($form->isValid()) {
99 //Get data
100 $data = $form->getData();
101
102 //Create message
103 $message = (new TemplatedEmail())
104 //Set sender
105 ->from(new Address($data['mail'], $data['name']))
106 //Set recipient
107 ->to(new Address($this->context['contact']['mail'], $this->context['contact']['title']))
108 //Set subject
109 ->subject($data['subject'])
110
111 //Set path to twig templates
112 ->htmlTemplate('@RapsysAir/mail/contact.html.twig')
113 ->textTemplate('@RapsysAir/mail/contact.text.twig')
114
115 //Set context
116 ->context(
117 [
118 'subject' => $data['subject'],
119 'message' => strip_tags($data['message']),
120 ]+$this->context
121 );
122
123 //Try sending message
124 //XXX: mail delivery may silently fail
125 try {
126 //Send message
127 $mailer->send($message);
128
129 //Redirect on the same route with sent=1 to cleanup form
130 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
131 //Catch obvious transport exception
132 } catch(TransportExceptionInterface $e) {
133 if ($message = $e->getMessage()) {
134 //Add error message mail unreachable
135 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->context['contact']['mail'], '%message%' => $this->translator->trans($message)])));
136 } else {
137 //Add error message mail unreachable
138 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%', ['%mail%' => $this->context['contact']['mail']])));
139 }
140 }
141 }
142 }
143
144 //Render template
145 return $this->render('@RapsysAir/form/contact.html.twig', ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->context);
146 }
147
148 /**
149 * The dispute page
150 *
151 * @desc Generate a dispute document
152 *
153 * @param Request $request The request instance
154 * @param MailerInterface $mailer The mailer instance
155 *
156 * @return Response The rendered view or redirection
157 */
158 public function dispute(Request $request, MailerInterface $mailer): Response {
159 //Prevent non-guest to access here
160 $this->denyAccessUnlessGranted('ROLE_USER', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('User')]));
161
162 //Set page
163 $this->context['title'] = $this->translator->trans('Dispute');
164
165 //Set description
166 $this->context['description'] = $this->translator->trans('Libre Air dispute');
167
168 //Set keywords
169 $this->context['keywords'] = [
170 $this->translator->trans('dispute'),
171 $this->translator->trans('Libre Air'),
172 $this->translator->trans('outdoor'),
173 $this->translator->trans('Argentine Tango'),
174 $this->translator->trans('calendar')
175 ];
176
177 //Create the form according to the FormType created previously.
178 //And give the proper parameters
179 $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.'], [
180 'action' => $this->generateUrl('rapsys_air_dispute'),
181 'method' => 'POST'
182 ]);
183
184 if ($request->isMethod('POST')) {
185 // Refill the fields in case the form is not valid.
186 $form->handleRequest($request);
187
188 if ($form->isValid()) {
189 //Get data
190 $data = $form->getData();
191
192 //Gathering offense
193 if (!empty($data['offense']) && $data['offense'] == 'gathering') {
194 //Add gathering
195 $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());
196 //Traffic offense
197 } elseif (!empty($data['offense'] && $data['offense'] == 'traffic')) {
198 //Add traffic
199 $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());
200 //Unsupported offense
201 } else {
202 header('Content-Type: text/plain');
203 die('TODO');
204 exit;
205 }
206
207 //Send common headers
208 header('Content-Type: application/pdf');
209
210 //Send remaining headers
211 header('Cache-Control: private, max-age=0, must-revalidate');
212 header('Pragma: public');
213
214 //Send content-length
215 header('Content-Length: '.strlen($output));
216
217 //Display the pdf
218 echo $output;
219
220 //Die for now
221 exit;
222
223 # //Create message
224 # $message = (new TemplatedEmail())
225 # //Set sender
226 # ->from(new Address($data['mail'], $data['name']))
227 # //Set recipient
228 # //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
229 # ->to(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
230 # //Set subject
231 # ->subject($data['subject'])
232 #
233 # //Set path to twig templates
234 # ->htmlTemplate('@RapsysAir/mail/contact.html.twig')
235 # ->textTemplate('@RapsysAir/mail/contact.text.twig')
236 #
237 # //Set context
238 # ->context(
239 # [
240 # 'subject' => $data['subject'],
241 # 'message' => strip_tags($data['message']),
242 # ]+$this->context
243 # );
244 #
245 # //Try sending message
246 # //XXX: mail delivery may silently fail
247 # try {
248 # //Send message
249 # $mailer->send($message);
250 #
251 # //Redirect on the same route with sent=1 to cleanup form
252 # return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
253 # //Catch obvious transport exception
254 # } catch(TransportExceptionInterface $e) {
255 # if ($message = $e->getMessage()) {
256 # //Add error message mail unreachable
257 # $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->config['contact']['mail'], '%message%' => $this->translator->trans($message)])));
258 # } else {
259 # //Add error message mail unreachable
260 # $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%', ['%mail%' => $this->config['contact']['mail']])));
261 # }
262 # }
263 }
264 }
265
266 //Render template
267 return $this->render('@RapsysAir/default/dispute.html.twig', ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->context);
268 }
269
270 /**
271 * The index page
272 *
273 * @desc Display all granted sessions with an application or login form
274 *
275 * @param Request $request The request instance
276 * @return Response The rendered view
277 */
278 public function index(Request $request): Response {
279 //Fetch doctrine
280 $doctrine = $this->getDoctrine();
281
282 //Set page
283 $this->context['title'] = $this->translator->trans('Argentine Tango in Paris');
284
285 //Set description
286 $this->context['description'] = $this->translator->trans('Outdoor Argentine Tango session calendar in Paris');
287
288 //Set keywords
289 $this->context['keywords'] = [
290 $this->translator->trans('Argentine Tango'),
291 $this->translator->trans('Paris'),
292 $this->translator->trans('outdoor'),
293 $this->translator->trans('calendar'),
294 $this->translator->trans('Libre Air')
295 ];
296
297 //Set facebook type
298 //XXX: only valid for home page
299 $this->context['facebook']['metas']['og:type'] = 'website';
300
301 //Compute period
302 $period = new \DatePeriod(
303 //Start from first monday of week
304 new \DateTime('Monday this week'),
305 //Iterate on each day
306 new \DateInterval('P1D'),
307 //End with next sunday and 4 weeks
308 new \DateTime(
309 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
310 )
311 );
312
313 //Fetch calendar
314 $calendar = $doctrine->getRepository(Session::class)->fetchCalendarByDatePeriod($this->translator, $period, null, $request->get('session'), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $request->getLocale());
315
316 //Fetch locations
317 //XXX: we want to display all active locations anyway
318 $locations = $doctrine->getRepository(Location::class)->findTranslatedSortedByPeriod($this->translator, $period);
319
320 //Render the view
321 return $this->render('@RapsysAir/default/index.html.twig', ['calendar' => $calendar, 'locations' => $locations]+$this->context);
322
323 //Set Cache-Control must-revalidate directive
324 //TODO: add a javascript forced refresh after 1h ? or header refresh ?
325 #$response->setPublic(true);
326 #$response->setMaxAge(300);
327 #$response->mustRevalidate();
328 ##$response->setCache(['public' => true, 'max_age' => 300]);
329
330 //Return the response
331 #return $response;
332 }
333
334 /**
335 * The organizer regulation page
336 *
337 * @desc Display the organizer regulation policy
338 *
339 * @param Request $request The request instance
340 * @return Response The rendered view
341 */
342 public function organizerRegulation(Request $request): Response {
343 //Set page
344 $this->context['title'] = $this->translator->trans('Organizer regulation');
345
346 //Set description
347 $this->context['description'] = $this->translator->trans('Libre Air organizer regulation');
348
349 //Set keywords
350 $this->context['keywords'] = [
351 $this->translator->trans('organizer regulation'),
352 $this->translator->trans('Libre Air')
353 ];
354
355 //Render template
356 $response = $this->render('@RapsysAir/default/organizer_regulation.html.twig', $this->context);
357
358 //Set as cachable
359 $response->setEtag(md5($response->getContent()));
360 $response->setPublic();
361 $response->isNotModified($request);
362
363 //Return response
364 return $response;
365 }
366
367 /**
368 * The terms of service page
369 *
370 * @desc Display the terms of service policy
371 *
372 * @param Request $request The request instance
373 * @return Response The rendered view
374 */
375 public function termsOfService(Request $request): Response {
376 //Set page
377 $this->context['title'] = $this->translator->trans('Terms of service');
378
379 //Set description
380 $this->context['description'] = $this->translator->trans('Libre Air terms of service');
381
382 //Set keywords
383 $this->context['keywords'] = [
384 $this->translator->trans('terms of service'),
385 $this->translator->trans('Libre Air')
386 ];
387
388 //Render template
389 $response = $this->render('@RapsysAir/default/terms_of_service.html.twig', $this->context);
390
391 //Set as cachable
392 $response->setEtag(md5($response->getContent()));
393 $response->setPublic();
394 $response->isNotModified($request);
395
396 //Return response
397 return $response;
398 }
399
400 /**
401 * The frequently asked questions page
402 *
403 * @desc Display the frequently asked questions
404 *
405 * @param Request $request The request instance
406 * @return Response The rendered view
407 */
408 public function frequentlyAskedQuestions(Request $request): Response {
409 //Set page
410 $this->context['title'] = $this->translator->trans('Frequently asked questions');
411
412 //Set description
413 $this->context['description'] = $this->translator->trans('Libre Air frequently asked questions');
414
415 //Set keywords
416 $this->context['keywords'] = [
417 $this->translator->trans('frequently asked questions'),
418 $this->translator->trans('faq'),
419 $this->translator->trans('Libre Air')
420 ];
421
422 //Render template
423 $response = $this->render('@RapsysAir/default/frequently_asked_questions.html.twig', $this->context);
424
425 //Set as cachable
426 $response->setEtag(md5($response->getContent()));
427 $response->setPublic();
428 $response->isNotModified($request);
429
430 //Return response
431 return $response;
432 }
433
434 /**
435 * List all users
436 *
437 * @desc Display all user with a group listed as users
438 *
439 * @param Request $request The request instance
440 *
441 * @return Response The rendered view
442 */
443 public function userIndex(Request $request): Response {
444 //Fetch doctrine
445 $doctrine = $this->getDoctrine();
446
447 //With admin role
448 if ($this->isGranted('ROLE_ADMIN')) {
449 //Set section
450 $section = $this->translator->trans('Libre Air users');
451
452 //Set description
453 $this->context['description'] = $this->translator->trans('Libre Air user list');
454 //Without admin role
455 } else {
456 //Set section
457 $section = $this->translator->trans('Libre Air organizers');
458
459 //Set description
460 $this->context['description'] = $this->translator->trans('Libre Air organizers list');
461 }
462
463 //Set keywords
464 $this->context['keywords'] = [
465 $this->translator->trans('users'),
466 $this->translator->trans('user list'),
467 $this->translator->trans('listing'),
468 $this->translator->trans('Libre Air')
469 ];
470
471 //Set title
472 $title = $this->translator->trans($this->config['site']['title']).' - '.$section;
473
474 //Fetch users
475 $users = $doctrine->getRepository(User::class)->findUserGroupedByTranslatedGroup($this->translator);
476
477 //Compute period
478 $period = new \DatePeriod(
479 //Start from first monday of week
480 new \DateTime('Monday this week'),
481 //Iterate on each day
482 new \DateInterval('P1D'),
483 //End with next sunday and 4 weeks
484 new \DateTime(
485 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
486 )
487 );
488
489 //With admin role
490 if ($this->isGranted('ROLE_ADMIN')) {
491 //Display all users
492 $this->context['groups'] = $users;
493 //Without admin role
494 } else {
495 //Only display senior organizers
496 $this->context['users'] = $users[$this->translator->trans('Senior')];
497 }
498
499 //Fetch locations
500 //XXX: we want to display all active locations anyway
501 $locations = $doctrine->getRepository(Location::class)->findTranslatedSortedByPeriod($this->translator, $period);
502
503 //Render the view
504 return $this->render('@RapsysAir/user/index.html.twig', ['title' => $title, 'section' => $section, 'locations' => $locations]+$this->context);
505 }
506
507 /**
508 * List all sessions for the user
509 *
510 * @desc Display all sessions for the user with an application or login form
511 *
512 * @param Request $request The request instance
513 * @param int $id The user id
514 *
515 * @return Response The rendered view
516 */
517 public function userView(Request $request, $id): Response {
518 //Fetch doctrine
519 $doctrine = $this->getDoctrine();
520
521 //Fetch user
522 if (empty($user = $doctrine->getRepository(User::class)->findOneById($id))) {
523 throw $this->createNotFoundException($this->translator->trans('Unable to find user: %id%', ['%id%' => $id]));
524 }
525
526 //Get user token
527 $token = new UsernamePasswordToken($user, null, 'none', $user->getRoles());
528
529 //Check if guest
530 $isGuest = $this->get('rapsys_user.access_decision_manager')->decide($token, ['ROLE_GUEST']);
531
532 //Prevent access when not admin, user is not guest and not currently logged user
533 if (!$this->isGranted('ROLE_ADMIN') && empty($isGuest) && $user != $this->getUser()) {
534 throw $this->createAccessDeniedException($this->translator->trans('Unable to access user: %id%', ['%id%' => $id]));
535 }
536
537 //Set section
538 $section = $user->getPseudonym();
539
540 //Set title
541 $title = $this->translator->trans($this->config['site']['title']).' - '.$section;
542
543 //Set description
544 $this->context['description'] = $this->translator->trans('%pseudonym% outdoor Argentine Tango session calendar', [ '%pseudonym%' => $user->getPseudonym() ]);
545
546 //Set keywords
547 $this->context['keywords'] = [
548 $user->getPseudonym(),
549 $this->translator->trans('outdoor'),
550 $this->translator->trans('Argentine Tango'),
551 $this->translator->trans('calendar')
552 ];
553
554 //Compute period
555 $period = new \DatePeriod(
556 //Start from first monday of week
557 new \DateTime('Monday this week'),
558 //Iterate on each day
559 new \DateInterval('P1D'),
560 //End with next sunday and 4 weeks
561 new \DateTime(
562 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
563 )
564 );
565
566 //Fetch calendar
567 //TODO: highlight with current session route parameter
568 $calendar = $doctrine->getRepository(Session::class)->fetchUserCalendarByDatePeriod($this->translator, $period, $isGuest?$id:null, $request->get('session'), $request->getLocale());
569
570 //Fetch locations
571 //XXX: we want to display all active locations anyway
572 $locations = $doctrine->getRepository(Location::class)->findTranslatedSortedByPeriod($this->translator, $period, $id);
573
574 //Create user form for admin or current user
575 if ($this->isGranted('ROLE_ADMIN') || $user == $this->getUser()) {
576 //Create SnippetType form
577 $userForm = $this->createForm('Rapsys\AirBundle\Form\RegisterType', $user, [
578 //Set action
579 'action' => $this->generateUrl('rapsys_air_user_view', ['id' => $id]),
580 //Set the form attribute
581 'attr' => [ 'class' => 'col' ],
582 //Set civility class
583 'civility_class' => Civility::class,
584 //Disable mail
585 'mail' => $this->isGranted('ROLE_ADMIN'),
586 //Disable password
587 'password' => false
588 ]);
589
590 //Init user to context
591 $this->context['forms']['user'] = $userForm->createView();
592
593 //Check if submitted
594 if ($request->isMethod('POST')) {
595 //Refill the fields in case the form is not valid.
596 $userForm->handleRequest($request);
597
598 //Handle invalid form
599 if (!$userForm->isSubmitted() || !$userForm->isValid()) {
600 //Render the view
601 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+$this->context);
602 }
603
604 //Get data
605 $data = $userForm->getData();
606
607 //Get manager
608 $manager = $doctrine->getManager();
609
610 //Queue snippet save
611 $manager->persist($data);
612
613 //Flush to get the ids
614 $manager->flush();
615
616 //Add notice
617 $this->addFlash('notice', $this->translator->trans('User %id% updated', ['%id%' => $id]));
618
619 //Extract and process referer
620 if ($referer = $request->headers->get('referer')) {
621 //Create referer request instance
622 $req = Request::create($referer);
623
624 //Get referer path
625 $path = $req->getPathInfo();
626
627 //Get referer query string
628 $query = $req->getQueryString();
629
630 //Remove script name
631 $path = str_replace($request->getScriptName(), '', $path);
632
633 //Try with referer path
634 try {
635 //Save old context
636 $oldContext = $this->router->getContext();
637
638 //Force clean context
639 //XXX: prevent MethodNotAllowedException because current context method is POST in onevendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php+42
640 $this->router->setContext(new RequestContext());
641
642 //Retrieve route matching path
643 $route = $this->router->match($path);
644
645 //Reset context
646 $this->router->setContext($oldContext);
647
648 //Clear old context
649 unset($oldContext);
650
651 //Extract name
652 $name = $route['_route'];
653
654 //Remove route and controller from route defaults
655 unset($route['_route'], $route['_controller']);
656
657 //Check if user view route
658 if ($name == 'rapsys_air_user_view' && !empty($route['id'])) {
659 //Replace id
660 $route['id'] = $data->getId();
661 //Other routes
662 } else {
663 //Set user
664 $route['user'] = $data->getId();
665 }
666
667 //Generate url
668 return $this->redirectToRoute($name, $route);
669 //No route matched
670 } catch(MethodNotAllowedException|ResourceNotFoundException $e) {
671 //Unset referer to fallback to default route
672 unset($referer);
673 }
674 }
675
676 //Redirect to cleanup the form
677 return $this->redirectToRoute('rapsys_air', ['user' => $data->getId()]);
678 }
679 }
680
681 //Create snippet forms for role_guest
682 if ($this->isGranted('ROLE_ADMIN') || ($this->isGranted('ROLE_GUEST') && $user == $this->getUser())) {
683 //Fetch all user snippet
684 $snippets = $doctrine->getRepository(Snippet::class)->findByLocaleUserId($request->getLocale(), $id);
685
686 //Rekey by location id
687 $snippets = array_reduce($snippets, function($carry, $item){$carry[$item->getLocation()->getId()] = $item; return $carry;}, []);
688
689 //Init snippets to context
690 $this->context['forms']['snippets'] = [];
691
692 //Iterate on locations
693 foreach($locations as $locationId => $location) {
694 //Init snippet
695 $snippet = new Snippet();
696
697 //Set default locale
698 $snippet->setLocale($request->getLocale());
699
700 //Set default user
701 $snippet->setUser($user);
702
703 //Set default location
704 $snippet->setLocation($doctrine->getRepository(Location::class)->findOneById($locationId));
705
706 //With existing snippet
707 if (!empty($snippets[$locationId])) {
708 $snippet = $snippets[$locationId];
709 $action = $this->generateUrl('rapsys_air_snippet_edit', ['id' => $snippet->getId()]);
710 //Without snippet
711 } else {
712 $action = $this->generateUrl('rapsys_air_snippet_add', ['location' => $locationId]);
713 }
714
715 //Create SnippetType form
716 $form = $this->container->get('form.factory')->createNamed('snipped_'.$request->getLocale().'_'.$locationId, 'Rapsys\AirBundle\Form\SnippetType', $snippet, [
717 //Set the action
718 'action' => $action,
719 //Set the form attribute
720 'attr' => []
721 ]);
722
723 //Add form to context
724 $this->context['forms']['snippets'][$locationId] = $form->createView();
725 }
726 }
727
728 //Render the view
729 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+$this->context);
730 }
731 }