]> Raphaël G. Git Repositories - userbundle/blob - Controller/AbstractController.php
Add note about hashed password
[userbundle] / Controller / AbstractController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys UserBundle 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\UserBundle\Controller;
13
14 use Doctrine\ORM\EntityManagerInterface;
15 use Doctrine\Persistence\ManagerRegistry;
16
17 use Rapsys\PackBundle\Util\SluggerUtil;
18
19 use Rapsys\UserBundle\RapsysUserBundle;
20
21 use Psr\Container\ContainerInterface;
22 use Psr\Log\LoggerInterface;
23
24 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
25 use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
26 use Symfony\Bundle\SecurityBundle\Security;
27 use Symfony\Component\Form\FormFactoryInterface;
28 use Symfony\Component\HttpFoundation\Request;
29 use Symfony\Component\HttpFoundation\RequestStack;
30 use Symfony\Component\HttpFoundation\Response;
31 use Symfony\Component\Mailer\MailerInterface;
32 use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
33 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
34 use Symfony\Component\Routing\RouterInterface;
35 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
36 use Symfony\Component\Security\Core\User\UserInterface;
37 use Symfony\Contracts\Cache\CacheInterface;
38 use Symfony\Contracts\Service\ServiceSubscriberInterface;
39 use Symfony\Contracts\Translation\TranslatorInterface;
40
41 use Twig\Environment;
42
43 /**
44 * {@inheritdoc}
45 *
46 * Provides common features needed in controllers.
47 */
48 abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface {
49 /**
50 * Alias string
51 */
52 protected string $alias;
53
54 /**
55 * Config array
56 */
57 protected array $config;
58
59 /**
60 * Context array
61 */
62 protected array $context;
63
64 /**
65 * Locale string
66 */
67 protected string $locale;
68
69 /**
70 * Page integer
71 */
72 protected int $page;
73
74 /**
75 * Request instance
76 */
77 protected Request $request;
78
79 /**
80 * Abstract constructor
81 *
82 * @param CacheInterface $cache The cache instance
83 * @param AuthorizationCheckerInterface $checker The checker instance
84 * @param ContainerInterface $container The container instance
85 * @param ManagerRegistry $doctrine The doctrine instance
86 * @param FormFactoryInterface $factory The factory instance
87 * @param UserPasswordHasherInterface $hasher The password hasher instance
88 * @param LoggerInterface $logger The logger instance
89 * @param MailerInterface $mailer The mailer instance
90 * @param EntityManagerInterface $manager The manager instance
91 * @param RouterInterface $router The router instance
92 * @param Security $security The security instance
93 * @param SluggerUtil $slugger The slugger instance
94 * @param RequestStack $stack The stack instance
95 * @param TranslatorInterface $translator The translator instance
96 * @param Environment $twig The twig environment instance
97 * @param integer $limit The page limit
98 */
99 public function __construct(protected CacheInterface $cache, protected AuthorizationCheckerInterface $checker, protected ContainerInterface $container, protected ManagerRegistry $doctrine, protected FormFactoryInterface $factory, protected UserPasswordHasherInterface $hasher, protected LoggerInterface $logger, protected MailerInterface $mailer, protected EntityManagerInterface $manager, protected RouterInterface $router, protected Security $security, protected SluggerUtil $slugger, protected RequestStack $stack, protected TranslatorInterface $translator, protected Environment $twig, protected int $limit = 5) {
100 //Retrieve config
101 $this->config = $container->getParameter($this->alias = RapsysUserBundle::getAlias());
102
103 //Get current request
104 $this->request = $stack->getCurrentRequest();
105
106 //Get current page
107 $this->page = (int) $this->request->query->get('page');
108
109 //With negative page
110 if ($this->page < 0) {
111 $this->page = 0;
112 }
113
114 //Get current locale
115 $this->locale = $this->request->getLocale();
116
117 //Set translate array
118 $translates = [];
119
120 //Look for keys to translate
121 if (!empty($this->config['translate'])) {
122 //Iterate on keys to translate
123 foreach($this->config['translate'] as $translate) {
124 //Set tmp
125 $tmp = null;
126
127 //Iterate on keys
128 foreach(array_reverse(explode('.', $translate)) as $curkey) {
129 $tmp = array_combine([$curkey], [$tmp]);
130 }
131
132 //Append tree
133 $translates = array_replace_recursive($translates, $tmp);
134 }
135 }
136
137 //Inject every requested route in view and mail context
138 foreach($this->config as $tag => $current) {
139 //Look for entry with route subkey
140 if (!empty($current['route'])) {
141 //Generate url for both view and mail
142 foreach(['view', 'mail'] as $view) {
143 //Check that context key is usable
144 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
145 //Merge with global context
146 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config['context'], $this->config[$tag][$view]['context']);
147
148 //Process every routes
149 foreach($current['route'] as $route => $key) {
150 //With confirm route
151 if ($route == 'confirm') {
152 //Skip route as it requires some parameters
153 continue;
154 }
155
156 //Set value
157 $value = $this->router->generate(
158 $this->config['route'][$route]['name'],
159 $this->config['route'][$route]['context'],
160 //Generate absolute url for mails
161 $view=='mail'?UrlGeneratorInterface::ABSOLUTE_URL:UrlGeneratorInterface::ABSOLUTE_PATH
162 );
163
164 //Multi level key
165 if (strpos($key, '.') !== false) {
166 //Set tmp
167 $tmp = $value;
168
169 //Iterate on key
170 foreach(array_reverse(explode('.', $key)) as $curkey) {
171 $tmp = array_combine([$curkey], [$tmp]);
172 }
173
174 //Set value
175 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
176 //Single level key
177 } else {
178 //Set value
179 $this->config[$tag][$view]['context'][$key] = $value;
180 }
181 }
182
183 //Look for successful intersections
184 if (!empty(array_intersect_key($translates, $this->config[$tag][$view]['context']))) {
185 //Iterate on keys to translate
186 foreach($this->config['translate'] as $translate) {
187 //Set keys
188 $keys = explode('.', $translate);
189
190 //Set tmp
191 $tmp = $this->config[$tag][$view]['context'];
192
193 //Iterate on keys
194 foreach($keys as $curkey) {
195 //Without child key
196 if (!isset($tmp[$curkey])) {
197 //Skip to next key
198 continue(2);
199 }
200
201 //Get child key
202 $tmp = $tmp[$curkey];
203 }
204
205 //Translate tmp value
206 $tmp = $this->translator->trans($tmp, [], $this->alias);
207
208 //Iterate on keys
209 foreach(array_reverse($keys) as $curkey) {
210 //Set parent key
211 $tmp = array_combine([$curkey], [$tmp]);
212 }
213
214 //Set value
215 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
216 }
217 }
218
219 //With view context
220 if ($view == 'view') {
221 //Get context path
222 $pathInfo = $this->router->getContext()->getPathInfo();
223
224 //Iterate on locales excluding current one
225 foreach(($locales = array_keys($this->config['default']['languages'])) as $locale) {
226 //Set titles
227 $titles = [];
228
229 //Iterate on other locales
230 foreach(array_diff($locales, [$locale]) as $other) {
231 $titles[$other] = $this->translator->trans($this->config['default']['languages'][$locale], [], $this->alias, $other);
232 }
233
234 //Retrieve route matching path
235 $route = $this->router->match($pathInfo);
236
237 //Get route name
238 $name = $route['_route'];
239
240 //Unset route name
241 unset($route['_route']);
242
243 //With current locale
244 if ($locale == $this->locale) {
245 //Set locale locales context
246 $this->config[$tag][$view]['context']['canonical'] = $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL);
247 $this->config[$tag][$view]['context']['self_url'] = $this->router->generate($name, ['_locale' => $locale]+$route);
248 } else {
249 //Set locale locales context
250 $this->config[$tag][$view]['context']['alternates'][$locale] = [
251 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
252 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
253 'title' => implode('/', $titles),
254 'translated' => $this->translator->trans($this->config['default']['languages'][$locale], [], $this->alias, $locale)
255 ];
256 }
257
258 //Add shorter locale
259 if (empty($this->config[$tag][$view]['context']['alternates'][$slocale = substr($locale, 0, 2)])) {
260 //Add shorter locale
261 $this->config[$tag][$view]['context']['alternates'][$slocale] = [
262 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
263 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
264 'title' => implode('/', $titles),
265 'translated' => $this->translator->trans($this->config['default']['languages'][$locale], [], $this->alias, $locale)
266 ];
267 }
268 }
269 }
270 }
271 }
272 }
273 }
274 }
275
276 /**
277 * {@inheritdoc}
278 *
279 * Renders a view
280 */
281 protected function render(string $view, array $parameters = [], Response $response = null): Response {
282 //Create response when null
283 $response ??= new Response();
284
285 //With empty head locale
286 if (empty($parameters['locale'])) {
287 //Set head locale
288 $parameters['locale'] = $this->locale;
289 }
290
291 //Call twig render method
292 $content = $this->twig->render($view, $parameters);
293
294 //Invalidate OK response on invalid form
295 if (200 === $response->getStatusCode()) {
296 foreach ($parameters as $v) {
297 if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
298 $response->setStatusCode(422);
299 break;
300 }
301 }
302 }
303
304 //Store content in response
305 $response->setContent($content);
306
307 //Return response
308 return $response;
309 }
310
311 /**
312 * {@inheritdoc}
313 *
314 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
315 */
316 public static function getSubscribedServices(): array {
317 //Return subscribed services
318 return [
319 'doctrine' => ManagerRegistry::class,
320 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
321 'form.factory' => FormFactoryInterface::class,
322 'logger' => LoggerInterface::class,
323 'mailer.mailer' => MailerInterface::class,
324 'rapsys_pack.slugger_util' => SluggerUtil::class,
325 'request_stack' => RequestStack::class,
326 'router' => RouterInterface::class,
327 'security.authorization_checker' => AuthorizationCheckerInterface::class,
328 'security.helper' => Security::class,
329 'security.user_password_hasher' => UserPasswordHasherInterface::class,
330 'service_container' => ContainerInterface::class,
331 'translator' => TranslatorInterface::class,
332 'twig' => Environment::class,
333 'user.cache' => CacheInterface::class
334 ];
335 }
336 }