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 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
;
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
;
39 use Rapsys\PackBundle\Util\FacebookUtil
;
40 use Rapsys\PackBundle\Util\ImageUtil
;
41 use Rapsys\PackBundle\Util\MapUtil
;
42 use Rapsys\PackBundle\Util\SluggerUtil
;
45 * Provides common features needed in controllers.
49 abstract class AbstractController
extends BaseAbstractController
implements ServiceSubscriberInterface
{
50 ///AuthorizationCheckerInterface instance
56 ///ContainerInterface instance
62 ///AccessDecisionManagerInterface instance
65 ///ManagerRegistry instance
68 ///FacebookUtil instance
71 ///FormFactoryInterface instance
74 ///Image util instance
80 ///MailerInterface instance
83 ///EntityManagerInterface instance
92 ///PackageInterface instance
95 ///DatePeriod instance
104 ///Route params array
105 protected $routeParams;
110 ///Slugger util instance
113 ///RequestStack instance
116 ///Translator instance
117 protected $translator;
120 * Abstract constructor
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
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
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) {
143 $this->checker
= $checker;
146 $this->config
= $container->getParameter(RapsysAirBundle
::getAlias());
149 $this->container
= $container;
152 $this->decision
= $decision;
155 $this->doctrine
= $doctrine;
158 $this->facebook
= $facebook;
161 $this->factory
= $factory;
164 $this->image
= $image;
167 $this->mailer
= $mailer;
170 $this->manager
= $manager;
176 $this->package
= $package;
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')
191 $this->router
= $router;
194 $this->slugger
= $slugger;
197 $this->stack
= $stack;
200 $this->translator
= $translator;
203 $this->request
= $this->stack
->getMainRequest();
206 $this->locale
= $this->request
->getLocale();
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');
221 $this->routeParams
= $this->request
->attributes
->get('_route_params');
223 //With route and routeParams
224 if ($this->route
!== null && $this->routeParams
!== null) {
226 $canonical = $this->router
->generate($this->route
, $this->routeParams
, UrlGeneratorInterface
::ABSOLUTE_URL
);
230 substr($this->locale
, 0, 2) => [
231 'absolute' => $canonical
238 'description' => null,
241 'locale' => str_replace('_', '-', $this->locale
),
243 'title' => $this->translator
->trans($this->config
['contact']['title']),
244 'mail' => $this->config
['contact']['mail']
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']
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'])
261 'canonical' => $canonical,
262 'alternates' => $alternates,
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']
272 $this->translator
->trans($this->config
['site']['title']) => [
273 'font' => 'irishgrover',
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());
296 $danceDefault = !empty($danceFavorites)?current($danceFavorites):null;
298 //Get favorites locations
299 $locationFavorites = $this->doctrine
->getRepository(Location
::class)->findByUserId($this->getUser()->getId());
301 //Set location default
302 $locationDefault = !empty($locationFavorites)?current($locationFavorites):null;
305 if ($this->checker
->isGranted('ROLE_ADMIN')) {
307 $dances = $this->doctrine
->getRepository(Dance
::class)->findAll();
310 $locations = $this->doctrine
->getRepository(Location
::class)->findAll();
313 //Restrict to favorite dances
314 $dances = $danceFavorites;
317 $danceFavorites = [];
319 //Restrict to favorite locations
320 $locations = $locationFavorites;
323 $locationFavorites = [];
326 //With session application dance id
327 if (!empty($parameters['session']['application']['dance']['id'])) {
328 //Iterate on each dance
329 foreach($dances as $dance) {
331 if ($dance->getId() == $parameters['session']['application']['dance']['id']) {
332 //Set dance as default
333 $danceDefault = $dance;
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) {
348 if ($location->getId() == $parameters['session']['location']['id']) {
349 //Set location as default
350 $locationDefault = $location;
358 //Create ApplicationType form
359 $application = $this->createForm('Rapsys\AirBundle\Form\ApplicationType', null, [
361 'action' => $this->generateUrl('rapsys_air_application_add'),
362 //Set the form attribute
363 'attr' => [ 'class' => 'col' ],
365 'dance_choices' => $dances,
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,
377 'user' => $this->checker
->isGranted('ROLE_ADMIN'),
379 'user_choices' => $this->doctrine
->getRepository(User
::class)->findIndexByGroupPseudonym(),
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)
387 //Add form to context
388 $parameters['forms']['application'] = $application->createView();
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, [
398 'action' => $this->generateUrl('rapsys_user_login'),
399 //Disable password repeated
400 'password_repeated' => false,
401 //Set the form attribute
402 'attr' => [ 'class' => 'col' ]
405 //Add form to context
406 $parameters['forms']['login'] = $login->createView();
415 'pseudonym' => false,
428 //Create RegisterType form
429 $register = $this->createForm('Rapsys\AirBundle\Form\RegisterType', null, $field+[
431 'action' => $this->generateUrl(
432 'rapsys_user_register',
434 'mail' => $smail = $this->slugger->short(''),
435 'field' => $sfield = $this->slugger->serialize($field),
436 'hash' => $this->slugger->hash($smail.$sfield)
439 //Set the form attribute
440 'attr' => [ 'class' => 'col' ]
443 //Add form to context
444 $parameters['forms']['register'] = $register->createView();
448 if (count($parameters['alternates']) <= 1) {
450 $routeParams = $this->routeParams
;
452 //Iterate on locales excluding current one
453 foreach($this->config
['locales'] as $locale) {
454 //With current locale
455 if ($locale !== $this->locale
) {
459 //Set route params locale
460 $routeParams['_locale'] = $locale;
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);
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)
477 if (empty($parameters['alternates'][$shortCurrent = substr($locale, 0, 2)])) {
478 //Set locale locales context
479 $parameters['alternates'][$shortCurrent] = $parameters['alternates'][str_replace('_', '-', $locale)];
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'] => [
491 ]/*XXX: same problem as url, too long :'(,
492 $parameters['description'] => [
495 'font' => 'labelleaurore',
500 /*With short path info
501 We don't add this stupid url in image !!!
502 if (strlen($pathInfo = $this->router->generate($this->route, $this->routeParams)) <= 64) {
506 'font' => 'labelleaurore',
512 //With empty locations link
513 if (empty($parameters['locations_link'])) {
515 $parameters['locations_link'] = $this->router
->generate('rapsys_air_location');
518 //With empty locations title
519 if (empty($parameters['locations_title'])) {
520 //Set locations title
521 $parameters['locations_title'] = $this->translator
->trans('Locations', [], null, $this->locale
);
525 if (!empty($parameters['canonical'])) {
527 $parameters['facebook']['metas']['og:url'] = $parameters['canonical'];
530 //With empty facebook title and title
531 if (empty($parameters['facebook']['metas']['og:title']) && !empty($parameters['title'])) {
533 $parameters['facebook']['metas']['og:title'] = $parameters['title'];
536 //With empty facebook description and description
537 if (empty($parameters['facebook']['metas']['og:description']) && !empty($parameters['description'])) {
538 //Set facebook description
539 $parameters['facebook']['metas']['og:description'] = $parameters['description'];
543 if (!empty($this->locale
)) {
544 //Set facebook locale
545 $parameters['facebook']['metas']['og:locale'] = $this->locale
;
548 //XXX: locale change when fb_locale=xx_xx is provided is done in FacebookSubscriber
549 //XXX: see https://stackoverflow.com/questions/20827882/in-open-graph-markup-whats-the-use-of-oglocalealternate-without-the-locati
550 if (!empty($parameters['alternates'])) {
551 //Iterate on alternates
552 foreach($parameters['alternates'] as $lang => $alternate) {
553 if (strlen($lang) == 5) {
554 //Set facebook locale alternate
555 $parameters['facebook']['metas']['og:locale:alternate'] = str_replace('-', '_', $lang);
561 //Without facebook image defined and texts
562 if (empty($parameters['facebook']['metas']['og:image']) && !empty($this->request
) && !empty($parameters['facebook']['texts']) && !empty($this->modified
)) {
564 $parameters['facebook']['metas'] +
= $this->facebook
->getImage($this->request
->getPathInfo(), $parameters['facebook']['texts'], $this->modified
->getTimestamp());
568 return parent
::render($view, $parameters, $response);
574 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
576 public static function getSubscribedServices(): array {
577 //Return subscribed services
579 'doctrine' => ManagerRegistry
::class,
580 'doctrine.orm.default_entity_manager' => EntityManagerInterface
::class,
581 'form.factory' => FormFactoryInterface
::class,
582 'mailer.mailer' => MailerInterface
::class,
583 'rapsys_air.facebook_util' => FacebookUtil
::class,
584 'rapsys_pack.image_util' => ImageUtil
::class,
585 'rapsys_pack.map_util' => MapUtil
::class,
586 'rapsys_pack.path_package' => PackageInterface
::class,
587 'rapsys_pack.slugger_util' => SluggerUtil
::class,
588 'rapsys_user.access_decision_manager' => AccessDecisionManagerInterface
::class,
589 'request_stack' => RequestStack
::class,
590 'router' => RouterInterface
::class,
591 'security.authorization_checker' => AuthorizationCheckerInterface
::class,
592 'service_container' => ContainerInterface
::class,
593 'translator' => TranslatorInterface
::class