]> Raphaël G. Git Repositories - treebundle/blob - Controller/TreeController.php
Add album and element templates
[treebundle] / Controller / TreeController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys TreeBundle 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\TreeBundle\Controller;
13
14 use Doctrine\Persistence\ManagerRegistry;
15
16 use Psr\Container\ContainerInterface;
17
18 use Rapsys\PackBundle\Util\FacebookUtil;
19 use Rapsys\TreeBundle\Entity\Album;
20 use Rapsys\TreeBundle\RapsysTreeBundle;
21
22 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
23 use Symfony\Bundle\SecurityBundle\Security;
24 use Symfony\Component\HttpFoundation\Request;
25 use Symfony\Component\HttpFoundation\RequestStack;
26 use Symfony\Component\HttpFoundation\Response;
27 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
28 use Symfony\Component\Routing\RouterInterface;
29 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
30
31 use Symfony\Contracts\Translation\TranslatorInterface;
32
33 use Twig\Environment;
34
35 /**
36 * {@inheritdoc}
37 */
38 class TreeController extends AbstractController {
39 /**
40 * Config array
41 */
42 protected array $config;
43
44 /**
45 * Context array
46 */
47 protected array $context = [];
48
49 /**
50 * Count integer
51 */
52 protected int $count;
53
54 /**
55 * Locale string
56 */
57 protected string $locale;
58
59 /**
60 * Page integer
61 */
62 protected int $page;
63
64 /**
65 * Request instance
66 */
67 protected Request $request;
68
69 /**
70 * Route string
71 */
72 protected string $route;
73
74 /**
75 * Route params array
76 */
77 protected array $routeParams;
78
79 /**
80 * Creates a new tree controller
81 *
82 * @param AuthorizationCheckerInterface $checker The container instance
83 * @param ContainerInterface $container The ContainerInterface instance
84 * @param ManagerRegistry $doctrine The doctrine instance
85 * @param FacebookUtil $facebook The facebook instance
86 * @param RouterInterface $router The router instance
87 * @param Security $security The security instance
88 * @param RequestStack $stack The stack instance
89 * @param TranslatorInterface $translator The translator instance
90 * @param Environment $twig The twig environment instance
91 * @param integer $limit The page limit
92 */
93 function __construct(protected AuthorizationCheckerInterface $checker, protected ContainerInterface $container, protected ManagerRegistry $doctrine, protected FacebookUtil $facebook, protected RouterInterface $router, protected Security $security, protected RequestStack $stack, protected TranslatorInterface $translator, protected Environment $twig, protected int $limit = 5) {
94 //Retrieve config
95 $this->config = $container->getParameter(RapsysTreeBundle::getAlias());
96
97 //Get main request
98 $this->request = $this->stack->getMainRequest();
99
100 //Get current locale
101 $this->locale = $this->request->getLocale();
102
103 //Set canonical
104 $canonical = null;
105
106 //Set alternates
107 $alternates = [];
108
109 //Get current page
110 $this->page = (int) $this->request->query->get('page');
111
112 //With negative page
113 if ($this->page < 0) {
114 $this->page = 0;
115 }
116
117 //Set route
118 //TODO: default to not found route ???
119 //TODO: pour une url not found, cet attribut n'est pas défini, comment on fait ???
120 //XXX: on génère une route bidon par défaut ???
121 $this->route = $this->request->attributes->get('_route');
122
123 //Set route params
124 $this->routeParams = $this->request->attributes->get('_route_params');
125
126 //With route and routeParams
127 if ($this->route !== null && $this->routeParams !== null) {
128 //Set canonical
129 $canonical = $this->router->generate($this->route, $this->routeParams, UrlGeneratorInterface::ABSOLUTE_URL);
130
131 //Set alternates
132 $alternates = [
133 substr($this->locale, 0, 2) => [
134 'absolute' => $canonical
135 ]
136 ];
137 }
138
139 //Set the context
140 $this->context = [
141 'alternates' => $alternates,
142 'canonical' => $canonical,
143 'contact' => [
144 'address' => $this->config['contact']['address'],
145 'name' => $this->translator->trans($this->config['contact']['name'])
146 ],
147 'copy' => [
148 'by' => $this->translator->trans($this->config['copy']['by']),
149 'link' => $this->config['copy']['link'],
150 'long' => $this->translator->trans($this->config['copy']['long']),
151 'short' => $this->translator->trans($this->config['copy']['short']),
152 'title' => $this->config['copy']['title']
153 ],
154 'description' => null,
155 'donate' => $this->config['donate'],
156 'facebook' => [
157 'og:type' => 'article',
158 'og:site_name' => $title = $this->translator->trans($this->config['title']),
159 'og:url' => $canonical,
160 #'fb:admins' => $this->config['facebook']['admins'],
161 'fb:app_id' => $this->config['facebook']['apps']
162 ],
163 //XXX: TODO: only generate it when fb robot request the url ???
164 'fbimage' => [
165 'texts' => [
166 $title => [
167 'font' => 'irishgrover',
168 'size' => 110
169 ]
170 ]
171 ],
172 'icon' => $this->config['icon'],
173 'keywords' => null,
174 'locale' => str_replace('_', '-', $this->locale),
175 'logo' => $this->config['logo'],
176 'forms' => [],
177 'root' => $this->router->generate($this->config['root']),
178 'title' => [
179 'page' => null,
180 'section' => null,
181 'site' => $title
182 ]
183 ];
184 }
185
186 /**
187 * The index page
188 *
189 * Display index
190 *
191 * @param Request $request The request instance
192 * @return Response The rendered view
193 */
194 public function index(Request $request): Response {
195 //Get user
196 $user = $this->security->getUser();
197
198 //With not enough albums
199 if (($this->count = $this->doctrine->getRepository(Album::class)->findCountAsInt($user?->getId())) < $this->page * $this->limit) {
200 //Throw 404
201 throw $this->createNotFoundException($this->translator->trans('Unable to find albums'));
202 }
203
204 //Get albums
205 if ($this->context['albums'] = $this->doctrine->getRepository(Album::class)->findAllAsArray($user?->getId(), $this->page, $this->limit)) {
206 //Set modified
207 $this->modified = max(array_map(function ($v) { return $v['modified']; }, $this->context['albums']));
208 //Without albums
209 } else {
210 //Set empty modified
211 $this->modified = new \DateTime('-1 year');
212 }
213
214 /*header('Content-Type: text/plain');
215 var_dump($this->context['albums']);
216 exit;*/
217
218 //Create response
219 $response = new Response();
220
221 //With logged user
222 if ($this->checker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
223 //Set last modified
224 $response->setLastModified(new \DateTime('-1 year'));
225
226 //Set as private
227 $response->setPrivate();
228 //Without logged user
229 } else {
230 //Set etag
231 //XXX: only for public to force revalidation by last modified
232 $response->setEtag(md5(serialize($this->context['albums'])));
233
234 //Set last modified
235 $response->setLastModified($this->modified);
236
237 //Set as public
238 $response->setPublic();
239
240 //Without role and modification
241 if ($response->isNotModified($request)) {
242 //Return 304 response
243 return $response;
244 }
245 }
246
247 //Set keywords
248 /*$this->context['head']['keywords'] = implode(
249 ', ',
250 //Use closure to extract each unique article keywords sorted
251 (function ($t) {
252 //Return array
253 $r = [];
254
255 //Iterate on articles
256 foreach($t as $a) {
257 //Non empty keywords
258 if (!empty($a['keywords'])) {
259 //Iterate on keywords
260 foreach($a['keywords'] as $k) {
261 //Set keyword
262 $r[$k['title']] = $k['title'];
263 }
264 }
265 }
266
267 //Sort array
268 sort($r);
269
270 //Return array
271 return $r;
272 })($this->context['articles'])
273 );
274 //Get albums
275 $this->context['albums'] = $this->config['albums'];*/
276
277 //Return rendered response
278 return $this->render('@RapsysTree/index.html.twig', $this->context, $response);
279 }
280
281 /**
282 * The directory page
283 *
284 * Display directory
285 *
286 * @param Request $request The request instance
287 * @param string $path The directory path
288 * @return Response The rendered view
289 */
290 public function directory(Request $request, string $path): Response {
291 header('Content-Type: text/plain');
292 var_dump($path);
293 exit;
294
295 //Render template
296 $response = $this->render('@RapsysTree/directory.html.twig', $this->context);
297
298 $response->setEtag(md5($response->getContent()));
299 $response->setPublic();
300 $response->isNotModified($request);
301
302 //Return response
303 return $response;
304 }
305
306 /**
307 * The document page
308 *
309 * Display document
310 *
311 * @param Request $request The request instance
312 * @param string $path The directory path
313 * @return Response The rendered view
314 */
315 public function document(Request $request, string $path): Response {
316 //Render template
317 $response = $this->render('@RapsysTree/document.html.twig', $this->context);
318
319 $response->setEtag(md5($response->getContent()));
320 $response->setPublic();
321 $response->isNotModified($request);
322
323 //Return response
324 return $response;
325 }
326
327 /**
328 * Renders a view
329 *
330 * {@inheritdoc}
331 */
332 protected function render(string $view, array $parameters = [], Response $response = null): Response {
333 //Create response when null
334 $response ??= new Response();
335
336 //Without alternates
337 if (count($parameters['alternates']) <= 1) {
338 //Set routeParams
339 $routeParams = $this->routeParams;
340
341 //Iterate on locales excluding current one
342 foreach($this->config['locales'] as $locale) {
343 //With current locale
344 if ($locale !== $this->locale) {
345 //Set titles
346 $titles = [];
347
348 //Set route params locale
349 $routeParams['_locale'] = $locale;
350
351 //Iterate on other locales
352 foreach(array_diff($this->config['locales'], [$locale]) as $other) {
353 //Set other locale title
354 $titles[$other] = $this->translator->trans($this->config['languages'][$locale], [], null, $other);
355 }
356
357 //Set locale locales context
358 $parameters['alternates'][str_replace('_', '-', $locale)] = [
359 'absolute' => $this->router->generate($this->route, $routeParams, UrlGeneratorInterface::ABSOLUTE_URL),
360 'relative' => $this->router->generate($this->route, $routeParams),
361 'title' => implode('/', $titles),
362 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
363 ];
364
365 //Add shorter locale
366 if (empty($parameters['alternates'][$shortCurrent = substr($locale, 0, 2)])) {
367 //Set locale locales context
368 $parameters['alternates'][$shortCurrent] = $parameters['alternates'][str_replace('_', '-', $locale)];
369 }
370 }
371 }
372 }
373
374 //With canonical
375 if (!empty($parameters['canonical'])) {
376 //Set facebook url
377 $parameters['facebook']['og:url'] = $parameters['canonical'];
378 }
379
380 //With empty facebook title and title
381 if (empty($parameters['facebook']['og:title']) && !empty($parameters['title']['page'])) {
382 //Set facebook title
383 $parameters['facebook']['og:title'] = $parameters['title']['page'];
384 }
385
386 //With empty facebook description and description
387 if (empty($parameters['facebook']['og:description']) && !empty($parameters['description'])) {
388 //Set facebook description
389 $parameters['facebook']['og:description'] = $parameters['description'];
390 }
391
392 //With locale
393 if (!empty($this->locale)) {
394 //Set facebook locale
395 $parameters['facebook']['og:locale'] = $this->locale;
396
397 //With alternates
398 //XXX: locale change when fb_locale=xx_xx is provided is done in FacebookSubscriber
399 //XXX: see https://stackoverflow.com/questions/20827882/in-open-graph-markup-whats-the-use-of-oglocalealternate-without-the-locati
400 if (!empty($parameters['alternates'])) {
401 //Iterate on alternates
402 foreach($parameters['alternates'] as $lang => $alternate) {
403 if (strlen($lang) == 5) {
404 //Set facebook locale alternate
405 $parameters['facebook']['og:locale:alternate'] = str_replace('-', '_', $lang);
406 }
407 }
408 }
409 }
410
411 //Without facebook image defined and texts
412 if (empty($parameters['facebook']['og:image']) && !empty($this->request) && !empty($parameters['fbimage']['texts']) && !empty($this->modified)) {
413 //Get facebook image
414 $parameters['facebook'] += $this->facebook->getImage($this->request->getPathInfo(), $parameters['fbimage']['texts'], $this->modified->getTimestamp());
415 }
416
417 //Call twig render method
418 $content = $this->twig->render($view, $parameters);
419
420 //Invalidate OK response on invalid form
421 if (200 === $response->getStatusCode()) {
422 foreach ($parameters as $v) {
423 if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
424 $response->setStatusCode(422);
425 break;
426 }
427 }
428 }
429
430 //Store content in response
431 $response->setContent($content);
432
433 //Return response
434 return $response;
435 }
436 }