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
;
22 use Rapsys\AirBundle\Entity\Location
;
23 use Rapsys\AirBundle\Entity\Session
;
24 use Rapsys\AirBundle\Pdf\DisputePdf
;
29 class DefaultController
extends AbstractController
{
33 * @desc Display the about informations
35 * @param Request $request The request instance
36 * @return Response The rendered view
38 public function about(Request
$request): Response
{
40 $this->context
['title'] = $this->translator
->trans('About');
43 $this->context
['description'] = $this->translator
->trans('Libre Air about');
46 $this->context
['keywords'] = [
47 $this->translator
->trans('about'),
48 $this->translator
->trans('Libre Air')
52 $response = $this->render('@RapsysAir/default/about.html.twig', $this->context
);
53 $response->setEtag(md5($response->getContent()));
54 $response->setPublic();
55 $response->isNotModified($request);
64 * @desc Send a contact mail to configured contact
66 * @param Request $request The request instance
67 * @param MailerInterface $mailer The mailer instance
69 * @return Response The rendered view or redirection
71 public function contact(Request
$request, MailerInterface
$mailer): Response
{
73 $this->context
['title'] = $this->translator
->trans('Contact');
76 $this->context
['description'] = $this->translator
->trans('Contact Libre Air');
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')
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'),
94 if ($request->isMethod('POST')) {
95 // Refill the fields in case the form is not valid.
96 $form->handleRequest($request);
98 if ($form->isValid()) {
100 $data = $form->getData();
103 $message = (new TemplatedEmail())
105 ->from(new Address($data['mail'], $data['name']))
107 ->to(new Address($this->context
['contact']['mail'], $this->context
['contact']['title']))
109 ->subject($data['subject'])
111 //Set path to twig templates
112 ->htmlTemplate('@RapsysAir/mail/contact.html.twig')
113 ->textTemplate('@RapsysAir/mail/contact.text.twig')
118 'subject' => $data['subject'],
119 'message' => strip_tags($data['message']),
123 //Try sending message
124 //XXX: mail delivery may silently fail
127 $mailer->send($message);
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)])));
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']])));
145 return $this->render('@RapsysAir/form/contact.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
);
151 * @desc Generate a dispute document
153 * @param Request $request The request instance
154 * @param MailerInterface $mailer The mailer instance
156 * @return Response The rendered view or redirection
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')]));
163 $this->context
['title'] = $this->translator
->trans('Dispute');
166 $this->context
['description'] = $this->translator
->trans('Libre Air dispute');
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')
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'),
184 if ($request->isMethod('POST')) {
185 // Refill the fields in case the form is not valid.
186 $form->handleRequest($request);
188 if ($form->isValid()) {
190 $data = $form->getData();
193 if (!empty($data['offense']) && $data['offense'] == '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());
197 } elseif (!empty($data['offense'] && $data['offense'] == '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
202 header('Content-Type: text/plain');
207 //Send common headers
208 header('Content-Type: application/pdf');
210 //Send remaining headers
211 header('Cache-Control: private, max-age=0, must-revalidate');
212 header('Pragma: public');
214 //Send content-length
215 header('Content-Length: '.strlen($output));
224 # $message = (new TemplatedEmail())
226 # ->from(new Address($data['mail'], $data['name']))
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']))
231 # ->subject($data['subject'])
233 # //Set path to twig templates
234 # ->htmlTemplate('@RapsysAir/mail/contact.html.twig')
235 # ->textTemplate('@RapsysAir/mail/contact.text.twig')
240 # 'subject' => $data['subject'],
241 # 'message' => strip_tags($data['message']),
245 # //Try sending message
246 # //XXX: mail delivery may silently fail
249 # $mailer->send($message);
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)])));
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']])));
267 return $this->render('@RapsysAir/default/dispute.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
);
273 * @desc Display all granted sessions with an application or login form
275 * @param Request $request The request instance
276 * @return Response The rendered view
278 public function index(Request
$request): Response
{
280 $doctrine = $this->getDoctrine();
283 $this->context
['title'] = $this->translator
->trans('Argentine Tango in Paris');
286 $this->context
['description'] = $this->translator
->trans('Outdoor Argentine Tango session calendar in Paris');
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')
298 //XXX: only valid for home page
299 $this->context
['facebook']['metas']['og:type'] = 'website';
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
309 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
314 $calendar = $doctrine->getRepository(Session
::class)->fetchCalendarByDatePeriod($this->translator
, $period, null, $request->get('session'), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $request->getLocale());
317 //XXX: we want to display all active locations anyway
318 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period);
321 return $this->render('@RapsysAir/default/index.html.twig', ['calendar' => $calendar, 'locations' => $locations]+
$this->context
);
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]);
330 //Return the response
335 * The organizer regulation page
337 * @desc Display the organizer regulation policy
339 * @param Request $request The request instance
340 * @return Response The rendered view
342 public function organizerRegulation(Request
$request): Response
{
344 $this->context
['title'] = $this->translator
->trans('Organizer regulation');
347 $this->context
['description'] = $this->translator
->trans('Libre Air organizer regulation');
350 $this->context
['keywords'] = [
351 $this->translator
->trans('organizer regulation'),
352 $this->translator
->trans('Libre Air')
356 $response = $this->render('@RapsysAir/default/organizer_regulation.html.twig', $this->context
);
359 $response->setEtag(md5($response->getContent()));
360 $response->setPublic();
361 $response->isNotModified($request);
368 * The terms of service page
370 * @desc Display the terms of service policy
372 * @param Request $request The request instance
373 * @return Response The rendered view
375 public function termsOfService(Request
$request): Response
{
377 $this->context
['title'] = $this->translator
->trans('Terms of service');
380 $this->context
['description'] = $this->translator
->trans('Libre Air terms of service');
383 $this->context
['keywords'] = [
384 $this->translator
->trans('terms of service'),
385 $this->translator
->trans('Libre Air')
389 $response = $this->render('@RapsysAir/default/terms_of_service.html.twig', $this->context
);
392 $response->setEtag(md5($response->getContent()));
393 $response->setPublic();
394 $response->isNotModified($request);
401 * The frequently asked questions page
403 * @desc Display the frequently asked questions
405 * @param Request $request The request instance
406 * @return Response The rendered view
408 public function frequentlyAskedQuestions(Request
$request): Response
{
410 $this->context
['title'] = $this->translator
->trans('Frequently asked questions');
413 $this->context
['description'] = $this->translator
->trans('Libre Air frequently asked questions');
416 $this->context
['keywords'] = [
417 $this->translator
->trans('frequently asked questions'),
418 $this->translator
->trans('faq'),
419 $this->translator
->trans('Libre Air')
423 $response = $this->render('@RapsysAir/default/frequently_asked_questions.html.twig', $this->context
);
426 $response->setEtag(md5($response->getContent()));
427 $response->setPublic();
428 $response->isNotModified($request);
437 * @desc Display all user with a group listed as users
439 * @param Request $request The request instance
441 * @return Response The rendered view
443 public function userIndex(Request
$request): Response
{
445 $doctrine = $this->getDoctrine();
448 if ($this->isGranted('ROLE_ADMIN')) {
450 $section = $this->translator
->trans('Libre Air users');
453 $this->context
['description'] = $this->translator
->trans('Libre Air user list');
457 $section = $this->translator
->trans('Libre Air organizers');
460 $this->context
['description'] = $this->translator
->trans('Libre Air organizers list');
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')
472 $title = $this->translator
->trans($this->config
['site']['title']).' - '.$section;
475 $users = $doctrine->getRepository(User
::class)->findUserGroupedByTranslatedGroup($this->translator
);
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
485 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
490 if ($this->isGranted('ROLE_ADMIN')) {
492 $this->context
['groups'] = $users;
495 //Only display senior organizers
496 $this->context
['users'] = $users[$this->translator
->trans('Senior')];
500 //XXX: we want to display all active locations anyway
501 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period);
504 return $this->render('@RapsysAir/user/index.html.twig', ['title' => $title, 'section' => $section, 'locations' => $locations]+
$this->context
);
508 * List all sessions for the user
510 * @desc Display all sessions for the user with an application or login form
512 * @param Request $request The request instance
513 * @param int $id The user id
515 * @return Response The rendered view
517 public function userView(Request
$request, $id): Response
{
519 $doctrine = $this->getDoctrine();
522 if (empty($user = $doctrine->getRepository(User
::class)->findOneById($id))) {
523 throw $this->createNotFoundException($this->translator
->trans('Unable to find user: %id%', ['%id%' => $id]));
527 $token = new UsernamePasswordToken($user, null, 'none', $user->getRoles());
530 $isGuest = $this->get('rapsys_user.access_decision_manager')->decide($token, ['ROLE_GUEST']);
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]));
538 $section = $user->getPseudonym();
541 $title = $this->translator
->trans($this->config
['site']['title']).' - '.$section;
544 $this->context
['description'] = $this->translator
->trans('%pseudonym% outdoor Argentine Tango session calendar', [ '%pseudonym%' => $user->getPseudonym() ]);
547 $this->context
['keywords'] = [
548 $user->getPseudonym(),
549 $this->translator
->trans('outdoor'),
550 $this->translator
->trans('Argentine Tango'),
551 $this->translator
->trans('calendar')
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
562 $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week'
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());
571 //XXX: we want to display all active locations anyway
572 $locations = $doctrine->getRepository(Location
::class)->findTranslatedSortedByPeriod($this->translator
, $period, $id);
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, [
579 'action' => $this->generateUrl('rapsys_air_user_view', ['id' => $id]),
580 //Set the form attribute
581 'attr' => [ 'class' => 'col' ],
583 'civility_class' => Civility
::class,
585 'mail' => $this->isGranted('ROLE_ADMIN'),
590 //Init user to context
591 $this->context
['forms']['user'] = $userForm->createView();
594 if ($request->isMethod('POST')) {
595 //Refill the fields in case the form is not valid.
596 $userForm->handleRequest($request);
598 //Handle invalid form
599 if (!$userForm->isSubmitted() || !$userForm->isValid()) {
601 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+
$this->context
);
605 $data = $userForm->getData();
608 $manager = $doctrine->getManager();
611 $manager->persist($data);
613 //Flush to get the ids
617 $this->addFlash('notice', $this->translator
->trans('User %id% updated', ['%id%' => $id]));
619 //Extract and process referer
620 if ($referer = $request->headers
->get('referer')) {
621 //Create referer request instance
622 $req = Request
::create($referer);
625 $path = $req->getPathInfo();
627 //Get referer query string
628 $query = $req->getQueryString();
631 $path = str_replace($request->getScriptName(), '', $path);
633 //Try with referer path
636 $oldContext = $this->router
->getContext();
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());
642 //Retrieve route matching path
643 $route = $this->router
->match($path);
646 $this->router
->setContext($oldContext);
652 $name = $route['_route'];
654 //Remove route and controller from route defaults
655 unset($route['_route'], $route['_controller']);
657 //Check if user view route
658 if ($name == 'rapsys_air_user_view' && !empty($route['id'])) {
660 $route['id'] = $data->getId();
664 $route['user'] = $data->getId();
668 return $this->redirectToRoute($name, $route);
670 } catch(MethodNotAllowedException
|ResourceNotFoundException
$e) {
671 //Unset referer to fallback to default route
676 //Redirect to cleanup the form
677 return $this->redirectToRoute('rapsys_air', ['user' => $data->getId()]);
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);
686 //Rekey by location id
687 $snippets = array_reduce($snippets, function($carry, $item){$carry
[$item
->getLocation()->getId()] = $item
; return $carry
;}, []);
689 //Init snippets to context
690 $this->context
['forms']['snippets'] = [];
692 //Iterate on locations
693 foreach($locations as $locationId => $location) {
695 $snippet = new Snippet();
698 $snippet->setLocale($request->getLocale());
701 $snippet->setUser($user);
703 //Set default location
704 $snippet->setLocation($doctrine->getRepository(Location
::class)->findOneById($locationId));
706 //With existing snippet
707 if (!empty($snippets[$locationId])) {
708 $snippet = $snippets[$locationId];
709 $action = $this->generateUrl('rapsys_air_snippet_edit', ['id' => $snippet->getId()]);
712 $action = $this->generateUrl('rapsys_air_snippet_add', ['location' => $locationId]);
715 //Create SnippetType form
716 $form = $this->container
->get('form.factory')->createNamed('snipped_'.$request->getLocale().'_'.$locationId, 'Rapsys\AirBundle\Form\SnippetType', $snippet, [
719 //Set the form attribute
723 //Add form to context
724 $this->context
['forms']['snippets'][$locationId] = $form->createView();
729 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+
$this->context
);