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\Filesystem\Exception\IOExceptionInterface
; 
  16 use Symfony\Component\Filesystem\Filesystem
; 
  17 use Symfony\Component\Form\FormError
; 
  18 use Symfony\Component\HttpFoundation\Request
; 
  19 use Symfony\Component\HttpFoundation\Response
; 
  20 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException
; 
  21 use Symfony\Component\Mailer\Exception\TransportExceptionInterface
; 
  22 use Symfony\Component\Mime\Address
; 
  23 use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken
; 
  24 use Symfony\Component\Security\Core\Exception\AccessDeniedException
; 
  26 use Rapsys\AirBundle\Entity\Dance
; 
  27 use Rapsys\AirBundle\Entity\Location
; 
  28 use Rapsys\AirBundle\Entity\Session
; 
  29 use Rapsys\AirBundle\Entity\Snippet
; 
  30 use Rapsys\AirBundle\Entity\User
; 
  31 use Rapsys\AirBundle\Pdf\DisputePdf
; 
  36 class DefaultController 
extends AbstractController 
{ 
  40          * @desc Display the about informations 
  42          * @param Request $request The request instance 
  43          * @return Response The rendered view 
  45         public function about(Request 
$request): Response 
{ 
  47                 $this->context
['title'] = $this->translator
->trans('About'); 
  50                 $this->context
['description'] = $this->translator
->trans('Libre Air about'); 
  53                 $this->context
['keywords'] = [ 
  54                         $this->translator
->trans('about'), 
  55                         $this->translator
->trans('Libre Air') 
  59                 $response = $this->render('@RapsysAir/default/about.html.twig', $this->context
); 
  60                 $response->setEtag(md5($response->getContent())); 
  61                 $response->setPublic(); 
  62                 $response->isNotModified($request); 
  71          * @desc Send a contact mail to configured contact 
  73          * @param Request $request The request instance 
  75          * @return Response The rendered view or redirection 
  77         public function contact(Request 
$request): Response 
{ 
  79                 $this->context
['title'] = $this->translator
->trans('Contact'); 
  82                 $this->context
['description'] = $this->translator
->trans('Contact Libre Air'); 
  85                 $this->context
['keywords'] = [ 
  86                         $this->translator
->trans('contact'), 
  87                         $this->translator
->trans('Libre Air'), 
  88                         $this->translator
->trans('outdoor'), 
  89                         $this->translator
->trans('Argentine Tango'), 
  90                         $this->translator
->trans('calendar') 
  97                 if ($user = $this->getUser()) { 
 100                                 'name' => $user->getRecipientName(), 
 101                                 'mail' => $user->getMail() 
 105                 //Create the form according to the FormType created previously. 
 106                 //And give the proper parameters 
 107                 $form = $this->createForm('Rapsys\AirBundle\Form\ContactType', $data, [ 
 108                         'action' => $this->generateUrl('rapsys_air_contact'), 
 112                 if ($request->isMethod('POST')) { 
 113                         // Refill the fields in case the form is not valid. 
 114                         $form->handleRequest($request); 
 116                         if ($form->isSubmitted() && $form->isValid()) { 
 118                                 $data = $form->getData(); 
 121                                 $message = (new TemplatedEmail()) 
 123                                         ->from(new Address($data['mail'], $data['name'])) 
 125                                         ->to(new Address($this->context
['contact']['mail'], $this->context
['contact']['title'])) 
 127                                         ->subject($data['subject']) 
 129                                         //Set path to twig templates 
 130                                         ->htmlTemplate('@RapsysAir/mail/contact.html.twig') 
 131                                         ->textTemplate('@RapsysAir/mail/contact.text.twig') 
 136                                                         'subject' => $data['subject'], 
 137                                                         'message' => strip_tags($data['message']), 
 141                                 //Try sending message 
 142                                 //XXX: mail delivery may silently fail 
 145                                         $this->mailer
->send($message); 
 147                                         //Redirect on the same route with sent=1 to cleanup form 
 148                                         return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params')); 
 149                                 //Catch obvious transport exception 
 150                                 } catch(TransportExceptionInterface 
$e) { 
 151                                         if ($message = $e->getMessage()) { 
 152                                                 //Add error message mail unreachable 
 153                                                 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->context
['contact']['mail'], '%message%' => $this->translator
->trans($message)]))); 
 155                                                 //Add error message mail unreachable 
 156                                                 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to contact: %mail%', ['%mail%' => $this->context
['contact']['mail']]))); 
 163                 return $this->render('@RapsysAir/form/contact.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
); 
 169          * @desc Generate a dispute document 
 171          * @param Request $request The request instance 
 173          * @return Response The rendered view or redirection 
 175         public function dispute(Request 
$request): Response 
{ 
 176                 //Prevent non-guest to access here 
 177                 $this->denyAccessUnlessGranted('ROLE_USER', null, $this->translator
->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator
->trans('User')])); 
 180                 $this->context
['title'] = $this->translator
->trans('Dispute'); 
 183                 $this->context
['description'] = $this->translator
->trans('Libre Air dispute'); 
 186                 $this->context
['keywords'] = [ 
 187                         $this->translator
->trans('dispute'), 
 188                         $this->translator
->trans('Libre Air'), 
 189                         $this->translator
->trans('outdoor'), 
 190                         $this->translator
->trans('Argentine Tango'), 
 191                         $this->translator
->trans('calendar') 
 194                 //Create the form according to the FormType created previously. 
 195                 //And give the proper parameters 
 196                 $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.'], [ 
 197                         'action' => $this->generateUrl('rapsys_air_dispute'), 
 201                 if ($request->isMethod('POST')) { 
 202                         // Refill the fields in case the form is not valid. 
 203                         $form->handleRequest($request); 
 205                         if ($form->isValid()) { 
 207                                 $data = $form->getData(); 
 210                                 if (!empty($data['offense']) && $data['offense'] == 'gathering') { 
 212                                         $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()); 
 214                                 } elseif (!empty($data['offense'] && $data['offense'] == 'traffic')) { 
 216                                         $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()); 
 217                                 //Unsupported offense 
 219                                         header('Content-Type: text/plain'); 
 224                                 //Send common headers 
 225                                 header('Content-Type: application/pdf'); 
 227                                 //Send remaining headers 
 228                                 header('Cache-Control: private, max-age=0, must-revalidate'); 
 229                                 header('Pragma: public'); 
 231                                 //Send content-length 
 232                                 header('Content-Length: '.strlen($output)); 
 241 #                               $message = (new TemplatedEmail()) 
 243 #                                       ->from(new Address($data['mail'], $data['name'])) 
 245 #                                       //XXX: remove the debug set in vendor/symfony/mime/Address.php +46 
 246 #                                       ->to(new Address($this->config['contact']['mail'], $this->config['contact']['title'])) 
 248 #                                       ->subject($data['subject']) 
 250 #                                       //Set path to twig templates 
 251 #                                       ->htmlTemplate('@RapsysAir/mail/contact.html.twig') 
 252 #                                       ->textTemplate('@RapsysAir/mail/contact.text.twig') 
 257 #                                                       'subject' => $data['subject'], 
 258 #                                                       'message' => strip_tags($data['message']), 
 262 #                               //Try sending message 
 263 #                               //XXX: mail delivery may silently fail 
 266 #                                       $this->mailer->send($message); 
 268 #                                       //Redirect on the same route with sent=1 to cleanup form 
 269 #                                       return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params')); 
 270 #                               //Catch obvious transport exception 
 271 #                               } catch(TransportExceptionInterface $e) { 
 272 #                                       if ($message = $e->getMessage()) { 
 273 #                                               //Add error message mail unreachable 
 274 #                                               $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%: %message%', ['%mail%' => $this->config['contact']['mail'], '%message%' => $this->translator->trans($message)]))); 
 276 #                                               //Add error message mail unreachable 
 277 #                                               $form->get('mail')->addError(new FormError($this->translator->trans('Unable to contact: %mail%', ['%mail%' => $this->config['contact']['mail']]))); 
 284                 return $this->render('@RapsysAir/default/dispute.html.twig', ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->context
); 
 290          * Display session calendar 
 292          * @param Request $request The request instance 
 293          * @return Response The rendered view 
 295         public function index(Request 
$request): Response 
{ 
 297                 $this->context
['cities'] = $this->doctrine
->getRepository(Location
::class)->findCitiesAsArray($this->period
); 
 300                 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsCalendarArray($this->period
, !$this->isGranted('IS_AUTHENTICATED_REMEMBERED')); 
 303                 $this->context
['dances'] = $this->doctrine
->getRepository(Dance
::class)->findNamesAsArray(); 
 306                 $this->modified 
= max(array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['cities'], $this->context
['dances']))); 
 309                 $response = new Response(); 
 312                 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) { 
 314                         $response->setLastModified(new \
DateTime('-1 year')); 
 317                         $response->setPrivate(); 
 318                 //Without logged user 
 321                         //XXX: only for public to force revalidation by last modified 
 322                         $response->setEtag(md5(serialize(array_merge($this->context
['calendar'], $this->context
['cities'], $this->context
['dances'])))); 
 325                         $response->setLastModified($this->modified
); 
 328                         $response->setPublic(); 
 330                         //Without role and modification 
 331                         if ($response->isNotModified($request)) { 
 332                                 //Return 304 response 
 338                 if (!empty($this->context
['cities'])) { 
 342                         //Iterate on each cities 
 343                         foreach($this->context
['cities'] as $city) { 
 344                                 //Iterate on each locations 
 345                                 foreach($city['locations'] as $location) { 
 347                                         $locations[$location['id']] = $location; 
 352                         $this->context
['multimap'] = $this->map
->getMultiMap($this->translator
->trans('Libre Air cities sector map'), $this->modified
->getTimestamp(), $locations); 
 355                         $cities = array_map(function ($v) { return $v
['in']; }, $this->context
['cities']); 
 358                         $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']); 
 368                 //TODO: use splice instead of that shit !!! 
 369                 //TODO: handle smartly indoor and outdoor !!! 
 370                 $this->context
['keywords'] = array_values( 
 375                                         $this->translator
->trans('indoor'), 
 376                                         $this->translator
->trans('outdoor'), 
 377                                         $this->translator
->trans('calendar'), 
 378                                         $this->translator
->trans('Libre Air') 
 384                 $cities = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($cities, 0, -1))], array_slice($cities, -1)), 'strlen')); 
 387                 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen')); 
 390                 $this->context
['title'] = $this->translator
->trans('%dances% %cities%', ['%dances%' => $dances, '%cities%' => $cities]); 
 393                 //TODO: handle french translation when city start with a A, change à in en ! 
 394                 $this->context
['description'] = $this->translator
->trans('%dances% indoor and outdoor calendar %cities%', ['%dances%' => $dances, '%cities%' => $cities]); 
 397                 //XXX: only valid for home page 
 398                 $this->context
['facebook']['metas']['og:type'] = 'website'; 
 401                 return $this->render('@RapsysAir/default/index.html.twig', $this->context
, $response); 
 405          * The organizer regulation page 
 407          * @desc Display the organizer regulation policy 
 409          * @param Request $request The request instance 
 410          * @return Response The rendered view 
 412         public function organizerRegulation(Request 
$request): Response 
{ 
 414                 $this->context
['title'] = $this->translator
->trans('Organizer regulation'); 
 417                 $this->context
['description'] = $this->translator
->trans('Libre Air organizer regulation'); 
 420                 $this->context
['keywords'] = [ 
 421                         $this->translator
->trans('organizer regulation'), 
 422                         $this->translator
->trans('Libre Air') 
 426                 $response = $this->render('@RapsysAir/default/organizer_regulation.html.twig', $this->context
); 
 429                 $response->setEtag(md5($response->getContent())); 
 430                 $response->setPublic(); 
 431                 $response->isNotModified($request); 
 438          * The terms of service page 
 440          * @desc Display the terms of service policy 
 442          * @param Request $request The request instance 
 443          * @return Response The rendered view 
 445         public function termsOfService(Request 
$request): Response 
{ 
 447                 $this->context
['title'] = $this->translator
->trans('Terms of service'); 
 450                 $this->context
['description'] = $this->translator
->trans('Libre Air terms of service'); 
 453                 $this->context
['keywords'] = [ 
 454                         $this->translator
->trans('terms of service'), 
 455                         $this->translator
->trans('Libre Air') 
 459                 $response = $this->render('@RapsysAir/default/terms_of_service.html.twig', $this->context
); 
 462                 $response->setEtag(md5($response->getContent())); 
 463                 $response->setPublic(); 
 464                 $response->isNotModified($request); 
 471          * The frequently asked questions page 
 473          * @desc Display the frequently asked questions 
 475          * @param Request $request The request instance 
 476          * @return Response The rendered view 
 478         public function frequentlyAskedQuestions(Request 
$request): Response 
{ 
 480                 $this->context
['title'] = $this->translator
->trans('Frequently asked questions'); 
 483                 $this->context
['description'] = $this->translator
->trans('Libre Air frequently asked questions'); 
 486                 $this->context
['keywords'] = [ 
 487                         $this->translator
->trans('frequently asked questions'), 
 488                         $this->translator
->trans('faq'), 
 489                         $this->translator
->trans('Libre Air') 
 493                 $response = $this->render('@RapsysAir/default/frequently_asked_questions.html.twig', $this->context
); 
 496                 $response->setEtag(md5($response->getContent())); 
 497                 $response->setPublic(); 
 498                 $response->isNotModified($request); 
 507          * @desc Display all user with a group listed as users 
 509          * @param Request $request The request instance 
 511          * @return Response The rendered view 
 513         public function userIndex(Request 
$request): Response 
{ 
 515                 if ($this->isGranted('ROLE_ADMIN')) { 
 517                         $section = $this->translator
->trans('Libre Air users'); 
 520                         $this->context
['description'] = $this->translator
->trans('Libre Air user list'); 
 524                         $section = $this->translator
->trans('Libre Air organizers'); 
 527                         $this->context
['description'] = $this->translator
->trans('Libre Air organizers list'); 
 531                 $this->context
['keywords'] = [ 
 532                         $this->translator
->trans('users'), 
 533                         $this->translator
->trans('user list'), 
 534                         $this->translator
->trans('listing'), 
 535                         $this->translator
->trans('Libre Air') 
 539                 $title = $this->translator
->trans($this->config
['site']['title']).' - '.$section; 
 542                 $users = $this->doctrine
->getRepository(User
::class)->findIndexByGroupId(); 
 545                 if ($this->isGranted('ROLE_ADMIN')) { 
 547                         $this->context
['groups'] = $users; 
 550                         //Only display senior organizers 
 551                         $this->context
['users'] = $users[$this->translator
->trans('Senior')]; 
 555                 return $this->render('@RapsysAir/user/index.html.twig', ['title' => $title, 'section' => $section]+
$this->context
); 
 559          * List all sessions for the user 
 561          * @desc Display all sessions for the user with an application or login form 
 563          * @param Request $request The request instance 
 564          * @param int $id The user id 
 566          * @return Response The rendered view 
 568         public function userView(Request 
$request, int $id, ?string $user): Response 
{ 
 570                 if (empty($this->context
['user'] = $this->doctrine
->getRepository(User
::class)->findOneByIdAsArray($id, $this->locale
))) { 
 572                         throw new NotFoundHttpException($this->translator
->trans('Unable to find user: %id%', ['%id%' => $id])); 
 576                 $token = new AnonymousToken('', $this->context
['user']['mail'], $this->context
['user']['roles']); 
 578                 //Prevent access when not admin, user is not guest and not currently logged user 
 579                 if (!($isAdmin = $this->isGranted('ROLE_ADMIN')) && !($isGuest = $this->decision
->decide($token, ['ROLE_GUEST']))) { 
 580                         //Throw access denied 
 581                         throw new AccessDeniedException($this->translator
->trans('Unable to access user: %id%', ['%id%' => $id])); 
 584                 //With invalid user slug 
 585                 if ($this->context
['user']['slug'] !== $user) { 
 586                         //Redirect to cleaned url 
 587                         return $this->redirectToRoute('rapsys_air_user_view', ['id' => $id, 'user' => $this->context
['user']['slug']]); 
 591                 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsCalendarArray($this->period
, !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), null, null, $id); 
 593                 //Get locations at less than 2 km 
 594                 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllByUserIdAsArray($id, $this->period
, 2); 
 617                 //Iterate on each calendar 
 618                 foreach($this->context
['calendar'] as $date => $calendar) { 
 619                         //Iterate on each session 
 620                         foreach($calendar['sessions'] as $sessionId => $session) { 
 622                                 $dances[$session['application']['dance']['name']] = $session['application']['dance']['name']; 
 625                                 $types[$session['application']['dance']['type']] = lcfirst($session['application']['dance']['type']); 
 628                                 $indoors[$session['location']['indoor']?'indoor':'outdoor'] = $this->translator
->trans($session['location']['indoor']?'indoor':'outdoor'); 
 631                                 $insides[$session['location']['indoor']?'inside':'outside'] = $this->translator
->trans($session['location']['indoor']?'inside':'outside'); 
 634                                 $ats[$session['location']['id']] = $session['location']['at']; 
 637                                 $ins[$session['location']['id']] = $session['location']['in']; 
 639                                 //Session with application user id 
 640                                 if (!empty($session['application']['user']['id']) && $session['application']['user']['id'] == $id) { 
 642                                         $locations[$session['location']['id']] = $session['location']; 
 648                 //XXX: dance modified is already computed inside calendar modified 
 649                 $this->modified 
= max(array_merge([$this->context
['user']['modified']], array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['locations'])))); 
 652                 $response = new Response(); 
 655                 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) { 
 657                         $response->setLastModified(new \
DateTime('-1 year')); 
 660                         $response->setPrivate(); 
 661                 //Without logged user 
 664                         //XXX: only for public to force revalidation by last modified 
 665                         $response->setEtag(md5(serialize(array_merge($this->context
['user'], $this->context
['calendar'], $this->context
['locations'])))); 
 668                         $response->setLastModified($this->modified
); 
 671                         $response->setPublic(); 
 673                         //Without role and modification 
 674                         if ($response->isNotModified($request)) { 
 675                                 //Return 304 response 
 681                 $this->context
['multimap'] = $this->map
->getMultiMap($this->context
['user']['multimap'], $this->modified
->getTimestamp(), $this->context
['locations']); 
 684                 $this->context
['keywords'] = [ 
 685                         $this->context
['user']['pseudonym'], 
 686                         $this->translator
->trans('calendar'), 
 687                         $this->translator
->trans('Libre Air') 
 691                 $cities = array_unique(array_map(function ($v) { return $v
['city']; }, $locations)); 
 694                 $titles = array_map(function ($v) { return $v
['title']; }, $locations); 
 696                 //Insert dances in keywords 
 697                 array_splice($this->context
['keywords'], 1, 0, array_merge($types, $dances, $indoors, $insides, $titles, $cities)); 
 700                 $ins = array_unique($ins); 
 703                 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen')); 
 706                 $types = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($types, 0, -1))], array_slice($types, -1)), 'strlen')); 
 708                 //Get textual indoors 
 709                 $indoors = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($indoors, 0, -1))], array_slice($indoors, -1)), 'strlen')); 
 712                 $ats = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($ats, 0, -1))], array_slice($ats, -1)), 'strlen')); 
 715                 $ins = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($ins, 0, -1))], array_slice($ins, -1)), 'strlen')); 
 718                 $this->context
['title'] = $this->translator
->trans('%pseudonym% organizer', ['%pseudonym%' => $this->context
['user']['pseudonym']]); 
 721                 if (!empty($locations)) { 
 723                         $this->context
['description'] = ucfirst($this->translator
->trans('%dances% %types% %indoors% calendar %ats% %ins% %pseudonym%', ['%dances%' => $dances, '%types%' => $types, '%indoors%' => $indoors, '%ats%' => $ats, '%ins%' => $ins, '%pseudonym%' => $this->translator
->trans('by %pseudonym%', ['%pseudonym%' => $this->context
['user']['pseudonym']])])); 
 727                         $this->context
['description'] = $this->translator
->trans('%pseudonym% calendar', ['%pseudonym%' => $this->context
['user']['pseudonym']]); 
 730                 //Set user description 
 731                 $this->context
['locations_description'] = $this->translator
->trans('Libre Air %pseudonym% location list', ['%pseudonym%' => $this->translator
->trans('by %pseudonym%', ['%pseudonym%' => $this->context
['user']['pseudonym']])]); 
 734                 $this->context
['alternates'] +
= $this->context
['user']['alternates']; 
 736                 //Create snippet forms for role_guest 
 737                 //TODO: optimize this call 
 738                 if ($isAdmin || $isGuest && $this->getUser() && $this->context
['user']['id'] == $this->getUser()->getId()) { 
 739                         //Fetch all user snippet 
 740                         $snippets = $this->doctrine
->getRepository(Snippet
::class)->findByUserIdLocaleIndexByLocationId($id, $this->locale
); 
 743                         $user = $this->doctrine
->getRepository(User
::class)->findOneById($id); 
 745                         //Iterate on locations 
 746                         foreach($this->context
['locations'] as $locationId => $location) { 
 747                                 //With existing snippet 
 748                                 if (isset($snippets[$location['id']])) { 
 749                                         //Set existing in current 
 750                                         $current = $snippets[$location['id']]; 
 751                                 //Without existing snippet 
 754                                         $current = new Snippet(); 
 757                                         $current->setLocale($this->locale
); 
 760                                         $current->setUser($user); 
 762                                         //Set default location 
 763                                         $current->setLocation($this->doctrine
->getRepository(Location
::class)->findOneById($location['id'])); 
 766                                 //Create SnippetType form 
 767                                 $form = $this->factory
->createNamed( 
 769                                         'snippet_'.$locationId.'_'.$id.'_'.$this->locale
, 
 771                                         'Rapsys\AirBundle\Form\SnippetType', 
 776                                 //Refill the fields in case of invalid form 
 777                                 $form->handleRequest($request); 
 779                                 //Handle submitted and valid form 
 780                                 //TODO: add a delete snippet ? 
 781                                 if ($form->isSubmitted() && $form->isValid()) { 
 783                                         $snippet = $form->getData(); 
 786                                         $this->manager
->persist($snippet); 
 788                                         //Flush to get the ids 
 789                                         $this->manager
->flush(); 
 792                                         $this->addFlash('notice', $this->translator
->trans('Snippet for %user% %location% updated', ['%location%' => $location['at'], '%user%' => $this->context
['user']['pseudonym']])); 
 794                                         //Redirect to cleaned url 
 795                                         return $this->redirectToRoute('rapsys_air_user_view', ['id' => $id, 'user' => $this->context
['user']['slug']]); 
 798                                 //Add form to context 
 799                                 $this->context
['forms']['snippets'][$locationId] = $form->createView(); 
 801                                 //With location user source image 
 802                                 if (($isFile = is_file($source = $this->config
['path'].'/location/'.$location['id'].'/'.$id.'.png')) && ($mtime = stat($source)['mtime'])) { 
 804                                         $this->context
['locations'][$locationId]['image'] = $this->image
->getThumb($location['miniature'], $mtime, $source); 
 807                                 //Create ImageType form 
 808                                 $form = $this->factory
->createNamed( 
 810                                         'image_'.$locationId.'_'.$id, 
 812                                         'Rapsys\AirBundle\Form\ImageType', 
 816                                                 'location' => $location['id'], 
 820                                         //Set form attributes 
 822                                                 //Enable delete with image 
 823                                                 'delete' => isset($this->context
['locations'][$locationId]['image']) 
 827                                 //Refill the fields in case of invalid form 
 828                                 $form->handleRequest($request); 
 830                                 //Handle submitted and valid form 
 831                                 if ($form->isSubmitted() && $form->isValid()) { 
 833                                         if ($form->has('delete') && $form->get('delete')->isClicked()) { 
 834                                                 //With source and mtime 
 835                                                 if ($isFile && !empty($source) && !empty($mtime)) { 
 837                                                         $this->image
->remove($mtime, $source); 
 840                                                         unlink($this->config
['path'].'/location/'.$location['id'].'/'.$id.'.png'); 
 843                                                         $this->addFlash('notice', $this->translator
->trans('Image for %user% %location% deleted', ['%location%' => $location['at'], '%user%' => $this->context
['user']['pseudonym']])); 
 845                                                         //Redirect to cleaned url 
 846                                                         return $this->redirectToRoute('rapsys_air_user_view', ['id' => $id, 'user' => $this->context
['user']['slug']]); 
 851                                         if ($image = $form->get('image')->getData()) { 
 853                                                 if (!is_dir($dir = dirname($source))) { 
 854                                                         //Create filesystem object 
 855                                                         $filesystem = new Filesystem(); 
 859                                                                 //XXX: set as 0775, symfony umask (0022) will reduce rights (0755) 
 860                                                                 $filesystem->mkdir($dir, 0775); 
 861                                                         } catch (IOExceptionInterface 
$e) { 
 863                                                                 throw new \
Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e); 
 868                                                 $source = realpath($dir).'/'.basename($source); 
 870                                                 //Create imagick object 
 871                                                 $imagick = new \
Imagick(); 
 874                                                 $imagick->readImage($image->getRealPath()); 
 877                                                 if (!$imagick->writeImage($source)) { 
 879                                                         throw new \
Exception(sprintf('Unable to write image "%s"', $source)); 
 883                                                 $this->addFlash('notice', $this->translator
->trans('Image for %user% %location% updated', ['%location%' => $location['at'], '%user%' => $this->context
['user']['pseudonym']])); 
 885                                                 //Redirect to cleaned url 
 886                                                 return $this->redirectToRoute('rapsys_air_user_view', ['id' => $id, 'user' => $this->context
['user']['slug']]); 
 890                                 //Add form to context 
 891                                 $this->context
['forms']['images'][$locationId] = $form->createView(); 
 896                 return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id]+
$this->context
);