* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Rapsys\UserBundle\Controller; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController; use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Environment; use Rapsys\PackBundle\Util\SluggerUtil; use Rapsys\UserBundle\RapsysUserBundle; /** * Provides common features needed in controllers. * * {@inheritdoc} */ abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface { /** * Config array */ protected array $config; /** * Context array */ protected array $context; /** * Limit integer */ protected int $limit; /** * Locale string */ protected string $locale; /** * Page integer */ protected int $page; /** * AuthorizationCheckerInterface instance */ protected AuthorizationCheckerInterface $checker; /** * ManagerRegistry instance */ protected ManagerRegistry $doctrine; /** * UserPasswordHasherInterface instance */ protected UserPasswordHasherInterface $hasher; /** * LoggerInterface instance */ protected LoggerInterface $logger; /** * MailerInterface instance */ protected MailerInterface $mailer; /** * EntityManagerInterface instance */ protected EntityManagerInterface $manager; /** * Request instance */ protected Request $request; /** * Router instance */ protected RouterInterface $router; /** * Security instance */ protected Security $security; /** * Slugger util instance */ protected SluggerUtil $slugger; /** * Translator instance */ protected TranslatorInterface $translator; /** * Twig\Environment instance */ protected Environment $twig; /** * Abstract constructor * * @param AuthorizationCheckerInterface $checker The checker instance * @param ContainerInterface $container The container instance * @param ManagerRegistry $doctrine The doctrine instance * @param UserPasswordHasherInterface $hasher The password hasher instance * @param LoggerInterface $logger The logger instance * @param MailerInterface $mailer The mailer instance * @param EntityManagerInterface $manager The manager instance * @param RouterInterface $router The router instance * @param Security $security The security instance * @param SluggerUtil $slugger The slugger instance * @param RequestStack $stack The stack instance * @param TranslatorInterface $translator The translator instance * @param Environment $twig The twig environment instance * @param integer $limit The page limit */ public function __construct(AuthorizationCheckerInterface $checker, ContainerInterface $container, ManagerRegistry $doctrine, UserPasswordHasherInterface $hasher, LoggerInterface $logger, MailerInterface $mailer, EntityManagerInterface $manager, RouterInterface $router, Security $security, SluggerUtil $slugger, RequestStack $stack, TranslatorInterface $translator, Environment $twig, int $limit = 5) { //Retrieve config $this->config = $container->getParameter(RapsysUserBundle::getAlias()); //Set checker $this->checker = $checker; //Set container $this->container = $container; //Set doctrine $this->doctrine = $doctrine; //Set hasher $this->hasher = $hasher; //Set logger $this->logger = $logger; //Set limit $this->limit = $limit; //Set mailer $this->mailer = $mailer; //Set manager $this->manager = $manager; //Set router $this->router = $router; //Set security $this->security = $security; //Set slugger $this->slugger = $slugger; //Set translator $this->translator = $translator; //Set twig $this->twig = $twig; //Get current request $this->request = $stack->getCurrentRequest(); //Get current page $this->page = (int) $this->request->query->get('page'); //With negative page if ($this->page < 0) { $this->page = 0; } //Get current locale $this->locale = $this->request->getLocale(); //Set translate array $translates = []; //Look for keys to translate if (!empty($this->config['translate'])) { //Iterate on keys to translate foreach($this->config['translate'] as $translate) { //Set tmp $tmp = null; //Iterate on keys foreach(array_reverse(explode('.', $translate)) as $curkey) { $tmp = array_combine([$curkey], [$tmp]); } //Append tree $translates = array_replace_recursive($translates, $tmp); } } //Inject every requested route in view and mail context foreach($this->config as $tag => $current) { //Look for entry with route subkey if (!empty($current['route'])) { //Generate url for both view and mail foreach(['view', 'mail'] as $view) { //Check that context key is usable if (isset($current[$view]['context']) && is_array($current[$view]['context'])) { //Merge with global context $this->config[$tag][$view]['context'] = array_replace_recursive($this->config['context'], $this->config[$tag][$view]['context']); //Process every routes foreach($current['route'] as $route => $key) { //With confirm route if ($route == 'confirm') { //Skip route as it requires some parameters continue; } //Set value $value = $this->router->generate( $this->config['route'][$route]['name'], $this->config['route'][$route]['context'], //Generate absolute url for mails $view=='mail'?UrlGeneratorInterface::ABSOLUTE_URL:UrlGeneratorInterface::ABSOLUTE_PATH ); //Multi level key if (strpos($key, '.') !== false) { //Set tmp $tmp = $value; //Iterate on key foreach(array_reverse(explode('.', $key)) as $curkey) { $tmp = array_combine([$curkey], [$tmp]); } //Set value $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp); //Single level key } else { //Set value $this->config[$tag][$view]['context'][$key] = $value; } } //Look for successful intersections if (!empty(array_intersect_key($translates, $this->config[$tag][$view]['context']))) { //Iterate on keys to translate foreach($this->config['translate'] as $translate) { //Set keys $keys = explode('.', $translate); //Set tmp $tmp = $this->config[$tag][$view]['context']; //Iterate on keys foreach($keys as $curkey) { //Without child key if (!isset($tmp[$curkey])) { //Skip to next key continue(2); } //Get child key $tmp = $tmp[$curkey]; } //Translate tmp value $tmp = $this->translator->trans($tmp); //Iterate on keys foreach(array_reverse($keys) as $curkey) { //Set parent key $tmp = array_combine([$curkey], [$tmp]); } //Set value $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp); } } //With view context if ($view == 'view') { //Get context path $pathInfo = $this->router->getContext()->getPathInfo(); //Iterate on locales excluding current one foreach(($locales = array_keys($this->config['languages'])) as $locale) { //Set titles $titles = []; //Iterate on other locales foreach(array_diff($locales, [$locale]) as $other) { $titles[$other] = $this->translator->trans($this->config['languages'][$locale], [], null, $other); } //Retrieve route matching path $route = $this->router->match($pathInfo); //Get route name $name = $route['_route']; //Unset route name unset($route['_route']); //With current locale if ($locale == $this->locale) { //Set locale locales context $this->config[$tag][$view]['context']['head']['canonical'] = $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL); } else { //Set locale locales context $this->config[$tag][$view]['context']['head']['alternates'][$locale] = [ 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL), 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route), 'title' => implode('/', $titles), 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale) ]; } //Add shorter locale if (empty($this->config[$tag][$view]['context']['head']['alternates'][$slocale = substr($locale, 0, 2)])) { //Add shorter locale $this->config[$tag][$view]['context']['head']['alternates'][$slocale] = [ 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL), 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route), 'title' => implode('/', $titles), 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale) ]; } } } } } } } } /** * Renders a view * * {@inheritdoc} */ protected function render(string $view, array $parameters = [], Response $response = null): Response { //Create response when null $response ??= new Response(); //With empty head locale if (empty($parameters['head']['locale'])) { //Set head locale $parameters['head']['locale'] = $this->locale; } //With empty head title and section if (empty($parameters['head']['title']) && !empty($parameters['section'])) { //Set head title $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['section'], $parameters['head']['site']]); //With empty head title } elseif (empty($parameters['head']['title'])) { //Set head title $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['head']['site']]); } //Call twig render method $content = $this->twig->render($view, $parameters); //Invalidate OK response on invalid form if (200 === $response->getStatusCode()) { foreach ($parameters as $v) { if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) { $response->setStatusCode(422); break; } } } //Store content in response $response->setContent($content); //Return response return $response; } /** * {@inheritdoc} * * @see vendor/symfony/framework-bundle/Controller/AbstractController.php */ public static function getSubscribedServices(): array { //Return subscribed services return [ 'doctrine' => ManagerRegistry::class, 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class, 'logger' => LoggerInterface::class, 'mailer.mailer' => MailerInterface::class, 'rapsys_pack.slugger_util' => SluggerUtil::class, 'request_stack' => RequestStack::class, 'router' => RouterInterface::class, 'security.authorization_checker' => AuthorizationCheckerInterface::class, 'security' => Security::class, 'security.user_password_hasher' => UserPasswordHasherInterface::class, 'service_container' => ContainerInterface::class, 'translator' => TranslatorInterface::class ]; } }