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

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$arrModuleIdstrue))
  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'] > && 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($strContainer0, -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), $medianull);
  438.                             }
  439.                         }
  440.                         else
  441.                         {
  442.                             $strStyleSheet Template::generateStyleTag(Controller::addAssetsUrlTo('assets/css/' $objStylesheets->name '.css'), $mediamax($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'], 00$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->analyticstrue);
  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->jquerytrue);
  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->mootoolstrue);
  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->scriptstrue);
  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->pathfalsenull);
  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');