vendor/boldr/cms-bundle/src/Twig/TwigExtension.php line 434

Open in your IDE?
  1. <?php
  2. namespace Boldr\Cms\CmsBundle\Twig;
  3. use Boldr\Cms\CmsBundle\SiteConfiguration;
  4. use Boldr\Cms\CmsBundle\Upload\UploadStorageManager;
  5. use Boldr\Cms\CmsBundle\Presenter\Menu\{ MenuPresenterMenuView };
  6. use Boldr\Cms\CmsBundle\Entity\{ UploadMenu };
  7. use Boldr\Cms\CmsBundle\Controller\PermalinkController;
  8. use Boldr\Cms\CmsBundle\Presenter\Attachment\AttachmentPresenterInterface;
  9. use Boldr\Cms\CmsBundle\Search\SearchConfigurationPermalinkable;
  10. use Boldr\Cms\CmsBundle\Link\{ LinkInterface };
  11. use Boldr\Cms\CmsBundle\Permalink\PermalinkableInterface;
  12. use Boldr\Cms\CmsBundle\Content\{ ContentManagerAssets };
  13. use Boldr\Cms\CmsBundle\Renderer\{ HtmlRenderer };
  14.  use Boldr\Cms\CmsBundle\Cookies\CookieManager;
  15. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  16. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  17. use Symfony\Component\HttpFoundation\RequestStack;
  18. use Symfony\Contracts\Cache\TagAwareCacheInterface;
  19. use Symfony\Component\Serializer\Serializer;
  20. use Symfony\Component\Serializer\Encoder\JsonEncoder;
  21. use Twig\{ TwigFunctionEnvironment };
  22. use Twig\Extension\AbstractExtension;
  23. use Doctrine\ORM\EntityManagerInterface;
  24. use Psr\Container\ContainerInterface;
  25. use Psr\Http\Client\ClientInterface;
  26. use Psr\Http\Message\{ RequestFactoryInterfaceUriFactoryInterface };
  27. use Embed\Embed;
  28. use Embed\Http\Crawler;
  29. use Exception;
  30. use Throwable;
  31. use MatthiasMullie\Minify;
  32. class TwigExtension extends AbstractExtension
  33. {
  34.     public function __construct(
  35.         private readonly ParameterBagInterface $parameterBag,
  36.         private readonly EntityManagerInterface $em,
  37.         private readonly RequestStack $requestStack,
  38.         private readonly MenuPresenter $menuPresenter,
  39.         private readonly PermalinkController $permalinkController,
  40.         private readonly UrlGeneratorInterface $urlGenerator,
  41.         private readonly TagAwareCacheInterface $cache,
  42.         private readonly UploadStorageManager $uploadStorageManager,
  43.         private readonly SiteConfiguration $siteConfiguration,
  44.         private readonly AttachmentPresenterInterface $attachmentPresenter,
  45.         private readonly ClientInterface $httpClient,
  46.         private readonly RequestFactoryInterface $requestFactory,
  47.         private readonly UriFactoryInterface $uriFactory,
  48.         private readonly HtmlRenderer $htmlRenderer,
  49.         private readonly ContentManager $contentManager,
  50.         private readonly CookieManager $cookieManager,
  51.         /** @var iterable<AssetProviderInterface> */
  52.         private readonly iterable $assetProviders,
  53.         private readonly ContainerInterface $linkGenerators,
  54.     ) {}
  55.     public function getFunctions()
  56.     {
  57.         $functions = [];
  58.         $functions[] = new TwigFunction('boldr_cms_content_render', [$this'renderContent'], ['is_safe' => ['html'], 'needs_context' => true]);
  59.         $functions[] = new TwigFunction('boldr_cms_get_assets', [$this'getAssets'], ['needs_context' => true]);
  60.         $functions[] = new TwigFunction('boldr_cms_render_css', [$this'renderCss'], ['is_safe' => ['html']]);
  61.         $functions[] = new TwigFunction('boldr_cms_render_js', [$this'renderJs'], ['is_safe' => ['html']]);
  62.         $functions[] = new TwigFunction('boldr_cms_get_available_locales', [$this'getAvailableLocales']);
  63.         $functions[] = new TwigFunction('boldr_cms_get_enabled_locales', [$this'getEnabledLocales']);
  64.         $functions[] = new TwigFunction('boldr_cms_get_cookie_state', [$this'getCookieState']);
  65.         $functions[] = new TwigFunction('boldr_cms_get_site_name', [$this'getSiteName']);
  66.         $functions[] = new TwigFunction('boldr_cms_get_menus_at_location', [$this'getMenusAtLocation']);
  67.         $functions[] = new TwigFunction('boldr_cms_get_search_url', [$this'getSearchUrl']);
  68.         $functions[] = new TwigFunction('boldr_cms_get_permalink', [$this'getPermalink']);
  69.         $functions[] = new TwigFunction('boldr_cms_get_translation_url', [$this'getTranslationUrl']);
  70.         $functions[] = new TwigFunction('boldr_cms_get_attachment_url', [$this'getUploadUrl']);
  71.         $functions[] = new TwigFunction('boldr_cms_render_attachment', [$this'renderAttachment']);
  72.         $functions[] = new TwigFunction('boldr_cms_get_heading_style_class', [$this'getHeadingStyleClass']);
  73.         $functions[] = new TwigFunction('boldr_cms_get_default_heading_style', [$this'getDefaultHeadingStyle']);
  74.         $functions[] = new TwigFunction('boldr_cms_get_button_style_class', [$this'getButtonStyleClass']);
  75.         $functions[] = new TwigFunction('boldr_cms_get_primary_button_style', [$this'getPrimaryButtonStyle']);
  76.         $functions[] = new TwigFunction('boldr_cms_get_secondary_button_style', [$this'getSecondaryButtonStyle']);
  77.         $functions[] = new TwigFunction('boldr_cms_get_section_style_class', [$this'getSectionStyleClass']);
  78.         $functions[] = new TwigFunction('boldr_cms_get_defaut_section_style', [$this'getDefaultSectionStyle']);
  79.         $functions[] = new TwigFunction('boldr_cms_get_default_section_style', [$this'getDefaultSectionStyle']);
  80.         $functions[] = new TwigFunction('boldr_cms_generate_link', [$this'generateLink']);
  81.         $functions[] = new TwigFunction('boldr_cms_get_oembed_code', [$this'getOembedCode'], ['is_safe' => ['html']]);
  82.         $functions[] = new TwigFunction('boldr_cms_serialize_view', [$this'serializeView'], ['is_safe' => ['js''html']]);
  83.         $functions[] = new TwigFunction('boldr_cms_render', [$this'render'], ['needs_context' => true'is_safe' => ['html']]);
  84.         if (!class_exists('Symfony\\WebpackEncoreBundle\\Twig\\EntryFilesTwigExtension'))
  85.         {
  86.             $functions[] = new TwigFunction('encore_entry_link_tags', [$this'encoreNotInstalled'], ['is_safe' => ['html'], 'needs_environment' => true]);
  87.             $functions[] = new TwigFunction('encore_entry_script_tags', [$this'encoreNotInstalled'], ['is_safe' => ['html'], 'needs_environment' => true]);
  88.         }
  89.         return $functions;
  90.     }
  91.     public function getCookieState(): array
  92.     {
  93.         return [
  94.             'marketing' => $this->cookieManager->getAcceptsMarketingCookies(),
  95.             'analysis' => $this->cookieManager->getAcceptsAnalysisCookies(),
  96.             'preference' => $this->cookieManager->getAcceptsPreferenceCookies(),
  97.         ];
  98.     }
  99.     public function render(array &$context$renderable): string
  100.     {
  101.         $render $this->htmlRenderer->render($renderable);
  102.         if ($render->getAssets() !== null)
  103.         {
  104.             if (isset($context['assets']))
  105.             {
  106.                 $context['assets']->addAll($render->getAssets());
  107.             }
  108.             else
  109.             {
  110.                 $context['assets'] = $render->getAssets();
  111.             }
  112.         }
  113.         return $render->getHtml();
  114.     }
  115.     public function serializeView($view): string
  116.     {
  117.         $serializer = new Serializer([new DynamicPropertyNormalizer], [new JsonEncoder]);
  118.         return $serializer->serialize($view'json');
  119.     }
  120.     public function getOembedCode(string $url): string
  121.     {
  122.         return $this->cache->get('oembed.'sha1($url), function() use ($url) {
  123.             try
  124.             {
  125.                 $embed = new Embed(new Crawler($this->httpClient$this->requestFactory$this->uriFactory));
  126.                 $info $embed->get($url);
  127.                 $code $info->code;
  128.                 return $code $code->html '[could not embed]';
  129.             }
  130.             catch (Throwable $ex)
  131.             {
  132.                 return '[could not embed]';
  133.             }
  134.         });
  135.     }
  136.     public function getDefaultSectionStyle(): string
  137.     {
  138.         return $this->parameterBag->get('boldr_cms.default_section_style');
  139.     }
  140.     public function getSectionStyleClass(string $sectionStyleName): string
  141.     {
  142.         /** @var array */
  143.         $sectionStyles $this->parameterBag->get('boldr_cms.section_styles');
  144.         return $sectionStyles[$sectionStyleName]['css_class'];
  145.     }
  146.     public function renderCss(iterable $files)
  147.     {
  148.         $files is_array($files) ? $files iterator_to_array($files);
  149.         if (count($files) === 0)
  150.         {
  151.             return '';
  152.         }
  153.         if ($this->parameterBag->get('boldr_cms.minify_assets') && !$this->parameterBag->get('kernel.debug')) {
  154.             $publicPath $this->parameterBag->get('kernel.project_dir').'/public';
  155.             $output '';
  156.             $filesToMinify = [];
  157.             foreach ($files as $file)
  158.             {
  159.                 if (strpos($file':') !== false || !file_exists($publicPath .'/'$file))
  160.                 {
  161.                     $output .= '<link rel="stylesheet" href="'.$file.'" />'.PHP_EOL;
  162.                 }
  163.                 else
  164.                 {
  165.                     $filesToMinify[] = $file;
  166.                 }
  167.             }
  168.             if (count($filesToMinify))
  169.             {
  170.                 $hash sha1(implode(','$filesToMinify));
  171.                 $dirPath =  $publicPath.'/assets.min';
  172.                 $minifiedPath $dirPath.'/'.$hash.'.css';
  173.                 $minifiedUrl '/assets.min/'.$hash.'.css';
  174.                 if (!file_exists($minifiedPath)) {
  175.                     if (!file_exists($dirPath)) {
  176.                         mkdir($dirPath);
  177.                     }
  178.                     $minify = new Minify\CSS;
  179.                     foreach ($filesToMinify as $file) {
  180.                         $minify->add($publicPath.$file);
  181.                     }
  182.                     $minify->minify($minifiedPath);
  183.                 }
  184.                 $output .= '<link rel="stylesheet" href="'.$minifiedUrl.'?v='.filemtime($minifiedPath).'" />'.PHP_EOL;
  185.             }
  186.             return $output;
  187.         }
  188.         else
  189.         {
  190.             $output '';
  191.             foreach ($files as $file) {
  192.                 $output .= '<link rel="stylesheet" href="'.$file.'" />'.PHP_EOL;
  193.             }
  194.             return $output;
  195.         }
  196.     }
  197.     public function renderJs(iterable $files)
  198.     {
  199.         $files is_array($files) ? $files iterator_to_array($files);
  200.         if (count($files) === 0)
  201.         {
  202.             return '';
  203.         }
  204.         if ($this->parameterBag->get('boldr_cms.minify_assets') && !$this->parameterBag->get('kernel.debug')) {
  205.             $output '';
  206.             $filesToMinify = [];
  207.             $publicPath $this->parameterBag->get('kernel.project_dir').'/public';
  208.             foreach ($files as $file)
  209.             {
  210.                 if (strpos($file':') !== false || !file_exists($publicPath .'/'$file))
  211.                 {
  212.                     $output .= '<script type="application/javascript" src="'.$file.'"></script>'.PHP_EOL;
  213.                 }
  214.                 else
  215.                 {
  216.                     $filesToMinify[] = $file;
  217.                 }
  218.             }
  219.             if (count($filesToMinify))
  220.             {
  221.                 $hash sha1(implode(','$filesToMinify));
  222.                 $dirPath =  $publicPath.'/assets.min';
  223.                 $minifiedPath $dirPath.'/'.$hash.'.js';
  224.                 $minifiedUrl '/assets.min/'.$hash.'.js';
  225.                 if (!file_exists($minifiedPath)) {
  226.                     if (!file_exists($dirPath)) {
  227.                         mkdir($dirPath);
  228.                     }
  229.                     $minify = new Minify\JS;
  230.                     foreach ($filesToMinify as $file) {
  231.                         $minify->add($publicPath.$file);
  232.                     }
  233.                     $minify->minify($minifiedPath);
  234.                 }
  235.                 $output .= '<script type="application/javascript" src="'.$minifiedUrl.'?v='.filemtime($minifiedPath).'"></script>'.PHP_EOL;
  236.             }
  237.             return $output;
  238.         }
  239.         else
  240.         {
  241.             $output '';
  242.             foreach ($files as $file) {
  243.                 $output .= '<script type="application/javascript" src="'.$file.'"></script>'.PHP_EOL;
  244.             }
  245.             return $output;
  246.         }
  247.     }
  248.     public function renderAttachment($upload, array $sizes = ['small''medium''large'])
  249.     {
  250.         $upload is_int($upload) ? $this->em->getReference(Upload::class, $upload) : $upload;
  251.         return $this->attachmentPresenter->createAttachmentView($upload$sizes$this->getCurrentLocale());
  252.     }
  253.     public function getPermalink(PermalinkableInterface $permalinkable, ?string $locale null)
  254.     {
  255.         return $this->permalinkController->generate($permalinkable$locale ?? $this->requestStack->getCurrentRequest()->getLocale());
  256.     }
  257.     public function generateLink(?LinkInterface $link)
  258.     {
  259.         if ($link === null)
  260.             return '';
  261.         return $this->linkGenerators->get(get_class($link))->generateLinkUrl($link);
  262.     }
  263.     public function getHeadingStyleClass(string $headingStyle)
  264.     {
  265.         /** @var array */
  266.         $styles $this->parameterBag->get('boldr_cms.heading_styles');
  267.         $style $styles[$headingStyle] ?? $styles['default'] ?? [];
  268.         return $style['css_class'] ?? '';
  269.     }
  270.     public function getDefaultHeadingStyle(): string
  271.     {
  272.         return $this->parameterBag->get('boldr_cms.default_heading_style');
  273.     }
  274.     public function getPrimaryButtonStyle(): string
  275.     {
  276.         return $this->parameterBag->get('boldr_cms.primary_button_style');
  277.     }
  278.     public function getSecondaryButtonStyle(): string
  279.     {
  280.         return $this->parameterBag->get('boldr_cms.secondary_button_style');
  281.     }
  282.     public function getButtonStyleClass(string $buttonStyle)
  283.     {
  284.         /** @var array */
  285.         $styles $this->parameterBag->get('boldr_cms.button_styles');
  286.         $style $styles[$buttonStyle] ?? $styles[$this->getPrimaryButtonStyle()] ?? null;
  287.         if ($style === null)
  288.         {
  289.             return '';
  290.         }
  291.         return $style['css_class'] ?? '';
  292.     }
  293.     public function getSearchUrl(string $searchConfigurationName)
  294.     {
  295.         /** @var array */
  296.         $searchConfigurations $this->parameterBag->get('boldr_cms.search_configurations');
  297.         $searchConfiguration $searchConfigurations[$searchConfigurationName] ?? null;
  298.         if ($searchConfiguration === null)
  299.         {
  300.             throw new Exception('No search configuration with id "'$searchConfigurationName .'" exists. Verify your search configuration in config/packages/boldr_cms.yaml');
  301.         }
  302.         return $this->permalinkController->generate(new SearchConfigurationPermalinkable($searchConfigurationName), $this->getCurrentLocale());
  303.     }
  304.     public function getAssets(array $context): Assets
  305.     {
  306.         $assets $context['assets'] ?? new Assets;
  307.         foreach ($this->assetProviders as $assetProvider)
  308.         {
  309.             $assetProvider->addAssets($assets);
  310.         }
  311.         return $assets;
  312.     }
  313.     public function getUploadUrl($upload, ?string $size null): string
  314.     {
  315.         if (is_int($upload))
  316.         {
  317.             $upload $this->em->getRepository(Upload::class)->find($upload);
  318.         }
  319.         $request $this->requestStack->getCurrentRequest();
  320.         $baseUrl $request->getSchemeAndHttpHost().$request->getBasePath();
  321.         return $this->uploadStorageManager->getImageUrl($upload$baseUrl$size);
  322.     }
  323.     /**
  324.      * @return string[]
  325.      */
  326.     public function getAvailableLocales(): array
  327.     {
  328.         return $this->siteConfiguration->getAvailableLocales();
  329.     }
  330.     /**
  331.      * @return string[]
  332.      */
  333.     public function getEnabledLocales(): array
  334.     {
  335.         return $this->siteConfiguration->getEnabledLocales();
  336.     }
  337.     public function getTranslationUrl(string $locale): ?string
  338.     {
  339.         $request $this->requestStack->getCurrentRequest();
  340.         /** @var array<string,string> */
  341.         $localeBaseUrls $this->parameterBag->get('boldr_cms.locale_base_urls');
  342.         $localeBaseUrl $localeBaseUrls[$locale] ?? '';
  343.         if (is_array($localeBaseUrl))
  344.         {
  345.             $localeBaseUrl $localeBaseUrl[0];
  346.         }
  347.         if ($request->attributes->has('_permalinkable'))
  348.         {
  349.             return $localeBaseUrl $this->permalinkController->generate($request->attributes->get('_permalinkable'), $locale);
  350.         }
  351.         $url = !$request->attributes->has('_route') ? '' $localeBaseUrl $this->urlGenerator->generate(
  352.             $request->attributes->get('_route'),
  353.             array_merge(
  354.                 $request->attributes->get('_route_params') ?: [],
  355.                 $request->query->all(),
  356.                 ['_locale' => $locale]
  357.             )
  358.         );
  359.         if (strpos($url'?_locale=') === false && strpos($url'&_locale=') === false)
  360.         {
  361.             return $url;
  362.         }
  363.         return null;
  364.     }
  365.     private function getCurrentLocale(): string
  366.     {
  367.         $request $this->requestStack->getCurrentRequest();
  368.         $locale $request === null $this->siteConfiguration->getDefaultLocale() : $request->getLocale();
  369.         return $locale;
  370.     }
  371.     public function getSiteName(): string {
  372.         $locale $this->getCurrentLocale();
  373.         return $this->siteConfiguration->getSiteName($locale);
  374.     }
  375.     /**
  376.      * @return MenuView[]
  377.      */
  378.     public function getMenusAtLocation(string $locationId): array
  379.     {
  380.         $locale $this->requestStack->getCurrentRequest()->getLocale();
  381.         return $this->cache->get('menus_at_location.'.$locationId.'.'.$locale, function($cacheItem) use ($locationId$locale) {
  382.             $cacheItem->tag('boldr_cms.menus_at_location');
  383.             $repo $this->em->getRepository(Menu::class);
  384.             /** @phpstan-ignore-next-line */
  385.             $menus $repo->createQueryBuilder('m')
  386.                 ->leftJoin('m.locations''l')
  387.                 ->where('l.location = ?1')
  388.                 ->setParameter(1$locationId)
  389.                 ->getQuery()
  390.                 ->getResult();
  391.             $menuViews = [];
  392.             foreach ($menus as $menu)
  393.             {
  394.                 $menuViews[] = $this->menuPresenter->createMenuView($menu$locale);
  395.             }
  396.             return $menuViews;
  397.         });
  398.     }
  399.     public function encoreNotInstalled(Environment $twig)
  400.     {
  401.         if ($twig->isDebug())
  402.             return '<!-- Webpack Encore is not installed -->';
  403.         return '';
  404.     }
  405.     public function renderContent($context, array $contentElements): string
  406.     {
  407.         $assets $context['assets'];
  408.         return $this->contentManager->renderAsHtml($contentElements$assets);
  409.     }
  410. }