vendor/contao/core-bundle/src/Resources/contao/pages/PageRegular.php line 59

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\EventListener\SubrequestCacheSubscriber;
  11. use Contao\CoreBundle\Exception\NoLayoutSpecifiedException;
  12. use Contao\CoreBundle\Framework\ContaoFramework;
  13. use Contao\CoreBundle\Routing\ResponseContext\HtmlHeadBag\HtmlHeadBag;
  14. use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager;
  15. use Contao\CoreBundle\Routing\ResponseContext\ResponseContext;
  16. use Contao\CoreBundle\Util\LocaleUtil;
  17. use Symfony\Component\HttpFoundation\Response;
  18. /**
  19.  * Provide methods to handle a regular front end page.
  20.  */
  21. #[\AllowDynamicProperties]
  22. class PageRegular extends Frontend
  23. {
  24.     /**
  25.      * @var ResponseContext
  26.      */
  27.     protected $responseContext;
  28.     /**
  29.      * Generate a regular page
  30.      *
  31.      * @param PageModel $objPage
  32.      * @param boolean   $blnCheckRequest
  33.      *
  34.      * @deprecated Deprecated since Contao 4.9, to be removed in Contao 5; use
  35.      *             the PageRegular::getResponse() method instead
  36.      */
  37.     public function generate($objPage$blnCheckRequest=false)
  38.     {
  39.         trigger_deprecation('contao/core-bundle''4.9''Using PageRegular::generate() has been deprecated in Contao 4.9 and will be removed in Contao 5.0. Use the PageRegular::getResponse() method instead.');
  40.         $this->prepare($objPage);
  41.         $this->Template->output($blnCheckRequest);
  42.     }
  43.     /**
  44.      * Return a response object
  45.      *
  46.      * @param PageModel $objPage
  47.      * @param boolean   $blnCheckRequest
  48.      *
  49.      * @return Response
  50.      */
  51.     public function getResponse($objPage$blnCheckRequest=false)
  52.     {
  53.         $this->prepare($objPage);
  54.         $response $this->Template->getResponse($blnCheckRequest);
  55.         // Allow subrequests on this controller (fragments) to dynamically influence the Cache-Control header
  56.         $response->headers->set(SubrequestCacheSubscriber::MERGE_CACHE_HEADERtrue);
  57.         // Finalize the response context so it cannot be used anymore
  58.         System::getContainer()->get('contao.routing.response_context_accessor')->finalizeCurrentContext($response);
  59.         return $response;
  60.     }
  61.     /**
  62.      * Generate a regular page
  63.      *
  64.      * @param PageModel $objPage
  65.      *
  66.      * @internal Do not call this method in your code. It will be made private in Contao 5.0.
  67.      */
  68.     protected function prepare($objPage)
  69.     {
  70.         $GLOBALS['TL_KEYWORDS'] = '';
  71.         $GLOBALS['TL_LANGUAGE'] = LocaleUtil::formatAsLanguageTag($objPage->language);
  72.         $locale LocaleUtil::formatAsLocale($objPage->language);
  73.         $container System::getContainer();
  74.         $container->get('translator')->setLocale($locale);
  75.         $request $container->get('request_stack')->getCurrentRequest();
  76.         $request->setLocale($locale);
  77.         $responseContextAccessor System::getContainer()->get('contao.routing.response_context_accessor');
  78.         if (!$this->responseContext $responseContextAccessor->getResponseContext())
  79.         {
  80.             $this->responseContext $container->get('contao.routing.response_context_factory')->createContaoWebpageResponseContext($objPage);
  81.         }
  82.         $blnShowUnpublished $container->get('contao.security.token_checker')->isPreviewMode();
  83.         System::loadLanguageFile('default');
  84.         // Static URLs
  85.         $this->setStaticUrls();
  86.         // Get the page layout
  87.         $objLayout $this->getPageLayout($objPage);
  88.         /** @var ThemeModel $objTheme */
  89.         $objTheme $objLayout->getRelated('pid');
  90.         // Set the default image densities
  91.         $container->get('contao.image.picture_factory')->setDefaultDensities($objLayout->defaultImageDensities);
  92.         $container->get('contao.image.preview_factory')->setDefaultDensities($objLayout->defaultImageDensities);
  93.         // Store the layout ID
  94.         $objPage->layoutId $objLayout->id;
  95.         // Set the layout template and template group
  96.         $objPage->template $objLayout->template ?: 'fe_page';
  97.         $objPage->templateGroup $objTheme->templates ?? null;
  98.         // Minify the markup
  99.         $objPage->minifyMarkup $objLayout->minifyMarkup;
  100.         // Initialize the template
  101.         $this->createTemplate($objPage$objLayout);
  102.         // Initialize modules and sections
  103.         $arrCustomSections = array();
  104.         $arrSections = array('header''left''right''main''footer');
  105.         $arrModules StringUtil::deserialize($objLayout->modules);
  106.         $arrModuleIds = array();
  107.         // Filter the disabled modules
  108.         foreach ($arrModules as $module)
  109.         {
  110.             if ($module['enable'] ?? null)
  111.             {
  112.                 $arrModuleIds[] = (int) $module['mod'];
  113.             }
  114.         }
  115.         // Get all modules in a single DB query
  116.         $objModules ModuleModel::findMultipleByIds($arrModuleIds);
  117.         if ($objModules !== null || \in_array(0$arrModuleIdstrue))
  118.         {
  119.             $arrMapper = array();
  120.             // Create a mapper array in case a module is included more than once (see #4849)
  121.             if ($objModules !== null)
  122.             {
  123.                 while ($objModules->next())
  124.                 {
  125.                     $arrMapper[$objModules->id] = $objModules->current();
  126.                 }
  127.             }
  128.             foreach ($arrModules as $arrModule)
  129.             {
  130.                 // Disabled module
  131.                 if (!$blnShowUnpublished && !($arrModule['enable'] ?? null))
  132.                 {
  133.                     continue;
  134.                 }
  135.                 // Replace the module ID with the module model
  136.                 if ($arrModule['mod'] > && isset($arrMapper[$arrModule['mod']]))
  137.                 {
  138.                     $arrModule['mod'] = $arrMapper[$arrModule['mod']];
  139.                 }
  140.                 // Generate the modules
  141.                 if (\in_array($arrModule['col'], $arrSections))
  142.                 {
  143.                     // Filter active sections (see #3273)
  144.                     if ($objLayout->rows != '2rwh' && $objLayout->rows != '3rw' && $arrModule['col'] == 'header')
  145.                     {
  146.                         continue;
  147.                     }
  148.                     if ($objLayout->cols != '2cll' && $objLayout->cols != '3cl' && $arrModule['col'] == 'left')
  149.                     {
  150.                         continue;
  151.                     }
  152.                     if ($objLayout->cols != '2clr' && $objLayout->cols != '3cl' && $arrModule['col'] == 'right')
  153.                     {
  154.                         continue;
  155.                     }
  156.                     if ($objLayout->rows != '2rwf' && $objLayout->rows != '3rw' && $arrModule['col'] == 'footer')
  157.                     {
  158.                         continue;
  159.                     }
  160.                     $this->Template->{$arrModule['col']} .= $this->getFrontendModule($arrModule['mod'], $arrModule['col']);
  161.                 }
  162.                 else
  163.                 {
  164.                     if (!isset($arrCustomSections[$arrModule['col']]))
  165.                     {
  166.                         $arrCustomSections[$arrModule['col']] = '';
  167.                     }
  168.                     $arrCustomSections[$arrModule['col']] .= $this->getFrontendModule($arrModule['mod'], $arrModule['col']);
  169.                 }
  170.             }
  171.         }
  172.         $this->Template->sections $arrCustomSections;
  173.         // Mark RTL languages (see #7171, #3360)
  174.         if ((\ResourceBundle::create($locale'ICUDATA'true)['layout']['characters'] ?? null) == 'right-to-left')
  175.         {
  176.             $this->Template->isRTL true;
  177.         }
  178.         // HOOK: modify the page or layout object
  179.         if (isset($GLOBALS['TL_HOOKS']['generatePage']) && \is_array($GLOBALS['TL_HOOKS']['generatePage']))
  180.         {
  181.             foreach ($GLOBALS['TL_HOOKS']['generatePage'] as $callback)
  182.             {
  183.                 $this->import($callback[0]);
  184.                 $this->{$callback[0]}->{$callback[1]}($objPage$objLayout$this);
  185.             }
  186.         }
  187.         $headBag $this->responseContext->get(HtmlHeadBag::class);
  188.         // Set the page title and description AFTER the modules have been generated
  189.         $this->Template->mainTitle $objPage->rootPageTitle;
  190.         $this->Template->pageTitle htmlspecialchars($headBag->getTitle(), ENT_QUOTES ENT_SUBSTITUTE ENT_HTML5);
  191.         // Remove shy-entities (see #2709)
  192.         $this->Template->mainTitle str_replace('[-]'''$this->Template->mainTitle);
  193.         $this->Template->pageTitle str_replace('[-]'''$this->Template->pageTitle);
  194.         // Meta robots tag
  195.         $this->Template->robots htmlspecialchars($headBag->getMetaRobots(), ENT_QUOTES ENT_SUBSTITUTE ENT_HTML5);
  196.         // Canonical
  197.         if ($objPage->enableCanonical)
  198.         {
  199.             $this->Template->canonical htmlspecialchars(
  200.                 str_replace(array('{''}'), array('%7B''%7D'), $headBag->getCanonicalUriForRequest($request)),
  201.                 ENT_QUOTES ENT_SUBSTITUTE ENT_HTML5
  202.             );
  203.         }
  204.         // Fall back to the default title tag
  205.         if (!$objLayout->titleTag)
  206.         {
  207.             $objLayout->titleTag '{{page::pageTitle}} - {{page::rootPageTitle}}';
  208.         }
  209.         // Assign the title and description
  210.         $this->Template->title strip_tags(System::getContainer()->get('contao.insert_tag.parser')->replaceInline($objLayout->titleTag));
  211.         $this->Template->description htmlspecialchars($headBag->getMetaDescription(), ENT_QUOTES ENT_SUBSTITUTE ENT_HTML5);
  212.         // Body onload and body classes
  213.         $this->Template->onload trim($objLayout->onload);
  214.         $this->Template->class trim($objLayout->cssClass ' ' $objPage->cssClass);
  215.         // Execute AFTER the modules have been generated and create footer scripts first
  216.         $this->createFooterScripts($objLayout$objPage);
  217.         $this->createHeaderScripts($objPage$objLayout);
  218.     }
  219.     /**
  220.      * Get a page layout and return it as database result object
  221.      *
  222.      * @param PageModel $objPage
  223.      *
  224.      * @return LayoutModel
  225.      */
  226.     protected function getPageLayout($objPage)
  227.     {
  228.         $objLayout LayoutModel::findByPk($objPage->layout);
  229.         // Die if there is no layout
  230.         if (null === $objLayout)
  231.         {
  232.             System::getContainer()->get('monolog.logger.contao.error')->error('Could not find layout ID "' $objPage->layout '"');
  233.             throw new NoLayoutSpecifiedException('No layout specified');
  234.         }
  235.         $objPage->hasJQuery $objLayout->addJQuery;
  236.         $objPage->hasMooTools $objLayout->addMooTools;
  237.         // HOOK: modify the page or layout object (see #4736)
  238.         if (isset($GLOBALS['TL_HOOKS']['getPageLayout']) && \is_array($GLOBALS['TL_HOOKS']['getPageLayout']))
  239.         {
  240.             foreach ($GLOBALS['TL_HOOKS']['getPageLayout'] as $callback)
  241.             {
  242.                 $this->import($callback[0]);
  243.                 $this->{$callback[0]}->{$callback[1]}($objPage$objLayout$this);
  244.             }
  245.         }
  246.         return $objLayout;
  247.     }
  248.     /**
  249.      * Create a new template
  250.      *
  251.      * @param PageModel   $objPage
  252.      * @param LayoutModel $objLayout
  253.      */
  254.     protected function createTemplate($objPage$objLayout)
  255.     {
  256.         $this->Template = new FrontendTemplate($objPage->template);
  257.         $this->Template->viewport '';
  258.         $this->Template->framework '';
  259.         $arrFramework StringUtil::deserialize($objLayout->framework);
  260.         // Generate the CSS framework
  261.         if (\is_array($arrFramework) && \in_array('layout.css'$arrFramework))
  262.         {
  263.             $strFramework '';
  264.             if (\in_array('responsive.css'$arrFramework))
  265.             {
  266.                 $this->Template->viewport '<meta name="viewport" content="width=device-width,initial-scale=1.0">' "\n";
  267.             }
  268.             // Wrapper
  269.             if ($objLayout->static)
  270.             {
  271.                 $arrSize StringUtil::deserialize($objLayout->width);
  272.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  273.                 {
  274.                     $arrMargin = array('left'=>'0 auto 0 0''center'=>'0 auto''right'=>'0 0 0 auto');
  275.                     $strFramework .= sprintf('#wrapper{width:%s;margin:%s}'$arrSize['value'] . $arrSize['unit'], $arrMargin[$objLayout->align]);
  276.                 }
  277.             }
  278.             // Header
  279.             if ($objLayout->rows == '2rwh' || $objLayout->rows == '3rw')
  280.             {
  281.                 $arrSize StringUtil::deserialize($objLayout->headerHeight);
  282.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  283.                 {
  284.                     $strFramework .= sprintf('#header{height:%s}'$arrSize['value'] . $arrSize['unit']);
  285.                 }
  286.             }
  287.             $strContainer '';
  288.             // Left column
  289.             if ($objLayout->cols == '2cll' || $objLayout->cols == '3cl')
  290.             {
  291.                 $arrSize StringUtil::deserialize($objLayout->widthLeft);
  292.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  293.                 {
  294.                     $strFramework .= sprintf('#left{width:%s;right:%s}'$arrSize['value'] . $arrSize['unit'], $arrSize['value'] . $arrSize['unit']);
  295.                     $strContainer .= sprintf('padding-left:%s;'$arrSize['value'] . $arrSize['unit']);
  296.                 }
  297.             }
  298.             // Right column
  299.             if ($objLayout->cols == '2clr' || $objLayout->cols == '3cl')
  300.             {
  301.                 $arrSize StringUtil::deserialize($objLayout->widthRight);
  302.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  303.                 {
  304.                     $strFramework .= sprintf('#right{width:%s}'$arrSize['value'] . $arrSize['unit']);
  305.                     $strContainer .= sprintf('padding-right:%s;'$arrSize['value'] . $arrSize['unit']);
  306.                 }
  307.             }
  308.             // Main column
  309.             if ($strContainer)
  310.             {
  311.                 $strFramework .= sprintf('#container{%s}'substr($strContainer0, -1));
  312.             }
  313.             // Footer
  314.             if ($objLayout->rows == '2rwf' || $objLayout->rows == '3rw')
  315.             {
  316.                 $arrSize StringUtil::deserialize($objLayout->footerHeight);
  317.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  318.                 {
  319.                     $strFramework .= sprintf('#footer{height:%s}'$arrSize['value'] . $arrSize['unit']);
  320.                 }
  321.             }
  322.             // Add the layout specific CSS
  323.             if ($strFramework)
  324.             {
  325.                 $this->Template->framework Template::generateInlineStyle($strFramework) . "\n";
  326.             }
  327.         }
  328.         // Overwrite the viewport tag (see #6251)
  329.         if ($objLayout->viewport)
  330.         {
  331.             $this->Template->viewport '<meta name="viewport" content="' $objLayout->viewport '">' "\n";
  332.         }
  333.         $this->Template->mooScripts '';
  334.         // Make sure TL_JAVASCRIPT exists (see #4890)
  335.         if (isset($GLOBALS['TL_JAVASCRIPT']) && \is_array($GLOBALS['TL_JAVASCRIPT']))
  336.         {
  337.             $arrAppendJs $GLOBALS['TL_JAVASCRIPT'];
  338.             $GLOBALS['TL_JAVASCRIPT'] = array();
  339.         }
  340.         else
  341.         {
  342.             $arrAppendJs = array();
  343.             $GLOBALS['TL_JAVASCRIPT'] = array();
  344.         }
  345.         // jQuery scripts
  346.         if ($objLayout->addJQuery)
  347.         {
  348.             $GLOBALS['TL_JAVASCRIPT'][] = 'assets/jquery/js/jquery.min.js|static';
  349.         }
  350.         // MooTools scripts
  351.         if ($objLayout->addMooTools)
  352.         {
  353.             $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/js/mootools.min.js|static';
  354.         }
  355.         // Check whether TL_APPEND_JS exists (see #4890)
  356.         if (!empty($arrAppendJs))
  357.         {
  358.             $GLOBALS['TL_JAVASCRIPT'] = array_merge($GLOBALS['TL_JAVASCRIPT'], $arrAppendJs);
  359.         }
  360.         // Initialize the sections
  361.         $this->Template->header '';
  362.         $this->Template->left '';
  363.         $this->Template->main '';
  364.         $this->Template->right '';
  365.         $this->Template->footer '';
  366.         // Initialize the custom layout sections
  367.         $this->Template->sections = array();
  368.         $this->Template->positions = array();
  369.         if ($objLayout->sections)
  370.         {
  371.             $arrPositions = array();
  372.             $arrSections StringUtil::deserialize($objLayout->sections);
  373.             if (!empty($arrSections) && \is_array($arrSections))
  374.             {
  375.                 foreach ($arrSections as $v)
  376.                 {
  377.                     $arrPositions[$v['position']][$v['id']] = $v;
  378.                 }
  379.             }
  380.             $this->Template->positions $arrPositions;
  381.         }
  382.         // Add the check_cookies image and the request token script if needed
  383.         if ($objPage->alwaysLoadFromCache)
  384.         {
  385.             $GLOBALS['TL_BODY'][] = sprintf('<img src="%s" width="1" height="1" class="invisible" alt aria-hidden="true" onload="this.parentNode.removeChild(this)">'System::getContainer()->get('router')->generate('contao_frontend_check_cookies'));
  386.             $GLOBALS['TL_BODY'][] = sprintf('<script src="%s" async></script>'System::getContainer()->get('router')->generate('contao_frontend_request_token_script'));
  387.         }
  388.         // Default settings
  389.         $this->Template->layout $objLayout;
  390.         $this->Template->language $GLOBALS['TL_LANGUAGE'];
  391.         $this->Template->charset System::getContainer()->getParameter('kernel.charset');
  392.         $this->Template->base Environment::get('base');
  393.         $this->Template->isRTL false;
  394.     }
  395.     /**
  396.      * Create all header scripts
  397.      *
  398.      * @param PageModel   $objPage
  399.      * @param LayoutModel $objLayout
  400.      */
  401.     protected function createHeaderScripts($objPage$objLayout)
  402.     {
  403.         $strStyleSheets '';
  404.         $strCcStyleSheets '';
  405.         $arrStyleSheets StringUtil::deserialize($objLayout->stylesheet);
  406.         $arrFramework StringUtil::deserialize($objLayout->framework);
  407.         // Add the Contao CSS framework style sheets
  408.         if (\is_array($arrFramework))
  409.         {
  410.             foreach ($arrFramework as $strFile)
  411.             {
  412.                 if ($strFile != 'tinymce.css')
  413.                 {
  414.                     $GLOBALS['TL_FRAMEWORK_CSS'][] = 'assets/contao/css/' basename($strFile'.css') . '.min.css';
  415.                 }
  416.             }
  417.         }
  418.         // Make sure TL_USER_CSS is set
  419.         if (!isset($GLOBALS['TL_USER_CSS']) || !\is_array($GLOBALS['TL_USER_CSS']))
  420.         {
  421.             $GLOBALS['TL_USER_CSS'] = array();
  422.         }
  423.         // User style sheets
  424.         if (\is_array($arrStyleSheets) && isset($arrStyleSheets[0]))
  425.         {
  426.             $objStylesheets StyleSheetModel::findByIds($arrStyleSheets);
  427.             if ($objStylesheets !== null)
  428.             {
  429.                 while ($objStylesheets->next())
  430.                 {
  431.                     $media implode(','StringUtil::deserialize($objStylesheets->media));
  432.                     // Overwrite the media type with a custom media query
  433.                     if ($objStylesheets->mediaQuery)
  434.                     {
  435.                         $media $objStylesheets->mediaQuery;
  436.                     }
  437.                     // Style sheets with a CC or a combination of font-face and media-type != all cannot be aggregated (see #5216)
  438.                     if ($objStylesheets->cc || ($objStylesheets->hasFontFace && $media != 'all'))
  439.                     {
  440.                         $strStyleSheet '';
  441.                         // External style sheet
  442.                         if ($objStylesheets->type == 'external')
  443.                         {
  444.                             $objFile FilesModel::findByPk($objStylesheets->singleSRC);
  445.                             if ($objFile !== null)
  446.                             {
  447.                                 $strStyleSheet Template::generateStyleTag(Controller::addFilesUrlTo($objFile->path), $medianull);
  448.                             }
  449.                         }
  450.                         else
  451.                         {
  452.                             $strStyleSheet Template::generateStyleTag(Controller::addAssetsUrlTo('assets/css/' $objStylesheets->name '.css'), $mediamax($objStylesheets->tstamp$objStylesheets->tstamp2$objStylesheets->tstamp3));
  453.                         }
  454.                         if ($objStylesheets->cc)
  455.                         {
  456.                             $strStyleSheet '<!--[' $objStylesheets->cc ']>' $strStyleSheet '<![endif]-->';
  457.                         }
  458.                         $strCcStyleSheets .= $strStyleSheet "\n";
  459.                     }
  460.                     elseif ($objStylesheets->type == 'external')
  461.                     {
  462.                         $objFile FilesModel::findByPk($objStylesheets->singleSRC);
  463.                         if ($objFile !== null)
  464.                         {
  465.                             $GLOBALS['TL_USER_CSS'][] = $objFile->path '|' $media '|static';
  466.                         }
  467.                     }
  468.                     else
  469.                     {
  470.                         $GLOBALS['TL_USER_CSS'][] = 'assets/css/' $objStylesheets->name '.css|' $media '|static|' max($objStylesheets->tstamp$objStylesheets->tstamp2$objStylesheets->tstamp3);
  471.                     }
  472.                 }
  473.             }
  474.         }
  475.         $arrExternal StringUtil::deserialize($objLayout->external);
  476.         // External style sheets
  477.         if (!empty($arrExternal) && \is_array($arrExternal))
  478.         {
  479.             // Get the file entries from the database
  480.             $objFiles FilesModel::findMultipleByUuids($arrExternal);
  481.             $projectDir System::getContainer()->getParameter('kernel.project_dir');
  482.             if ($objFiles !== null)
  483.             {
  484.                 $arrFiles = array();
  485.                 while ($objFiles->next())
  486.                 {
  487.                     if (file_exists($projectDir '/' $objFiles->path))
  488.                     {
  489.                         $arrFiles[] = $objFiles->path '|static';
  490.                     }
  491.                 }
  492.                 // Inject the external style sheets before or after the internal ones (see #6937)
  493.                 if ($objLayout->loadingOrder == 'external_first')
  494.                 {
  495.                     array_splice($GLOBALS['TL_USER_CSS'], 00$arrFiles);
  496.                 }
  497.                 else
  498.                 {
  499.                     array_splice($GLOBALS['TL_USER_CSS'], \count($GLOBALS['TL_USER_CSS']), 0$arrFiles);
  500.                 }
  501.             }
  502.         }
  503.         $nonce ContaoFramework::getNonce();
  504.         // Add a placeholder for dynamic style sheets (see #4203)
  505.         $strStyleSheets .= "[[TL_CSS_$nonce]]";
  506.         // Always add conditional style sheets at the end
  507.         $strStyleSheets .= $strCcStyleSheets;
  508.         // Add a placeholder for dynamic <head> tags (see #4203)
  509.         $strHeadTags "[[TL_HEAD_$nonce]]";
  510.         // Add the analytics scripts
  511.         if ($objLayout->analytics)
  512.         {
  513.             $arrAnalytics StringUtil::deserialize($objLayout->analyticstrue);
  514.             foreach ($arrAnalytics as $strTemplate)
  515.             {
  516.                 if ($strTemplate)
  517.                 {
  518.                     $objTemplate = new FrontendTemplate($strTemplate);
  519.                     $strHeadTags .= $objTemplate->parse();
  520.                 }
  521.             }
  522.         }
  523.         // Add the user <head> tags
  524.         if ($strHead trim($objLayout->head ?? ''))
  525.         {
  526.             $strHeadTags .= $strHead "\n";
  527.         }
  528.         $this->Template->stylesheets $strStyleSheets;
  529.         $this->Template->head $strHeadTags;
  530.     }
  531.     /**
  532.      * Create all footer scripts
  533.      *
  534.      * @param LayoutModel $objLayout
  535.      * @param PageModel   $objPage
  536.      *
  537.      * @todo Change the method signature to ($objPage, $objLayout) in Contao 5.0
  538.      */
  539.     protected function createFooterScripts($objLayout$objPage null)
  540.     {
  541.         $strScripts '';
  542.         $nonce ContaoFramework::getNonce();
  543.         // jQuery
  544.         if ($objLayout->addJQuery)
  545.         {
  546.             $arrJquery StringUtil::deserialize($objLayout->jquerytrue);
  547.             foreach ($arrJquery as $strTemplate)
  548.             {
  549.                 if ($strTemplate)
  550.                 {
  551.                     $objTemplate = new FrontendTemplate($strTemplate);
  552.                     $strScripts .= $objTemplate->parse();
  553.                 }
  554.             }
  555.             // Add a placeholder for dynamic scripts (see #4203)
  556.             $strScripts .= "[[TL_JQUERY_$nonce]]";
  557.         }
  558.         // MooTools
  559.         if ($objLayout->addMooTools)
  560.         {
  561.             $arrMootools StringUtil::deserialize($objLayout->mootoolstrue);
  562.             foreach ($arrMootools as $strTemplate)
  563.             {
  564.                 if ($strTemplate)
  565.                 {
  566.                     $objTemplate = new FrontendTemplate($strTemplate);
  567.                     $strScripts .= $objTemplate->parse();
  568.                 }
  569.             }
  570.             // Add a placeholder for dynamic scripts (see #4203)
  571.             $strScripts .= "[[TL_MOOTOOLS_$nonce]]";
  572.         }
  573.         // Add the framework-agnostic JavaScripts
  574.         if ($objLayout->scripts)
  575.         {
  576.             $arrScripts StringUtil::deserialize($objLayout->scriptstrue);
  577.             foreach ($arrScripts as $strTemplate)
  578.             {
  579.                 if ($strTemplate)
  580.                 {
  581.                     $objTemplate = new FrontendTemplate($strTemplate);
  582.                     $strScripts .= $objTemplate->parse();
  583.                 }
  584.             }
  585.         }
  586.         // Add a placeholder for dynamic scripts (see #4203, #5583)
  587.         $strScripts .= "[[TL_BODY_$nonce]]";
  588.         // Add the external JavaScripts
  589.         $arrExternalJs StringUtil::deserialize($objLayout->externalJs);
  590.         // Get the file entries from the database
  591.         $objFiles FilesModel::findMultipleByUuids($arrExternalJs);
  592.         $projectDir System::getContainer()->getParameter('kernel.project_dir');
  593.         if ($objFiles !== null)
  594.         {
  595.             while ($objFiles->next())
  596.             {
  597.                 if (file_exists($projectDir '/' $objFiles->path))
  598.                 {
  599.                     $strScripts .= Template::generateScriptTag($objFiles->pathfalsenull);
  600.                 }
  601.             }
  602.         }
  603.         // Add the custom JavaScript
  604.         if ($objLayout->script)
  605.         {
  606.             $strScripts .= "\n" trim($objLayout->script) . "\n";
  607.         }
  608.         $this->Template->mootools $strScripts;
  609.         $this->Template->jsonLdScripts = function ()
  610.         {
  611.             if (!$this->responseContext->isInitialized(JsonLdManager::class))
  612.             {
  613.                 return '';
  614.             }
  615.             /** @var JsonLdManager $jsonLdManager */
  616.             $jsonLdManager $this->responseContext->get(JsonLdManager::class);
  617.             return $jsonLdManager->collectFinalScriptFromGraphs();
  618.         };
  619.     }
  620. }
  621. class_alias(PageRegular::class, 'PageRegular');