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
);