X-Git-Url: https://git.rapsys.eu/airbundle/blobdiff_plain/050190bebc029d54b8aaea9d6e839962a04d1ee9..adb446c3086f97e4832043d71de83959d7769f11:/Controller/DefaultController.php diff --git a/Controller/DefaultController.php b/Controller/DefaultController.php index 3a79805..e55d6e8 100644 --- a/Controller/DefaultController.php +++ b/Controller/DefaultController.php @@ -1,213 +1,50 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace Rapsys\AirBundle\Controller; use Symfony\Bridge\Twig\Mime\TemplatedEmail; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait; -use Symfony\Component\Asset\Packages; -use Symfony\Component\Filesystem\Exception\IOExceptionInterface; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Address; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\RouterInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Rapsys\AirBundle\Entity\Application; +use Rapsys\AirBundle\Entity\Civility; use Rapsys\AirBundle\Entity\Location; use Rapsys\AirBundle\Entity\Session; -use Rapsys\AirBundle\Entity\Slot; +use Rapsys\AirBundle\Entity\Snippet; use Rapsys\AirBundle\Entity\User; use Rapsys\AirBundle\Pdf\DisputePdf; -use Rapsys\UserBundle\Utils\Slugger; - - -class DefaultController { - use ControllerTrait { - //Rename render as _render - render as protected _render; - } - - ///Config array - protected $config; - - ///Context array - protected $context; - - ///Router instance - protected $router; - - ///Translator instance - protected $translator; - - ///Packages instance - protected $asset; - - ///RequestStack instance - protected $stack; - - ///Request instance - protected $request; - - ///Locale instance - protected $locale; - - /** - * @var ContainerInterface - */ - protected $container; - - ///Facebook image array - protected $facebookImage = []; - - /** - * Inject container and translator interface - * - * @param ContainerInterface $container The container instance - * @param RouterInterface $router The router instance - * @param RequestStack $stack The request stack - * @param TranslatorInterface $translator The translator instance - */ - public function __construct(ContainerInterface $container, RouterInterface $router, RequestStack $stack, TranslatorInterface $translator, Packages $asset) { - //Retrieve config - $this->config = $container->getParameter($this->getAlias()); - - //Set the container - $this->container = $container; - - //Set the router - $this->router = $router; - - //Set the translator - $this->translator = $translator; - - //Set the asset - $this->asset = $asset; - - //Set the request stack - $this->stack = $stack; - - //Set the context - $this->context = [ - 'contact' => [ - 'title' => $translator->trans($this->config['contact']['title']), - 'mail' => $this->config['contact']['mail'] - ], - 'copy' => [ - 'by' => $translator->trans($this->config['copy']['by']), - 'link' => $this->config['copy']['link'], - 'long' => $translator->trans($this->config['copy']['long']), - 'short' => $translator->trans($this->config['copy']['short']), - 'title' => $this->config['copy']['title'] - ], - 'page' => [ - 'description' => null, - 'section' => null, - 'title' => null - ], - 'site' => [ - 'donate' => $this->config['site']['donate'], - 'ico' => $this->config['site']['ico'], - 'logo' => $this->config['site']['logo'], - 'png' => $this->config['site']['png'], - 'svg' => $this->config['site']['svg'], - 'title' => $translator->trans($this->config['site']['title']), - 'url' => $router->generate($this->config['site']['url']) - ], - 'canonical' => null, - 'alternates' => [], - 'ogps' => [ - 'type' => 'article', - 'site_name' => $this->translator->trans($this->config['site']['title']) - ], - 'facebooks' => [ - #'admins' => $this->config['facebook']['admins'], - 'app_id' => $this->config['facebook']['apps'] - ], - 'forms' => [] - ]; - - //Get current request - $this->request = $stack->getCurrentRequest(); - - //Get current locale - #$this->locale = $router->getContext()->getParameters()['_locale']; - $this->locale = $this->request->getLocale(); - - //Set translator locale - //XXX: allow LocaleSubscriber on the fly locale change for first page - $this->translator->setLocale($this->locale); - - //Iterate on locales excluding current one - foreach($this->config['locales'] as $locale) { - //Set titles - $titles = []; - - //Iterate on other locales - foreach(array_diff($this->config['locales'], [$locale]) as $other) { - $titles[$other] = $translator->trans($this->config['languages'][$locale], [], null, $other); - } - - //Get context path - $path = $router->getContext()->getPathInfo(); - - //Retrieve route matching path - $route = $router->match($path); - - //Get route name - $name = $route['_route']; - - //Unset route name - unset($route['_route']); - - //With current locale - if ($locale == $this->locale) { - //Set locale locales context - $this->context['canonical'] = $router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL); - } else { - //Set locale locales context - $this->context['alternates'][$locale] = [ - 'absolute' => $router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL), - 'relative' => $router->generate($name, ['_locale' => $locale]+$route), - 'title' => implode('/', $titles), - 'translated' => $translator->trans($this->config['languages'][$locale], [], null, $locale) - ]; - } - - //Add shorter locale - if (empty($this->context['alternates'][$shortLocale = substr($locale, 0, 2)])) { - //Set locale locales context - $this->context['alternates'][$shortLocale] = [ - 'absolute' => $router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL), - 'relative' => $router->generate($name, ['_locale' => $locale]+$route), - 'title' => implode('/', $titles), - 'translated' => $translator->trans($this->config['languages'][$locale], [], null, $locale) - ]; - } - } - } +/** + * {@inheritdoc} + */ +class DefaultController extends AbstractController { /** * The about page * * @desc Display the about informations * + * @param Request $request The request instance * @return Response The rendered view */ - public function about(): Response { + public function about(Request $request): Response { //Set page - $this->context['page']['title'] = $this->translator->trans('About'); + $this->context['title'] = $this->translator->trans('About'); //Set description - $this->context['page']['description'] = $this->translator->trans('Libre Air about'); + $this->context['description'] = $this->translator->trans('Libre Air about'); //Set keywords $this->context['keywords'] = [ @@ -219,7 +56,7 @@ class DefaultController { $response = $this->render('@RapsysAir/default/about.html.twig', $this->context); $response->setEtag(md5($response->getContent())); $response->setPublic(); - $response->isNotModified($this->request); + $response->isNotModified($request); //Return response return $response; @@ -237,10 +74,10 @@ class DefaultController { */ public function contact(Request $request, MailerInterface $mailer): Response { //Set page - $this->context['page']['title'] = $this->translator->trans('Contact'); + $this->context['title'] = $this->translator->trans('Contact'); //Set description - $this->context['page']['description'] = $this->translator->trans('Contact Libre Air'); + $this->context['description'] = $this->translator->trans('Contact Libre Air'); //Set keywords $this->context['keywords'] = [ @@ -327,10 +164,10 @@ class DefaultController { $this->denyAccessUnlessGranted('ROLE_USER', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('User')])); //Set page - $this->context['page']['title'] = $this->translator->trans('Dispute'); + $this->context['title'] = $this->translator->trans('Dispute'); //Set description - $this->context['page']['description'] = $this->translator->trans('Libre Air dispute'); + $this->context['description'] = $this->translator->trans('Libre Air dispute'); //Set keywords $this->context['keywords'] = [ @@ -440,7 +277,6 @@ class DefaultController { * @desc Display all granted sessions with an application or login form * * @param Request $request The request instance - * * @return Response The rendered view */ public function index(Request $request): Response { @@ -448,10 +284,10 @@ class DefaultController { $doctrine = $this->getDoctrine(); //Set page - $this->context['page']['title'] = $this->translator->trans('Argentine Tango in Paris'); + $this->context['title'] = $this->translator->trans('Argentine Tango in Paris'); //Set description - $this->context['page']['description'] = $this->translator->trans('Outdoor Argentine Tango session calendar in Paris'); + $this->context['description'] = $this->translator->trans('Outdoor Argentine Tango session calendar in Paris'); //Set keywords $this->context['keywords'] = [ @@ -462,9 +298,9 @@ class DefaultController { $this->translator->trans('Libre Air') ]; - //Set type + //Set facebook type //XXX: only valid for home page - $this->context['ogps']['type'] = 'website'; + $this->context['facebook']['metas']['og:type'] = 'website'; //Compute period $period = new \DatePeriod( @@ -479,7 +315,7 @@ class DefaultController { ); //Fetch calendar - $calendar = $doctrine->getRepository(Session::class)->fetchCalendarByDatePeriod($this->translator, $period, null, $request->get('session'), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED')); + $calendar = $doctrine->getRepository(Session::class)->fetchCalendarByDatePeriod($this->translator, $period, null, $request->get('session'), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $request->getLocale()); //Fetch locations //XXX: we want to display all active locations anyway @@ -504,14 +340,15 @@ class DefaultController { * * @desc Display the organizer regulation policy * + * @param Request $request The request instance * @return Response The rendered view */ - public function organizerRegulation(): Response { + public function organizerRegulation(Request $request): Response { //Set page - $this->context['page']['title'] = $this->translator->trans('Organizer regulation'); + $this->context['title'] = $this->translator->trans('Organizer regulation'); //Set description - $this->context['page']['description'] = $this->translator->trans('Libre Air organizer regulation'); + $this->context['description'] = $this->translator->trans('Libre Air organizer regulation'); //Set keywords $this->context['keywords'] = [ @@ -525,7 +362,7 @@ class DefaultController { //Set as cachable $response->setEtag(md5($response->getContent())); $response->setPublic(); - $response->isNotModified($this->request); + $response->isNotModified($request); //Return response return $response; @@ -536,14 +373,15 @@ class DefaultController { * * @desc Display the terms of service policy * + * @param Request $request The request instance * @return Response The rendered view */ - public function termsOfService(): Response { + public function termsOfService(Request $request): Response { //Set page - $this->context['page']['title'] = $this->translator->trans('Terms of service'); + $this->context['title'] = $this->translator->trans('Terms of service'); //Set description - $this->context['page']['description'] = $this->translator->trans('Libre Air terms of service'); + $this->context['description'] = $this->translator->trans('Libre Air terms of service'); //Set keywords $this->context['keywords'] = [ @@ -557,7 +395,7 @@ class DefaultController { //Set as cachable $response->setEtag(md5($response->getContent())); $response->setPublic(); - $response->isNotModified($this->request); + $response->isNotModified($request); //Return response return $response; @@ -568,14 +406,15 @@ class DefaultController { * * @desc Display the frequently asked questions * + * @param Request $request The request instance * @return Response The rendered view */ - public function frequentlyAskedQuestions(): Response { + public function frequentlyAskedQuestions(Request $request): Response { //Set page - $this->context['page']['title'] = $this->translator->trans('Frequently asked questions'); + $this->context['title'] = $this->translator->trans('Frequently asked questions'); //Set description - $this->context['page']['description'] = $this->translator->trans('Libre Air frequently asked questions'); + $this->context['description'] = $this->translator->trans('Libre Air frequently asked questions'); //Set keywords $this->context['keywords'] = [ @@ -590,441 +429,307 @@ class DefaultController { //Set as cachable $response->setEtag(md5($response->getContent())); $response->setPublic(); - $response->isNotModified($this->request); + $response->isNotModified($request); //Return response return $response; } /** - * Return the bundle alias + * List all users * - * {@inheritdoc} - */ - public function getAlias(): string { - return 'rapsys_air'; - } - - /** - * Return the facebook image + * @desc Display all user with a group listed as users * - * @desc Generate image in jpeg format or load it from cache + * @param Request $request The request instance * - * @return array The image array + * @return Response The rendered view */ - protected function getFacebookImage(): array { - //Set texts - $texts = $this->facebookImage['texts'] ?? []; - - //Set default source - $source = $this->facebookImage['source'] ?? 'png/facebook.png'; - - //Set default source - $updated = $this->facebookImage['updated'] ?? strtotime('last week'); - - //Set default destination - //XXX: format facebook.jpeg - //XXX: was facebook//..jpeg - $destination = $this->facebookImage['destination'] ?? 'facebook'.$this->request->getPathInfo().'.jpeg'; - - //Set source path - $src = $this->config['path']['public'].'/'.$source; - - //Set cache path - //XXX: remove extension and store as png anyway - $cache = $this->config['path']['cache'].'/facebook/'.substr($source, 0, strrpos($source, '.')).'.'.$this->config['facebook']['width'].'x'.$this->config['facebook']['height'].'.png'; - - //Set destination path - $dest = $this->config['path']['public'].'/'.$destination; - - //Set asset - $asset = '@RapsysAir/'.$destination; - - //With up to date generated image - if ( - is_file($dest) && - ($stat = stat($dest)) && - $stat['mtime'] >= $updated - ) { - //Get image size - list ($width, $height) = getimagesize($dest); - - //With canonical in texts - if (!empty($texts[$this->context['canonical']])) { - //Prevent canonical to finish in alt - unset($texts[$this->context['canonical']]); - } - - //Return image data - return [ - #'image' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - 'image:url' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - #'image:secure_url' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - 'image:alt' => str_replace("\n", ' ', implode(' - ', array_keys($texts))), - 'image:height' => $height, - 'image:width' => $width - ]; - //With image candidate - } elseif (is_file($src)) { - //Create image object - $image = new \Imagick(); - - //With cache image - if (is_file($cache)) { - //Read image - $image->readImage($cache); - //Without we generate it - } else { - //Check target directory - if (!is_dir($dir = dirname($cache))) { - //Create filesystem object - $filesystem = new Filesystem(); - - try { - //Create dir - //XXX: set as 0775, symfony umask (0022) will reduce rights (0755) - $filesystem->mkdir($dir, 0775); - } catch (IOExceptionInterface $e) { - //Throw error - throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e); - } - } - - //Read image - $image->readImage($src); + public function userIndex(Request $request): Response { + //Fetch doctrine + $doctrine = $this->getDoctrine(); - //Crop using aspect ratio - //XXX: for better result upload image directly in aspect ratio :) - $image->cropThumbnailImage($this->config['facebook']['width'], $this->config['facebook']['height']); + //With admin role + if ($this->isGranted('ROLE_ADMIN')) { + //Set section + $section = $this->translator->trans('Libre Air users'); - //Strip image exif data and properties - $image->stripImage(); + //Set description + $this->context['description'] = $this->translator->trans('Libre Air user list'); + //Without admin role + } else { + //Set section + $section = $this->translator->trans('Libre Air organizers'); - //Save cache image - if (!$image->writeImage($cache)) { - //Throw error - throw new \Exception(sprintf('Unable to write image "%s"', $cache)); - } - } - //Check target directory - if (!is_dir($dir = dirname($dest))) { - //Create filesystem object - $filesystem = new Filesystem(); + //Set description + $this->context['description'] = $this->translator->trans('Libre Air organizers list'); + } - try { - //Create dir - //XXX: set as 0775, symfony umask (0022) will reduce rights (0755) - $filesystem->mkdir($dir, 0775); - } catch (IOExceptionInterface $e) { - //Throw error - throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e); - } - } + //Set keywords + $this->context['keywords'] = [ + $this->translator->trans('users'), + $this->translator->trans('user list'), + $this->translator->trans('listing'), + $this->translator->trans('Libre Air') + ]; - //Get image width - $width = $image->getImageWidth(); + //Set title + $title = $this->translator->trans($this->config['site']['title']).' - '.$section; - //Get image height - $height = $image->getImageHeight(); + //Fetch users + $users = $doctrine->getRepository(User::class)->findUserGroupedByTranslatedGroup($this->translator); - //Create draw - $draw = new \ImagickDraw(); + //Compute period + $period = new \DatePeriod( + //Start from first monday of week + new \DateTime('Monday this week'), + //Iterate on each day + new \DateInterval('P1D'), + //End with next sunday and 4 weeks + new \DateTime( + $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week' + ) + ); - //Set stroke antialias - $draw->setStrokeAntialias(true); + //With admin role + if ($this->isGranted('ROLE_ADMIN')) { + //Display all users + $this->context['groups'] = $users; + //Without admin role + } else { + //Only display senior organizers + $this->context['users'] = $users[$this->translator->trans('Senior')]; + } - //Set text antialias - $draw->setTextAntialias(true); + //Fetch locations + //XXX: we want to display all active locations anyway + $locations = $doctrine->getRepository(Location::class)->findTranslatedSortedByPeriod($this->translator, $period); - //Set stroke width - $draw->setStrokeWidth($this->facebookImage['stroke']??15); + //Render the view + return $this->render('@RapsysAir/user/index.html.twig', ['title' => $title, 'section' => $section, 'locations' => $locations]+$this->context); + } - //Set font aliases - $fonts = [ - 'irishgrover' => $this->config['path']['public'].'/ttf/irishgrover.v10.ttf', - 'droidsans' => $this->config['path']['public'].'/ttf/droidsans.regular.ttf', - 'dejavusans' => $this->config['path']['public'].'/ttf/dejavusans.2.37.ttf', - 'labelleaurore' => $this->config['path']['public'].'/ttf/labelleaurore.v10.ttf' - ]; + /** + * List all sessions for the user + * + * @desc Display all sessions for the user with an application or login form + * + * @param Request $request The request instance + * @param int $id The user id + * + * @return Response The rendered view + */ + public function userView(Request $request, $id): Response { + //Fetch doctrine + $doctrine = $this->getDoctrine(); - //Set align aliases - $aligns = [ - 'left' => \Imagick::ALIGN_LEFT, - 'center' => \Imagick::ALIGN_CENTER, - 'right' => \Imagick::ALIGN_RIGHT - ]; + //Fetch user + if (empty($user = $doctrine->getRepository(User::class)->findOneById($id))) { + throw $this->createNotFoundException($this->translator->trans('Unable to find user: %id%', ['%id%' => $id])); + } - //Set default font - $defaultFont = 'dejavusans'; + //Get user token + $token = new UsernamePasswordToken($user, null, 'none', $user->getRoles()); - //Set default align - $defaultAlign = 'center'; + //Check if guest + $isGuest = $this->get('rapsys_user.access_decision_manager')->decide($token, ['ROLE_GUEST']); - //Set default size - $defaultSize = 60; + //Prevent access when not admin, user is not guest and not currently logged user + if (!$this->isGranted('ROLE_ADMIN') && empty($isGuest) && $user != $this->getUser()) { + throw $this->createAccessDeniedException($this->translator->trans('Unable to access user: %id%', ['%id%' => $id])); + } - //Set default stroke - $defaultStroke = '#00c3f9'; + //Set section + $section = $user->getPseudonym(); - //Set default fill - $defaultFill = 'white'; + //Set title + $title = $this->translator->trans($this->config['site']['title']).' - '.$section; - //Init counter - $i = 1; + //Set description + $this->context['description'] = $this->translator->trans('%pseudonym% outdoor Argentine Tango session calendar', [ '%pseudonym%' => $user->getPseudonym() ]); - //Set text count - $count = count($texts); + //Set keywords + $this->context['keywords'] = [ + $user->getPseudonym(), + $this->translator->trans('outdoor'), + $this->translator->trans('Argentine Tango'), + $this->translator->trans('calendar') + ]; - //Draw each text stroke - foreach($texts as $text => $data) { - //Set font - $draw->setFont($fonts[$data['font']??$defaultFont]); + //Compute period + $period = new \DatePeriod( + //Start from first monday of week + new \DateTime('Monday this week'), + //Iterate on each day + new \DateInterval('P1D'), + //End with next sunday and 4 weeks + new \DateTime( + $this->isGranted('IS_AUTHENTICATED_REMEMBERED')?'Monday this week + 3 week':'Monday this week + 2 week' + ) + ); - //Set font size - $draw->setFontSize($data['size']??$defaultSize); + //Fetch calendar + //TODO: highlight with current session route parameter + $calendar = $doctrine->getRepository(Session::class)->fetchUserCalendarByDatePeriod($this->translator, $period, $isGuest?$id:null, $request->get('session'), $request->getLocale()); - //Set text alignment - $draw->setTextAlignment($align = ($aligns[$data['align']??$defaultAlign])); + //Fetch locations + //XXX: we want to display all active locations anyway + $locations = $doctrine->getRepository(Location::class)->findTranslatedSortedByPeriod($this->translator, $period, $id); + + //Create user form for admin or current user + if ($this->isGranted('ROLE_ADMIN') || $user == $this->getUser()) { + //Create SnippetType form + $userForm = $this->createForm('Rapsys\AirBundle\Form\RegisterType', $user, [ + //Set action + 'action' => $this->generateUrl('rapsys_air_user_view', ['id' => $id]), + //Set the form attribute + 'attr' => [ 'class' => 'col' ], + //Set civility class + 'civility_class' => Civility::class, + //Disable mail + 'mail' => $this->isGranted('ROLE_ADMIN'), + //Disable password + 'password' => false + ]); - //Get font metrics - $metrics = $image->queryFontMetrics($draw, $text); + //Init user to context + $this->context['forms']['user'] = $userForm->createView(); - //Without y - if (empty($data['y'])) { - //Position verticaly each text evenly - $texts[$text]['y'] = $data['y'] = (($height + 100) / (count($texts) + 1) * $i) - 50; - } + //Check if submitted + if ($request->isMethod('POST')) { + //Refill the fields in case the form is not valid. + $userForm->handleRequest($request); - //Without x - if (empty($data['x'])) { - if ($align == \Imagick::ALIGN_CENTER) { - $texts[$text]['x'] = $data['x'] = $width/2; - } elseif ($align == \Imagick::ALIGN_LEFT) { - $texts[$text]['x'] = $data['x'] = 50; - } elseif ($align == \Imagick::ALIGN_RIGHT) { - $texts[$text]['x'] = $data['x'] = $width - 50; - } + //Handle invalid form + if (!$userForm->isSubmitted() || !$userForm->isValid()) { + //Render the view + return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+$this->context); } - //Center verticaly - //XXX: add ascender part then center it back by half of textHeight - //TODO: maybe add a boundingbox ??? - $texts[$text]['y'] = $data['y'] += $metrics['ascender'] - $metrics['textHeight']/2; - - //Set stroke color - $draw->setStrokeColor(new \ImagickPixel($data['stroke']??$defaultStroke)); - - //Set fill color - $draw->setFillColor(new \ImagickPixel($data['stroke']??$defaultStroke)); - - //Add annotation - $draw->annotation($data['x'], $data['y'], $text); - - //Increase counter - $i++; - } - - //Create stroke object - $stroke = new \Imagick(); - - //Add new image - $stroke->newImage($width, $height, new \ImagickPixel('transparent')); - - //Draw on image - $stroke->drawImage($draw); - - //Blur image - //XXX: blur the stroke canvas only - $stroke->blurImage(5,3); - - //Set opacity to 0.5 - //XXX: see https://www.php.net/manual/en/image.evaluateimage.php - $stroke->evaluateImage(\Imagick::EVALUATE_DIVIDE, 1.5, \Imagick::CHANNEL_ALPHA); + //Get data + $data = $userForm->getData(); - //Compose image - $image->compositeImage($stroke, \Imagick::COMPOSITE_OVER, 0, 0); + //Get manager + $manager = $doctrine->getManager(); - //Clear stroke - $stroke->clear(); + //Queue snippet save + $manager->persist($data); - //Destroy stroke - unset($stroke); + //Flush to get the ids + $manager->flush(); - //Clear draw - $draw->clear(); + //Add notice + $this->addFlash('notice', $this->translator->trans('User %id% updated', ['%id%' => $id])); - //Set text antialias - $draw->setTextAntialias(true); + //Extract and process referer + if ($referer = $request->headers->get('referer')) { + //Create referer request instance + $req = Request::create($referer); - //Draw each text - foreach($texts as $text => $data) { - //Set font - $draw->setFont($fonts[$data['font']??$defaultFont]); + //Get referer path + $path = $req->getPathInfo(); - //Set font size - $draw->setFontSize($data['size']??$defaultSize); + //Get referer query string + $query = $req->getQueryString(); - //Set text alignment - $draw->setTextAlignment($aligns[$data['align']??$defaultAlign]); + //Remove script name + $path = str_replace($request->getScriptName(), '', $path); - //Set fill color - $draw->setFillColor(new \ImagickPixel($data['fill']??$defaultFill)); + //Try with referer path + try { + //Save old context + $oldContext = $this->router->getContext(); + + //Force clean context + //XXX: prevent MethodNotAllowedException because current context method is POST in onevendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php+42 + $this->router->setContext(new RequestContext()); + + //Retrieve route matching path + $route = $this->router->match($path); + + //Reset context + $this->router->setContext($oldContext); + + //Clear old context + unset($oldContext); + + //Extract name + $name = $route['_route']; + + //Remove route and controller from route defaults + unset($route['_route'], $route['_controller']); + + //Check if user view route + if ($name == 'rapsys_air_user_view' && !empty($route['id'])) { + //Replace id + $route['id'] = $data->getId(); + //Other routes + } else { + //Set user + $route['user'] = $data->getId(); + } + + //Generate url + return $this->redirectToRoute($name, $route); + //No route matched + } catch(MethodNotAllowedException|ResourceNotFoundException $e) { + //Unset referer to fallback to default route + unset($referer); + } + } - //Add annotation - $draw->annotation($data['x'], $data['y'], $text); + //Redirect to cleanup the form + return $this->redirectToRoute('rapsys_air', ['user' => $data->getId()]); } + } - //Draw on image - $image->drawImage($draw); + //Create snippet forms for role_guest + if ($this->isGranted('ROLE_ADMIN') || ($this->isGranted('ROLE_GUEST') && $user == $this->getUser())) { + //Fetch all user snippet + $snippets = $doctrine->getRepository(Snippet::class)->findByLocaleUserId($request->getLocale(), $id); - //Strip image exif data and properties - $image->stripImage(); + //Rekey by location id + $snippets = array_reduce($snippets, function($carry, $item){$carry[$item->getLocation()->getId()] = $item; return $carry;}, []); - //Set image format - $image->setImageFormat('jpeg'); + //Init snippets to context + $this->context['forms']['snippets'] = []; - //Save image - if (!$image->writeImage($dest)) { - //Throw error - throw new \Exception(sprintf('Unable to write image "%s"', $dest)); - } + //Iterate on locations + foreach($locations as $locationId => $location) { + //Init snippet + $snippet = new Snippet(); - //Get dest stat - //TODO: see if it works every time - $stat = stat($dest); + //Set default locale + $snippet->setLocale($request->getLocale()); - //With canonical in texts - if (!empty($texts[$this->context['canonical']])) { - //Prevent canonical to finish in alt - unset($texts[$this->context['canonical']]); - } + //Set default user + $snippet->setUser($user); - //Return image data - return [ - //TODO: see if it works every time - #'image' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - 'image:url' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - #'image:secure_url' => $this->stack->getCurrentRequest()->getUriForPath($this->asset->getUrl($asset), true),#.'?fbrefresh='.$stat['mtime'], - 'image:alt' => str_replace("\n", ' ', implode(' - ', array_keys($texts))), - 'image:height' => $height, - 'image:width' => $width - ]; - } + //Set default location + $snippet->setLocation($doctrine->getRepository(Location::class)->findOneById($locationId)); - //Return empty array without image - return []; - } + //With existing snippet + if (!empty($snippets[$locationId])) { + $snippet = $snippets[$locationId]; + $action = $this->generateUrl('rapsys_air_snippet_edit', ['id' => $snippet->getId()]); + //Without snippet + } else { + $action = $this->generateUrl('rapsys_air_snippet_add', ['location' => $locationId]); + } - /** - * Renders a view - * - * {@inheritdoc} - */ - protected function render(string $view, array $parameters = [], Response $response = null): Response { - //Create application form for role_guest - if ($this->isGranted('ROLE_GUEST')) { - //Without application form - if (empty($parameters['forms']['application'])) { - //Fetch doctrine - $doctrine = $this->getDoctrine(); - - //Create ApplicationType form - $application = $this->createForm('Rapsys\AirBundle\Form\ApplicationType', null, [ + //Create SnippetType form + $form = $this->container->get('form.factory')->createNamed('snipped_'.$request->getLocale().'_'.$locationId, 'Rapsys\AirBundle\Form\SnippetType', $snippet, [ //Set the action - 'action' => $this->generateUrl('rapsys_air_application_add'), + 'action' => $action, //Set the form attribute - 'attr' => [ 'class' => 'col' ], - //Set admin - 'admin' => $this->isGranted('ROLE_ADMIN'), - //Set default user to current - 'user' => $this->getUser()->getId(), - //Set default slot to evening - //XXX: default to Evening (3) - 'slot' => $doctrine->getRepository(Slot::class)->findOneById(3) + 'attr' => [] ]); //Add form to context - $parameters['forms']['application'] = $application->createView(); + $this->context['forms']['snippets'][$locationId] = $form->createView(); } - //Create login form for anonymous - } elseif (!$this->isGranted('IS_AUTHENTICATED_REMEMBERED')) { - //Create ApplicationType form - $login = $this->createForm('Rapsys\UserBundle\Form\LoginType', null, [ - //Set the action - 'action' => $this->generateUrl('rapsys_user_login'), - //Set the form attribute - 'attr' => [ 'class' => 'col' ] - ]); - - //Add form to context - $parameters['forms']['login'] = $login->createView(); - } - - //With page infos and without facebook image - if (empty($this->facebookImage) && !empty($parameters['site']['title']) && !empty($parameters['page']['title']) && !empty($parameters['canonical'])) { - //Set facebook image - $this->facebookImage = [ - 'texts' => [ - $parameters['site']['title'] => [ - 'font' => 'irishgrover', - 'size' => 110 - ], - $parameters['page']['title'] => [ - 'align' => 'left' - ], - $parameters['canonical'] => [ - 'align' => 'right', - 'font' => 'labelleaurore', - 'size' => 50 - ] - ] - ]; - } - - //With canonical - if (!empty($parameters['canonical'])) { - //Set facebook url - $parameters['ogps']['url'] = $parameters['canonical']; } - //With page title - if (!empty($parameters['page']['title'])) { - //Set facebook title - $parameters['ogps']['title'] = $parameters['page']['title']; - } - - //With page description - if (!empty($parameters['page']['description'])) { - //Set facebook description - $parameters['ogps']['description'] = $parameters['page']['description']; - } - - //With locale - if (!empty($this->locale)) { - //Set facebook locale - $parameters['ogps']['locale'] = str_replace('-', '_', $this->locale); - - //With alternates - //XXX: disabled as we don't support fb_locale=xx_xx - //XXX: see https://stackoverflow.com/questions/20827882/in-open-graph-markup-whats-the-use-of-oglocalealternate-without-the-locati - #if (!empty($parameters['alternates'])) { - # //Iterate on alternates - # foreach($parameters['alternates'] as $lang => $alternate) { - # if (strlen($lang) == 5) { - # //Set facebook locale alternate - # $parameters['ogps']['locale:alternate'] = str_replace('-', '_', $lang); - # } - # } - #} - } - - //With facebook image defined - if (!empty($this->facebookImage)) { - //Get facebook image - $parameters['ogps'] += $this->getFacebookImage(); - } - - //Call parent method - return $this->_render($view, $parameters, $response); + //Render the view + return $this->render('@RapsysAir/user/view.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'calendar' => $calendar, 'locations' => $locations]+$this->context); } }