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

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\Util\PackageUtil;
  12. use Symfony\Component\Cache\Adapter\AdapterInterface;
  13. use Symfony\Component\HttpFoundation\Response;
  14. /**
  15.  * Provide methods to handle a regular front end page.
  16.  *
  17.  * @author Leo Feyer <https://github.com/leofeyer>
  18.  */
  19. #[\AllowDynamicProperties]
  20. class PageRegular extends Frontend
  21. {
  22.     /**
  23.      * Generate a regular page
  24.      *
  25.      * @param PageModel $objPage
  26.      * @param boolean   $blnCheckRequest
  27.      *
  28.      * @deprecated Deprecated since Contao 4.9, to be removed in Contao 5. Use the PageRegular::getResponse() method instead.
  29.      */
  30.     public function generate($objPage$blnCheckRequest=false)
  31.     {
  32.         @trigger_error('Using PageRegular::generate() has been deprecated in Contao 4.9 and will be removed in Contao 5.0. Use the PageRegular::getResponse() method instead.'E_USER_DEPRECATED);
  33.         $this->prepare($objPage);
  34.         $this->Template->output($blnCheckRequest);
  35.     }
  36.     /**
  37.      * Return a response object
  38.      *
  39.      * @param PageModel $objPage
  40.      * @param boolean   $blnCheckRequest
  41.      *
  42.      * @return Response
  43.      */
  44.     public function getResponse($objPage$blnCheckRequest=false)
  45.     {
  46.         $this->prepare($objPage);
  47.         return $this->Template->getResponse($blnCheckRequest);
  48.     }
  49.     /**
  50.      * Generate a regular page
  51.      *
  52.      * @param PageModel $objPage
  53.      *
  54.      * @internal Do not call this method in your code. It will be made private in Contao 5.0.
  55.      */
  56.     protected function prepare($objPage)
  57.     {
  58.         $GLOBALS['TL_KEYWORDS'] = '';
  59.         $GLOBALS['TL_LANGUAGE'] = $objPage->language;
  60.         $locale str_replace('-''_'$objPage->language);
  61.         $container System::getContainer();
  62.         $container->get('request_stack')->getCurrentRequest()->setLocale($locale);
  63.         $container->get('translator')->setLocale($locale);
  64.         System::loadLanguageFile('default');
  65.         // Static URLs
  66.         $this->setStaticUrls();
  67.         // Get the page layout
  68.         $objLayout $this->getPageLayout($objPage);
  69.         /** @var ThemeModel $objTheme */
  70.         $objTheme $objLayout->getRelated('pid');
  71.         // Set the default image densities
  72.         $container->get('contao.image.picture_factory')->setDefaultDensities($objLayout->defaultImageDensities);
  73.         // Store the layout ID
  74.         $objPage->layoutId $objLayout->id;
  75.         // Set the layout template and template group
  76.         $objPage->template $objLayout->template ?: 'fe_page';
  77.         $objPage->templateGroup $objTheme->templates;
  78.         // Minify the markup
  79.         $objPage->minifyMarkup $objLayout->minifyMarkup;
  80.         // Initialize the template
  81.         $this->createTemplate($objPage$objLayout);
  82.         // Initialize modules and sections
  83.         $arrCustomSections = array();
  84.         $arrSections = array('header''left''right''main''footer');
  85.         $arrModules StringUtil::deserialize($objLayout->modules);
  86.         $arrModuleIds = array();
  87.         // Filter the disabled modules
  88.         foreach ($arrModules as $module)
  89.         {
  90.             if ($module['enable'])
  91.             {
  92.                 $arrModuleIds[] = (int) $module['mod'];
  93.             }
  94.         }
  95.         // Get all modules in a single DB query
  96.         $objModules ModuleModel::findMultipleByIds($arrModuleIds);
  97.         if ($objModules !== null || \in_array(0$arrModuleIdstrue))
  98.         {
  99.             $arrMapper = array();
  100.             // Create a mapper array in case a module is included more than once (see #4849)
  101.             if ($objModules !== null)
  102.             {
  103.                 while ($objModules->next())
  104.                 {
  105.                     $arrMapper[$objModules->id] = $objModules->current();
  106.                 }
  107.             }
  108.             foreach ($arrModules as $arrModule)
  109.             {
  110.                 // Disabled module
  111.                 if (!BE_USER_LOGGED_IN && !$arrModule['enable'])
  112.                 {
  113.                     continue;
  114.                 }
  115.                 // Replace the module ID with the module model
  116.                 if ($arrModule['mod'] > && isset($arrMapper[$arrModule['mod']]))
  117.                 {
  118.                     $arrModule['mod'] = $arrMapper[$arrModule['mod']];
  119.                 }
  120.                 // Generate the modules
  121.                 if (\in_array($arrModule['col'], $arrSections))
  122.                 {
  123.                     // Filter active sections (see #3273)
  124.                     if ($objLayout->rows != '2rwh' && $objLayout->rows != '3rw' && $arrModule['col'] == 'header')
  125.                     {
  126.                         continue;
  127.                     }
  128.                     if ($objLayout->cols != '2cll' && $objLayout->cols != '3cl' && $arrModule['col'] == 'left')
  129.                     {
  130.                         continue;
  131.                     }
  132.                     if ($objLayout->cols != '2clr' && $objLayout->cols != '3cl' && $arrModule['col'] == 'right')
  133.                     {
  134.                         continue;
  135.                     }
  136.                     if ($objLayout->rows != '2rwf' && $objLayout->rows != '3rw' && $arrModule['col'] == 'footer')
  137.                     {
  138.                         continue;
  139.                     }
  140.                     $this->Template->{$arrModule['col']} .= $this->getFrontendModule($arrModule['mod'], $arrModule['col']);
  141.                 }
  142.                 else
  143.                 {
  144.                     $arrCustomSections[$arrModule['col']] .= $this->getFrontendModule($arrModule['mod'], $arrModule['col']);
  145.                 }
  146.             }
  147.         }
  148.         $this->Template->sections $arrCustomSections;
  149.         // Mark RTL languages (see #7171, #3360)
  150.         if ((\ResourceBundle::create($locale'ICUDATA'true)['layout']['characters'] ?? null) == 'right-to-left')
  151.         {
  152.             $this->Template->isRTL true;
  153.         }
  154.         // HOOK: modify the page or layout object
  155.         if (isset($GLOBALS['TL_HOOKS']['generatePage']) && \is_array($GLOBALS['TL_HOOKS']['generatePage']))
  156.         {
  157.             foreach ($GLOBALS['TL_HOOKS']['generatePage'] as $callback)
  158.             {
  159.                 $this->import($callback[0]);
  160.                 $this->{$callback[0]}->{$callback[1]}($objPage$objLayout$this);
  161.             }
  162.         }
  163.         // Set the page title and description AFTER the modules have been generated
  164.         $this->Template->mainTitle $objPage->rootPageTitle;
  165.         $this->Template->pageTitle $objPage->pageTitle ?: $objPage->title;
  166.         // Meta robots tag
  167.         $this->Template->robots $objPage->robots ?: 'index,follow';
  168.         // Remove shy-entities (see #2709)
  169.         $this->Template->mainTitle str_replace('[-]'''$this->Template->mainTitle);
  170.         $this->Template->pageTitle str_replace('[-]'''$this->Template->pageTitle);
  171.         // Fall back to the default title tag
  172.         if (!$objLayout->titleTag)
  173.         {
  174.             $objLayout->titleTag '{{page::pageTitle}} - {{page::rootPageTitle}}';
  175.         }
  176.         // Assign the title and description
  177.         $this->Template->title strip_tags($this->replaceInsertTags($objLayout->titleTag));
  178.         $this->Template->description str_replace(array("\n""\r"'"'), array(' '''''), $objPage->description ?? '');
  179.         // Body onload and body classes
  180.         $this->Template->onload trim($objLayout->onload);
  181.         $this->Template->class trim($objLayout->cssClass ' ' $objPage->cssClass);
  182.         // Execute AFTER the modules have been generated and create footer scripts first
  183.         $this->createFooterScripts($objLayout$objPage);
  184.         $this->createHeaderScripts($objPage$objLayout);
  185.     }
  186.     /**
  187.      * Get a page layout and return it as database result object
  188.      *
  189.      * @param PageModel $objPage
  190.      *
  191.      * @return LayoutModel
  192.      */
  193.     protected function getPageLayout($objPage)
  194.     {
  195.         $objLayout LayoutModel::findByPk($objPage->layout);
  196.         // Die if there is no layout
  197.         if (null === $objLayout)
  198.         {
  199.             $this->log('Could not find layout ID "' $objPage->layout '"'__METHOD__TL_ERROR);
  200.             throw new NoLayoutSpecifiedException('No layout specified');
  201.         }
  202.         $objPage->hasJQuery $objLayout->addJQuery;
  203.         $objPage->hasMooTools $objLayout->addMooTools;
  204.         // HOOK: modify the page or layout object (see #4736)
  205.         if (isset($GLOBALS['TL_HOOKS']['getPageLayout']) && \is_array($GLOBALS['TL_HOOKS']['getPageLayout']))
  206.         {
  207.             foreach ($GLOBALS['TL_HOOKS']['getPageLayout'] as $callback)
  208.             {
  209.                 $this->import($callback[0]);
  210.                 $this->{$callback[0]}->{$callback[1]}($objPage$objLayout$this);
  211.             }
  212.         }
  213.         return $objLayout;
  214.     }
  215.     /**
  216.      * Create a new template
  217.      *
  218.      * @param PageModel   $objPage
  219.      * @param LayoutModel $objLayout
  220.      */
  221.     protected function createTemplate($objPage$objLayout)
  222.     {
  223.         $this->Template = new FrontendTemplate($objPage->template);
  224.         $this->Template->viewport '';
  225.         $this->Template->framework '';
  226.         $arrFramework StringUtil::deserialize($objLayout->framework);
  227.         // Generate the CSS framework
  228.         if (\is_array($arrFramework) && \in_array('layout.css'$arrFramework))
  229.         {
  230.             $strFramework '';
  231.             if (\in_array('responsive.css'$arrFramework))
  232.             {
  233.                 $this->Template->viewport '<meta name="viewport" content="width=device-width,initial-scale=1.0">' "\n";
  234.             }
  235.             // Wrapper
  236.             if ($objLayout->static)
  237.             {
  238.                 $arrSize StringUtil::deserialize($objLayout->width);
  239.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  240.                 {
  241.                     $arrMargin = array('left'=>'0 auto 0 0''center'=>'0 auto''right'=>'0 0 0 auto');
  242.                     $strFramework .= sprintf('#wrapper{width:%s;margin:%s}'$arrSize['value'] . $arrSize['unit'], $arrMargin[$objLayout->align]);
  243.                 }
  244.             }
  245.             // Header
  246.             if ($objLayout->rows == '2rwh' || $objLayout->rows == '3rw')
  247.             {
  248.                 $arrSize StringUtil::deserialize($objLayout->headerHeight);
  249.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  250.                 {
  251.                     $strFramework .= sprintf('#header{height:%s}'$arrSize['value'] . $arrSize['unit']);
  252.                 }
  253.             }
  254.             $strContainer '';
  255.             // Left column
  256.             if ($objLayout->cols == '2cll' || $objLayout->cols == '3cl')
  257.             {
  258.                 $arrSize StringUtil::deserialize($objLayout->widthLeft);
  259.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  260.                 {
  261.                     $strFramework .= sprintf('#left{width:%s;right:%s}'$arrSize['value'] . $arrSize['unit'], $arrSize['value'] . $arrSize['unit']);
  262.                     $strContainer .= sprintf('padding-left:%s;'$arrSize['value'] . $arrSize['unit']);
  263.                 }
  264.             }
  265.             // Right column
  266.             if ($objLayout->cols == '2clr' || $objLayout->cols == '3cl')
  267.             {
  268.                 $arrSize StringUtil::deserialize($objLayout->widthRight);
  269.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  270.                 {
  271.                     $strFramework .= sprintf('#right{width:%s}'$arrSize['value'] . $arrSize['unit']);
  272.                     $strContainer .= sprintf('padding-right:%s;'$arrSize['value'] . $arrSize['unit']);
  273.                 }
  274.             }
  275.             // Main column
  276.             if ($strContainer)
  277.             {
  278.                 $strFramework .= sprintf('#container{%s}'substr($strContainer0, -1));
  279.             }
  280.             // Footer
  281.             if ($objLayout->rows == '2rwf' || $objLayout->rows == '3rw')
  282.             {
  283.                 $arrSize StringUtil::deserialize($objLayout->footerHeight);
  284.                 if (isset($arrSize['value']) && $arrSize['value'] && $arrSize['value'] >= 0)
  285.                 {
  286.                     $strFramework .= sprintf('#footer{height:%s}'$arrSize['value'] . $arrSize['unit']);
  287.                 }
  288.             }
  289.             // Add the layout specific CSS
  290.             if ($strFramework)
  291.             {
  292.                 $this->Template->framework Template::generateInlineStyle($strFramework) . "\n";
  293.             }
  294.         }
  295.         // Overwrite the viewport tag (see #6251)
  296.         if ($objLayout->viewport)
  297.         {
  298.             $this->Template->viewport '<meta name="viewport" content="' $objLayout->viewport '">' "\n";
  299.         }
  300.         $this->Template->mooScripts '';
  301.         // Make sure TL_JAVASCRIPT exists (see #4890)
  302.         if (isset($GLOBALS['TL_JAVASCRIPT']) && \is_array($GLOBALS['TL_JAVASCRIPT']))
  303.         {
  304.             $arrAppendJs $GLOBALS['TL_JAVASCRIPT'];
  305.             $GLOBALS['TL_JAVASCRIPT'] = array();
  306.         }
  307.         else
  308.         {
  309.             $arrAppendJs = array();
  310.             $GLOBALS['TL_JAVASCRIPT'] = array();
  311.         }
  312.         $container System::getContainer();
  313.         $projectDir $container->getParameter('kernel.project_dir');
  314.         // jQuery scripts
  315.         if ($objLayout->addJQuery)
  316.         {
  317.             if ($objLayout->jSource == 'j_googleapis' || $objLayout->jSource == 'j_fallback')
  318.             {
  319.                 try
  320.                 {
  321.                     /** @var AdapterInterface $cache */
  322.                     $cache $container->get('cache.system');
  323.                     $hash $cache->getItem('contao.jquery_hash');
  324.                     if (!$hash->isHit())
  325.                     {
  326.                         $hash->set('sha256-' base64_encode(hash_file('sha256'$projectDir '/assets/jquery/js/jquery.min.js'true)));
  327.                         $cache->save($hash);
  328.                     }
  329.                     $this->Template->mooScripts .= Template::generateScriptTag('https://code.jquery.com/jquery-' PackageUtil::getNormalizedVersion('contao-components/jquery') . '.min.js'falsefalse$hash->get(), 'anonymous''no-referrer') . "\n";
  330.                     // Local fallback (thanks to DyaGa)
  331.                     if ($objLayout->jSource == 'j_fallback')
  332.                     {
  333.                         $this->Template->mooScripts .= Template::generateInlineScript('window.jQuery || document.write(\'<script src="' Controller::addAssetsUrlTo('assets/jquery/js/jquery.min.js') . '">\x3C/script>\')') . "\n";
  334.                     }
  335.                 }
  336.                 catch (\OutOfBoundsException $e)
  337.                 {
  338.                     $GLOBALS['TL_JAVASCRIPT'][] = 'assets/jquery/js/jquery.min.js|static';
  339.                 }
  340.             }
  341.             else
  342.             {
  343.                 $GLOBALS['TL_JAVASCRIPT'][] = 'assets/jquery/js/jquery.min.js|static';
  344.             }
  345.         }
  346.         // MooTools scripts
  347.         if ($objLayout->addMooTools)
  348.         {
  349.             if ($objLayout->mooSource == 'moo_googleapis' || $objLayout->mooSource == 'moo_fallback')
  350.             {
  351.                 try
  352.                 {
  353.                     $version PackageUtil::getNormalizedVersion('contao-components/mootools');
  354.                     if (version_compare($version'1.5.1''>'))
  355.                     {
  356.                         $this->Template->mooScripts .= Template::generateScriptTag('https://ajax.googleapis.com/ajax/libs/mootools/' $version '/mootools.min.js'falsefalsenull'anonymous''no-referrer') . "\n";
  357.                     }
  358.                     else
  359.                     {
  360.                         $this->Template->mooScripts .= Template::generateScriptTag('https://ajax.googleapis.com/ajax/libs/mootools/' $version '/mootools-yui-compressed.js'falsefalsenull'anonymous''no-referrer') . "\n";
  361.                     }
  362.                     // Local fallback (thanks to DyaGa)
  363.                     if ($objLayout->mooSource == 'moo_fallback')
  364.                     {
  365.                         $this->Template->mooScripts .= Template::generateInlineScript('window.MooTools || document.write(\'<script src="' Controller::addAssetsUrlTo('assets/mootools/js/mootools-core.min.js') . '">\x3C/script>\')') . "\n";
  366.                     }
  367.                     $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/js/mootools-more.min.js|static';
  368.                     $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/js/mootools-mobile.min.js|static';
  369.                 }
  370.                 catch (\OutOfBoundsException $e)
  371.                 {
  372.                     $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/js/mootools.min.js|static';
  373.                 }
  374.             }
  375.             else
  376.             {
  377.                 $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/js/mootools.min.js|static';
  378.             }
  379.         }
  380.         // Check whether TL_APPEND_JS exists (see #4890)
  381.         if (!empty($arrAppendJs))
  382.         {
  383.             $GLOBALS['TL_JAVASCRIPT'] = array_merge($GLOBALS['TL_JAVASCRIPT'], $arrAppendJs);
  384.         }
  385.         // Initialize the sections
  386.         $this->Template->header '';
  387.         $this->Template->left '';
  388.         $this->Template->main '';
  389.         $this->Template->right '';
  390.         $this->Template->footer '';
  391.         // Initialize the custom layout sections
  392.         $this->Template->sections = array();
  393.         $this->Template->positions = array();
  394.         if ($objLayout->sections)
  395.         {
  396.             $arrPositions = array();
  397.             $arrSections StringUtil::deserialize($objLayout->sections);
  398.             if (!empty($arrSections) && \is_array($arrSections))
  399.             {
  400.                 foreach ($arrSections as $v)
  401.                 {
  402.                     $arrPositions[$v['position']][$v['id']] = $v;
  403.                 }
  404.             }
  405.             $this->Template->positions $arrPositions;
  406.         }
  407.         // Add the check_cookies image and the request token script if needed
  408.         if ($objPage->alwaysLoadFromCache)
  409.         {
  410.             $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'));
  411.             $GLOBALS['TL_BODY'][] = sprintf('<script src="%s" async></script>'System::getContainer()->get('router')->generate('contao_frontend_request_token_script'));
  412.         }
  413.         // Default settings
  414.         $this->Template->layout $objLayout;
  415.         $this->Template->language $GLOBALS['TL_LANGUAGE'];
  416.         $this->Template->charset Config::get('characterSet');
  417.         $this->Template->base Environment::get('base');
  418.         $this->Template->isRTL false;
  419.     }
  420.     /**
  421.      * Create all header scripts
  422.      *
  423.      * @param PageModel   $objPage
  424.      * @param LayoutModel $objLayout
  425.      */
  426.     protected function createHeaderScripts($objPage$objLayout)
  427.     {
  428.         $strStyleSheets '';
  429.         $strCcStyleSheets '';
  430.         $arrStyleSheets StringUtil::deserialize($objLayout->stylesheet);
  431.         $arrFramework StringUtil::deserialize($objLayout->framework);
  432.         // Google web fonts
  433.         if ($objLayout->webfonts)
  434.         {
  435.             $strStyleSheets .= Template::generateStyleTag('https://fonts.googleapis.com/css?family=' str_replace('|''%7C'$objLayout->webfonts), 'all') . "\n";
  436.         }
  437.         // Add the Contao CSS framework style sheets
  438.         if (\is_array($arrFramework))
  439.         {
  440.             foreach ($arrFramework as $strFile)
  441.             {
  442.                 if ($strFile != 'tinymce.css')
  443.                 {
  444.                     $GLOBALS['TL_FRAMEWORK_CSS'][] = 'assets/contao/css/' basename($strFile'.css') . '.min.css';
  445.                 }
  446.             }
  447.         }
  448.         // Make sure TL_USER_CSS is set
  449.         if (!\is_array($GLOBALS['TL_USER_CSS']))
  450.         {
  451.             $GLOBALS['TL_USER_CSS'] = array();
  452.         }
  453.         // User style sheets
  454.         if (\is_array($arrStyleSheets) && isset($arrStyleSheets[0]))
  455.         {
  456.             $objStylesheets StyleSheetModel::findByIds($arrStyleSheets);
  457.             if ($objStylesheets !== null)
  458.             {
  459.                 while ($objStylesheets->next())
  460.                 {
  461.                     $media implode(','StringUtil::deserialize($objStylesheets->media));
  462.                     // Overwrite the media type with a custom media query
  463.                     if ($objStylesheets->mediaQuery)
  464.                     {
  465.                         $media $objStylesheets->mediaQuery;
  466.                     }
  467.                     // Style sheets with a CC or a combination of font-face and media-type != all cannot be aggregated (see #5216)
  468.                     if ($objStylesheets->cc || ($objStylesheets->hasFontFace && $media != 'all'))
  469.                     {
  470.                         $strStyleSheet '';
  471.                         // External style sheet
  472.                         if ($objStylesheets->type == 'external')
  473.                         {
  474.                             $objFile FilesModel::findByPk($objStylesheets->singleSRC);
  475.                             if ($objFile !== null)
  476.                             {
  477.                                 $strStyleSheet Template::generateStyleTag(Controller::addFilesUrlTo($objFile->path), $medianull);
  478.                             }
  479.                         }
  480.                         else
  481.                         {
  482.                             $strStyleSheet Template::generateStyleTag(Controller::addAssetsUrlTo('assets/css/' $objStylesheets->name '.css'), $mediamax($objStylesheets->tstamp$objStylesheets->tstamp2$objStylesheets->tstamp3));
  483.                         }
  484.                         if ($objStylesheets->cc)
  485.                         {
  486.                             $strStyleSheet '<!--[' $objStylesheets->cc ']>' $strStyleSheet '<![endif]-->';
  487.                         }
  488.                         $strCcStyleSheets .= $strStyleSheet "\n";
  489.                     }
  490.                     elseif ($objStylesheets->type == 'external')
  491.                     {
  492.                         $objFile FilesModel::findByPk($objStylesheets->singleSRC);
  493.                         if ($objFile !== null)
  494.                         {
  495.                             $GLOBALS['TL_USER_CSS'][] = $objFile->path '|' $media '|static';
  496.                         }
  497.                     }
  498.                     else
  499.                     {
  500.                         $GLOBALS['TL_USER_CSS'][] = 'assets/css/' $objStylesheets->name '.css|' $media '|static|' max($objStylesheets->tstamp$objStylesheets->tstamp2$objStylesheets->tstamp3);
  501.                     }
  502.                 }
  503.             }
  504.         }
  505.         $arrExternal StringUtil::deserialize($objLayout->external);
  506.         // External style sheets
  507.         if (!empty($arrExternal) && \is_array($arrExternal))
  508.         {
  509.             // Consider the sorting order (see #5038)
  510.             if ($objLayout->orderExt)
  511.             {
  512.                 $tmp StringUtil::deserialize($objLayout->orderExt);
  513.                 if (!empty($tmp) && \is_array($tmp))
  514.                 {
  515.                     // Remove all values
  516.                     $arrOrder array_map(static function () {}, array_flip($tmp));
  517.                     // Move the matching elements to their position in $arrOrder
  518.                     foreach ($arrExternal as $k=>$v)
  519.                     {
  520.                         if (\array_key_exists($v$arrOrder))
  521.                         {
  522.                             $arrOrder[$v] = $v;
  523.                             unset($arrExternal[$k]);
  524.                         }
  525.                     }
  526.                     // Append the left-over style sheets at the end
  527.                     if (!empty($arrExternal))
  528.                     {
  529.                         $arrOrder array_merge($arrOrderarray_values($arrExternal));
  530.                     }
  531.                     // Remove empty (unreplaced) entries
  532.                     $arrExternal array_values(array_filter($arrOrder));
  533.                     unset($arrOrder);
  534.                 }
  535.             }
  536.             // Get the file entries from the database
  537.             $objFiles FilesModel::findMultipleByUuids($arrExternal);
  538.             $projectDir System::getContainer()->getParameter('kernel.project_dir');
  539.             if ($objFiles !== null)
  540.             {
  541.                 $arrFiles = array();
  542.                 while ($objFiles->next())
  543.                 {
  544.                     if (file_exists($projectDir '/' $objFiles->path))
  545.                     {
  546.                         $arrFiles[] = $objFiles->path '|static';
  547.                     }
  548.                 }
  549.                 // Inject the external style sheets before or after the internal ones (see #6937)
  550.                 if ($objLayout->loadingOrder == 'external_first')
  551.                 {
  552.                     array_splice($GLOBALS['TL_USER_CSS'], 00$arrFiles);
  553.                 }
  554.                 else
  555.                 {
  556.                     array_splice($GLOBALS['TL_USER_CSS'], \count($GLOBALS['TL_USER_CSS']), 0$arrFiles);
  557.                 }
  558.             }
  559.         }
  560.         // Add a placeholder for dynamic style sheets (see #4203)
  561.         $strStyleSheets .= '[[TL_CSS]]';
  562.         // Always add conditional style sheets at the end
  563.         $strStyleSheets .= $strCcStyleSheets;
  564.         // Add a placeholder for dynamic <head> tags (see #4203)
  565.         $strHeadTags '[[TL_HEAD]]';
  566.         // Add the analytics scripts
  567.         if ($objLayout->analytics)
  568.         {
  569.             $arrAnalytics StringUtil::deserialize($objLayout->analyticstrue);
  570.             foreach ($arrAnalytics as $strTemplate)
  571.             {
  572.                 if ($strTemplate)
  573.                 {
  574.                     $objTemplate = new FrontendTemplate($strTemplate);
  575.                     $strHeadTags .= $objTemplate->parse();
  576.                 }
  577.             }
  578.         }
  579.         // Add the user <head> tags
  580.         if ($strHead trim($objLayout->head ?? ''))
  581.         {
  582.             $strHeadTags .= $strHead "\n";
  583.         }
  584.         $this->Template->stylesheets $strStyleSheets;
  585.         $this->Template->head $strHeadTags;
  586.     }
  587.     /**
  588.      * Create all footer scripts
  589.      *
  590.      * @param LayoutModel $objLayout
  591.      * @param PageModel   $objPage
  592.      *
  593.      * @todo Change the method signature to ($objPage, $objLayout) in Contao 5.0
  594.      */
  595.     protected function createFooterScripts($objLayout$objPage null)
  596.     {
  597.         $strScripts '';
  598.         // jQuery
  599.         if ($objLayout->addJQuery)
  600.         {
  601.             $arrJquery StringUtil::deserialize($objLayout->jquerytrue);
  602.             foreach ($arrJquery as $strTemplate)
  603.             {
  604.                 if ($strTemplate)
  605.                 {
  606.                     $objTemplate = new FrontendTemplate($strTemplate);
  607.                     $strScripts .= $objTemplate->parse();
  608.                 }
  609.             }
  610.             // Add a placeholder for dynamic scripts (see #4203)
  611.             $strScripts .= '[[TL_JQUERY]]';
  612.         }
  613.         // MooTools
  614.         if ($objLayout->addMooTools)
  615.         {
  616.             $arrMootools StringUtil::deserialize($objLayout->mootoolstrue);
  617.             foreach ($arrMootools as $strTemplate)
  618.             {
  619.                 if ($strTemplate)
  620.                 {
  621.                     $objTemplate = new FrontendTemplate($strTemplate);
  622.                     $strScripts .= $objTemplate->parse();
  623.                 }
  624.             }
  625.             // Add a placeholder for dynamic scripts (see #4203)
  626.             $strScripts .= '[[TL_MOOTOOLS]]';
  627.         }
  628.         // Add the framework agnostic JavaScripts
  629.         if ($objLayout->scripts)
  630.         {
  631.             $arrScripts StringUtil::deserialize($objLayout->scriptstrue);
  632.             foreach ($arrScripts as $strTemplate)
  633.             {
  634.                 if ($strTemplate)
  635.                 {
  636.                     $objTemplate = new FrontendTemplate($strTemplate);
  637.                     $strScripts .= $objTemplate->parse();
  638.                 }
  639.             }
  640.         }
  641.         // Add a placeholder for dynamic scripts (see #4203, #5583)
  642.         $strScripts .= '[[TL_BODY]]';
  643.         // Add the external JavaScripts
  644.         $arrExternalJs StringUtil::deserialize($objLayout->externalJs);
  645.         // Consider the sorting order (see #5038)
  646.         if (!empty($arrExternalJs) && \is_array($arrExternalJs) && $objLayout->orderExtJs)
  647.         {
  648.             $tmp StringUtil::deserialize($objLayout->orderExtJs);
  649.             if (!empty($tmp) && \is_array($tmp))
  650.             {
  651.                 // Remove all values
  652.                 $arrOrder array_map(static function () {}, array_flip($tmp));
  653.                 // Move the matching elements to their position in $arrOrder
  654.                 foreach ($arrExternalJs as $k=>$v)
  655.                 {
  656.                     if (\array_key_exists($v$arrOrder))
  657.                     {
  658.                         $arrOrder[$v] = $v;
  659.                         unset($arrExternalJs[$k]);
  660.                     }
  661.                 }
  662.                 // Append the left-over JavaScripts at the end
  663.                 if (!empty($arrExternalJs))
  664.                 {
  665.                     $arrOrder array_merge($arrOrderarray_values($arrExternalJs));
  666.                 }
  667.                 // Remove empty (unreplaced) entries
  668.                 $arrExternalJs array_values(array_filter($arrOrder));
  669.                 unset($arrOrder);
  670.             }
  671.         }
  672.         // Get the file entries from the database
  673.         $objFiles FilesModel::findMultipleByUuids($arrExternalJs);
  674.         $projectDir System::getContainer()->getParameter('kernel.project_dir');
  675.         if ($objFiles !== null)
  676.         {
  677.             while ($objFiles->next())
  678.             {
  679.                 if (file_exists($projectDir '/' $objFiles->path))
  680.                 {
  681.                     $strScripts .= Template::generateScriptTag($objFiles->pathfalsenull);
  682.                 }
  683.             }
  684.         }
  685.         // Add search index meta data
  686.         if ($objPage !== null)
  687.         {
  688.             $noSearch = (bool) $objPage->noSearch;
  689.             // Do not search the page if the query has a key that is in TL_NOINDEX_KEYS
  690.             if (preg_grep('/^(' implode('|'$GLOBALS['TL_NOINDEX_KEYS']) . ')$/'array_keys($_GET)))
  691.             {
  692.                 $noSearch true;
  693.             }
  694.             $meta = array
  695.             (
  696.                 '@context' => array('contao' => 'https://schema.contao.org/'),
  697.                 '@type' => 'contao:Page',
  698.                 'contao:title' => $objPage->pageTitle ?: $objPage->title,
  699.                 'contao:pageId' => (int) $objPage->id,
  700.                 'contao:noSearch' => $noSearch,
  701.                 'contao:protected' => (bool) $objPage->protected,
  702.                 'contao:groups' => array_map('intval'array_filter((array) $objPage->groups)),
  703.                 'contao:fePreview' => System::getContainer()->get('contao.security.token_checker')->isPreviewMode()
  704.             );
  705.             $strScripts .= '<script type="application/ld+json">' json_encode($meta) . '</script>';
  706.         }
  707.         // Add the custom JavaScript
  708.         if ($objLayout->script)
  709.         {
  710.             $strScripts .= "\n" trim($objLayout->script) . "\n";
  711.         }
  712.         $this->Template->mootools $strScripts;
  713.     }
  714. }
  715. class_alias(PageRegular::class, 'PageRegular');