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

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