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

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