]> Raphaël G. Git Repositories - airbundle/blob - Controller/AbstractController.php
New config tree
[airbundle] / Controller / AbstractController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys AirBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\AirBundle\Controller;
13
14 use Doctrine\ORM\EntityManagerInterface;
15 use Doctrine\Persistence\ManagerRegistry;
16 use Psr\Log\LoggerInterface;
17 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
18 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
19 use Symfony\Component\Asset\PackageInterface;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
22 use Symfony\Component\Filesystem\Filesystem;
23 use Symfony\Component\Form\FormFactoryInterface;
24 use Symfony\Component\HttpFoundation\RequestStack;
25 use Symfony\Component\HttpFoundation\Response;
26 use Symfony\Component\Mailer\MailerInterface;
27 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
28 use Symfony\Component\Routing\RouterInterface;
29 use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
30 use Symfony\Contracts\Translation\TranslatorInterface;
31 use Symfony\Contracts\Service\ServiceSubscriberInterface;
32
33 use Rapsys\AirBundle\Entity\Dance;
34 use Rapsys\AirBundle\Entity\Location;
35 use Rapsys\AirBundle\Entity\Slot;
36 use Rapsys\AirBundle\Entity\User;
37 use Rapsys\AirBundle\RapsysAirBundle;
38
39 use Rapsys\PackBundle\Util\FacebookUtil;
40 use Rapsys\PackBundle\Util\ImageUtil;
41 use Rapsys\PackBundle\Util\MapUtil;
42 use Rapsys\PackBundle\Util\SluggerUtil;
43
44 /**
45 * Provides common features needed in controllers.
46 *
47 * {@inheritdoc}
48 */
49 abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface {
50 ///AuthorizationCheckerInterface instance
51 protected $checker;
52
53 ///Config array
54 protected $config;
55
56 ///ContainerInterface instance
57 protected $container;
58
59 ///Context array
60 protected $context;
61
62 ///AccessDecisionManagerInterface instance
63 protected $decision;
64
65 ///ManagerRegistry instance
66 protected $doctrine;
67
68 ///FacebookUtil instance
69 protected $facebook;
70
71 ///FormFactoryInterface instance
72 protected $factory;
73
74 ///Image util instance
75 protected $image;
76
77 ///Locale string
78 protected $locale;
79
80 ///MailerInterface instance
81 protected $mailer;
82
83 ///EntityManagerInterface instance
84 protected $manager;
85
86 ///Map util instance
87 protected $map;
88
89 ///Modified DateTime
90 protected $modified;
91
92 ///PackageInterface instance
93 protected $package;
94
95 ///DatePeriod instance
96 protected $period;
97
98 ///Request instance
99 protected $request;
100
101 ///Route string
102 protected $route;
103
104 ///Route params array
105 protected $routeParams;
106
107 ///Router instance
108 protected $router;
109
110 ///Slugger util instance
111 protected $slugger;
112
113 ///RequestStack instance
114 protected $stack;
115
116 ///Translator instance
117 protected $translator;
118
119 /**
120 * Abstract constructor
121 *
122 * @param AuthorizationCheckerInterface $checker The container instance
123 * @param ContainerInterface $container The container instance
124 * @param AccessDecisionManagerInterface $decision The decision instance
125 * @param ManagerRegistry $doctrine The doctrine instance
126 * @param FacebookUtil $facebook The facebook instance
127 * @param FormFactoryInterface $factory The factory instance
128 * @param ImageUtil $image The image instance
129 * @param MailerInterface $mailer The mailer instance
130 * @param EntityManagerInterface $manager The manager instance
131 * @param MapUtil $map The map instance
132 * @param PackageInterface $package The package instance
133 * @param RouterInterface $router The router instance
134 * @param SluggerUtil $slugger The slugger instance
135 * @param RequestStack $stack The stack instance
136 * @param TranslatorInterface $translator The translator instance
137 *
138 * @TODO move all that stuff to setSlugger('@slugger') setters with a calls: [ setSlugger: [ '@slugger' ] ] to unbload classes ???
139 * @TODO add a calls: [ ..., prepare: ['@???'] ] that do all the logic that can't be done in constructor because various things are not available
140 */
141 public function __construct(AuthorizationCheckerInterface $checker, ContainerInterface $container, AccessDecisionManagerInterface $decision, ManagerRegistry $doctrine, FacebookUtil $facebook, FormFactoryInterface $factory, ImageUtil $image, MailerInterface $mailer, EntityManagerInterface $manager, MapUtil $map, PackageInterface $package, RouterInterface $router, SluggerUtil $slugger, RequestStack $stack, TranslatorInterface $translator) {
142 //Set checker
143 $this->checker = $checker;
144
145 //Retrieve config
146 $this->config = $container->getParameter(RapsysAirBundle::getAlias());
147
148 //Set the container
149 $this->container = $container;
150
151 //Set decision
152 $this->decision = $decision;
153
154 //Set doctrine
155 $this->doctrine = $doctrine;
156
157 //Set facebook
158 $this->facebook = $facebook;
159
160 //Set factory
161 $this->factory = $factory;
162
163 //Set image
164 $this->image = $image;
165
166 //Set mailer
167 $this->mailer = $mailer;
168
169 //Set manager
170 $this->manager = $manager;
171
172 //Set map
173 $this->map = $map;
174
175 //Set package
176 $this->package = $package;
177
178 //Set period
179 $this->period = new \DatePeriod(
180 //Start from first monday of week
181 new \DateTime('Monday this week'),
182 //Iterate on each day
183 new \DateInterval('P1D'),
184 //End with next sunday and 4 weeks
185 //XXX: we can't use isGranted here as AuthenticatedVoter deny access because user is likely not authenticated yet :'(
186 new \DateTime('Monday this week + 2 week')
187 );
188
189
190 //Set router
191 $this->router = $router;
192
193 //Set slugger
194 $this->slugger = $slugger;
195
196 //Set stack
197 $this->stack = $stack;
198
199 //Set translator
200 $this->translator = $translator;
201
202 //Get main request
203 $this->request = $this->stack->getMainRequest();
204
205 //Get current locale
206 $this->locale = $this->request->getLocale();
207
208 //Set canonical
209 $canonical = null;
210
211 //Set alternates
212 $alternates = [];
213
214 //Set route
215 //TODO: default to not found route ???
216 //TODO: pour une url not found, cet attribut n'est pas défini, comment on fait ???
217 //XXX: on génère une route bidon par défaut ???
218 $this->route = $this->request->attributes->get('_route');
219
220 //Set route params
221 $this->routeParams = $this->request->attributes->get('_route_params');
222
223 //With route and routeParams
224 if ($this->route !== null && $this->routeParams !== null) {
225 //Set canonical
226 $canonical = $this->router->generate($this->route, $this->routeParams, UrlGeneratorInterface::ABSOLUTE_URL);
227
228 //Set alternates
229 $alternates = [
230 substr($this->locale, 0, 2) => [
231 'absolute' => $canonical
232 ]
233 ];
234 }
235
236 //Set the context
237 $this->context = [
238 'description' => null,
239 'section' => null,
240 'title' => null,
241 'locale' => str_replace('_', '-', $this->locale),
242 'contact' => [
243 'title' => $this->translator->trans($this->config['contact']['title']),
244 'mail' => $this->config['contact']['mail']
245 ],
246 'copy' => [
247 'by' => $this->translator->trans($this->config['copy']['by']),
248 'link' => $this->config['copy']['link'],
249 'long' => $this->translator->trans($this->config['copy']['long']),
250 'short' => $this->translator->trans($this->config['copy']['short']),
251 'title' => $this->config['copy']['title']
252 ],
253 'site' => [
254 'donate' => $this->config['site']['donate'],
255 'icon' => $this->config['site']['icon'],
256 'logo' => $this->config['site']['logo'],
257 'png' => $this->config['site']['png'],
258 'title' => $title = $this->translator->trans($this->config['site']['title']),
259 'url' => $this->router->generate($this->config['site']['url'])
260 ],
261 'canonical' => $canonical,
262 'alternates' => $alternates,
263 'facebook' => [
264 'metas' => [
265 'og:type' => 'article',
266 'og:site_name' => $title,
267 'og:url' => $canonical,
268 #'fb:admins' => $this->config['facebook']['admins'],
269 'fb:app_id' => $this->config['facebook']['apps']
270 ],
271 'texts' => [
272 $this->translator->trans($this->config['site']['title']) => [
273 'font' => 'irishgrover',
274 'size' => 110
275 ]
276 ]
277 ],
278 'forms' => []
279 ];
280 }
281
282 /**
283 * Renders a view
284 *
285 * {@inheritdoc}
286 */
287 protected function render(string $view, array $parameters = [], Response $response = null): Response {
288 //Create application form for role_guest
289 if ($this->isGranted('ROLE_GUEST')) {
290 //Without application form
291 if (empty($parameters['forms']['application'])) {
292 //Get favorites dances
293 $danceFavorites = $this->doctrine->getRepository(Dance::class)->findByUserId($this->getUser()->getId());
294
295 //Set dance default
296 $danceDefault = !empty($danceFavorites)?current($danceFavorites):null;
297
298 //Get favorites locations
299 $locationFavorites = $this->doctrine->getRepository(Location::class)->findByUserId($this->getUser()->getId());
300
301 //Set location default
302 $locationDefault = !empty($locationFavorites)?current($locationFavorites):null;
303
304 //With admin
305 if ($this->checker->isGranted('ROLE_ADMIN')) {
306 //Get dances
307 $dances = $this->doctrine->getRepository(Dance::class)->findAll();
308
309 //Get locations
310 $locations = $this->doctrine->getRepository(Location::class)->findAll();
311 //Without admin
312 } else {
313 //Restrict to favorite dances
314 $dances = $danceFavorites;
315
316 //Reset favorites
317 $danceFavorites = [];
318
319 //Restrict to favorite locations
320 $locations = $locationFavorites;
321
322 //Reset favorites
323 $locationFavorites = [];
324 }
325
326 //With session application dance id
327 if (!empty($parameters['session']['application']['dance']['id'])) {
328 //Iterate on each dance
329 foreach($dances as $dance) {
330 //Found dance
331 if ($dance->getId() == $parameters['session']['application']['dance']['id']) {
332 //Set dance as default
333 $danceDefault = $dance;
334
335 //Stop search
336 break;
337 }
338 }
339 }
340
341 //With session location id
342 //XXX: set in session controller
343 //TODO: with new findAll that key by id, it should be as simple as isset($locations[$id]) ?
344 if (!empty($parameters['session']['location']['id'])) {
345 //Iterate on each location
346 foreach($locations as $location) {
347 //Found location
348 if ($location->getId() == $parameters['session']['location']['id']) {
349 //Set location as default
350 $locationDefault = $location;
351
352 //Stop search
353 break;
354 }
355 }
356 }
357
358 //Create ApplicationType form
359 $application = $this->createForm('Rapsys\AirBundle\Form\ApplicationType', null, [
360 //Set the action
361 'action' => $this->generateUrl('rapsys_air_application_add'),
362 //Set the form attribute
363 'attr' => [ 'class' => 'col' ],
364 //Set dance choices
365 'dance_choices' => $dances,
366 //Set dance default
367 'dance_default' => $danceDefault,
368 //Set dance favorites
369 'dance_favorites' => $danceFavorites,
370 //Set location choices
371 'location_choices' => $locations,
372 //Set location default
373 'location_default' => $locationDefault,
374 //Set location favorites
375 'location_favorites' => $locationFavorites,
376 //With user
377 'user' => $this->checker->isGranted('ROLE_ADMIN'),
378 //Set user choices
379 'user_choices' => $this->doctrine->getRepository(User::class)->findChoicesAsArray(),
380 //Set default user to current
381 'user_default' => $this->getUser()->getId(),
382 //Set to session slot or evening by default
383 //XXX: default to Evening (3)
384 'slot_default' => $this->doctrine->getRepository(Slot::class)->findOneById($parameters['session']['slot']['id']??3)
385 ]);
386
387 //Add form to context
388 $parameters['forms']['application'] = $application->createView();
389 }
390 }/*
391 #XXX: removed because it fucks up the seo by displaying register and login form instead of content
392 #XXX: until we find a better way, removed !!!
393 //Create login form for anonymous
394 elseif (!$this->checker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
395 //Create LoginType form
396 $login = $this->createForm('Rapsys\UserBundle\Form\LoginType', null, [
397 //Set the action
398 'action' => $this->generateUrl('rapsys_user_login'),
399 //Disable password repeated
400 'password_repeated' => false,
401 //Set the form attribute
402 'attr' => [ 'class' => 'col' ]
403 ]);
404
405 //Add form to context
406 $parameters['forms']['login'] = $login->createView();
407
408 //Set field
409 $field = [
410 //With mail
411 'mail' => true,
412 //Without civility
413 'civility' => false,
414 //Without pseudonym
415 'pseudonym' => false,
416 //Without forename
417 'forename' => false,
418 //Without surname
419 'surname' => false,
420 //Without password
421 'password' => false,
422 //Without slug
423 'slug' => false,
424 //Without phone
425 'phone' => false
426 ];
427
428 //Create RegisterType form
429 $register = $this->createForm('Rapsys\AirBundle\Form\RegisterType', null, $field+[
430 //Set the action
431 'action' => $this->generateUrl(
432 'rapsys_user_register',
433 [
434 'mail' => $smail = $this->slugger->short(''),
435 'field' => $sfield = $this->slugger->serialize($field),
436 'hash' => $this->slugger->hash($smail.$sfield)
437 ]
438 ),
439 //Set the form attribute
440 'attr' => [ 'class' => 'col' ]
441 ]);
442
443 //Add form to context
444 $parameters['forms']['register'] = $register->createView();
445 }*/
446
447 //Without alternates
448 if (count($parameters['alternates']) <= 1) {
449 //Set routeParams
450 $routeParams = $this->routeParams;
451
452 //Iterate on locales excluding current one
453 foreach($this->config['locales'] as $locale) {
454 //With current locale
455 if ($locale !== $this->locale) {
456 //Set titles
457 $titles = [];
458
459 //Set route params locale
460 $routeParams['_locale'] = $locale;
461
462 //Iterate on other locales
463 foreach(array_diff($this->config['locales'], [$locale]) as $other) {
464 //Set other locale title
465 $titles[$other] = $this->translator->trans($this->config['languages'][$locale], [], null, $other);
466 }
467
468 //Set locale locales context
469 $parameters['alternates'][str_replace('_', '-', $locale)] = [
470 'absolute' => $this->router->generate($this->route, $routeParams, UrlGeneratorInterface::ABSOLUTE_URL),
471 'relative' => $this->router->generate($this->route, $routeParams),
472 'title' => implode('/', $titles),
473 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
474 ];
475
476 //Add shorter locale
477 if (empty($parameters['alternates'][$shortCurrent = substr($locale, 0, 2)])) {
478 //Set locale locales context
479 $parameters['alternates'][$shortCurrent] = $parameters['alternates'][str_replace('_', '-', $locale)];
480 }
481 }
482 }
483 }
484
485 //With page infos and without facebook texts
486 if (count($parameters['facebook']['texts']) <= 1 && isset($parameters['title']) && isset($this->route) && isset($this->routeParams)) {
487 //Append facebook image texts
488 $parameters['facebook']['texts'] += [
489 $parameters['title'] => [
490 'font' => 'irishgrover',
491 'align' => 'left'
492 ]/*XXX: same problem as url, too long :'(,
493 $parameters['description'] => [
494 'align' => 'right',
495 'canonical' => true,
496 'font' => 'labelleaurore',
497 'size' => 50
498 ]*/
499 ];
500
501 /*With short path info
502 We don't add this stupid url in image !!!
503 if (strlen($pathInfo = $this->router->generate($this->route, $this->routeParams)) <= 64) {
504 => [
505 'align' => 'right',
506 'canonical' => true,
507 'font' => 'labelleaurore',
508 'size' => 50
509 ]
510 }*/
511 }
512
513 //With empty locations link
514 if (empty($parameters['locations_link'])) {
515 //Set locations link
516 $parameters['locations_link'] = $this->router->generate('rapsys_air_location');
517 }
518
519 //With empty locations title
520 if (empty($parameters['locations_title'])) {
521 //Set locations title
522 $parameters['locations_title'] = $this->translator->trans('Locations', [], null, $this->locale);
523 }
524
525 //With canonical
526 if (!empty($parameters['canonical'])) {
527 //Set facebook url
528 $parameters['facebook']['metas']['og:url'] = $parameters['canonical'];
529 }
530
531 //With empty facebook title and title
532 if (empty($parameters['facebook']['metas']['og:title']) && !empty($parameters['title'])) {
533 //Set facebook title
534 $parameters['facebook']['metas']['og:title'] = $parameters['title'];
535 }
536
537 //With empty facebook description and description
538 if (empty($parameters['facebook']['metas']['og:description']) && !empty($parameters['description'])) {
539 //Set facebook description
540 $parameters['facebook']['metas']['og:description'] = $parameters['description'];
541 }
542
543 //With locale
544 if (!empty($this->locale)) {
545 //Set facebook locale
546 $parameters['facebook']['metas']['og:locale'] = $this->locale;
547
548 //With alternates
549 //XXX: locale change when fb_locale=xx_xx is provided is done in FacebookSubscriber
550 //XXX: see https://stackoverflow.com/questions/20827882/in-open-graph-markup-whats-the-use-of-oglocalealternate-without-the-locati
551 if (!empty($parameters['alternates'])) {
552 //Iterate on alternates
553 foreach($parameters['alternates'] as $lang => $alternate) {
554 if (strlen($lang) == 5) {
555 //Set facebook locale alternate
556 $parameters['facebook']['metas']['og:locale:alternate'] = str_replace('-', '_', $lang);
557 }
558 }
559 }
560 }
561
562 //Without facebook image defined and texts
563 if (empty($parameters['facebook']['metas']['og:image']) && !empty($this->request) && !empty($parameters['facebook']['texts']) && !empty($this->modified)) {
564 //Get facebook image
565 $parameters['facebook']['metas'] += $this->facebook->getImage($this->request->getPathInfo(), $parameters['facebook']['texts'], $this->modified->getTimestamp());
566 }
567
568 //Call parent method
569 return parent::render($view, $parameters, $response);
570 }
571
572 /**
573 * {@inheritdoc}
574 *
575 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
576 */
577 public static function getSubscribedServices(): array {
578 //Return subscribed services
579 return [
580 'doctrine' => ManagerRegistry::class,
581 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
582 'form.factory' => FormFactoryInterface::class,
583 'mailer.mailer' => MailerInterface::class,
584 'rapsys_air.facebook_util' => FacebookUtil::class,
585 'rapsys_pack.image_util' => ImageUtil::class,
586 'rapsys_pack.map_util' => MapUtil::class,
587 'rapsys_pack.path_package' => PackageInterface::class,
588 'rapsys_pack.slugger_util' => SluggerUtil::class,
589 'rapsys_user.access_decision_manager' => AccessDecisionManagerInterface::class,
590 'request_stack' => RequestStack::class,
591 'router' => RouterInterface::class,
592 'security.authorization_checker' => AuthorizationCheckerInterface::class,
593 'service_container' => ContainerInterface::class,
594 'translator' => TranslatorInterface::class
595 ];
596 }
597 }