џџџџџџџџџџџџ‡@џџџџџџџџlџџџџџџџџVjџџџџџџџџџџџџџџџџ1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_џџџџЁџџџџџџџџ›‘џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ]џџџџџџџџџџџџy|џџџџџџџџџџџџ,r\џџџџџџџџEFџџџџџџџџџџџџџџџџџџџџ&џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ5џџџџџџџџџџџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџAmџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰џџџџџџџџџџџџ[џџџџezџџџџџџџџџџџџџџџџGџџџџџџџџƒџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџHџџџџџџџџџџџџџџџџ>џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџXџџџџџџџџ%‹}џџџџџџџџџџџџџџџџџџџџџџџџџџџџvџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ„џџџџџџџџџџџџ†џџџџ“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџk џџџџџџџџџџџџџџџџ…Yџџџџџџџџџџџџџџџџџџџџ/8џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ!џџџџџџџџџџџџџџџџ"€џџџџџџџџZџџџџџџџџџџџџ;џџџџџџџџџџџџ”gџџџџžџџџџ џџџџџџџџџџџџџџџџџџџџtwџџџџџџџџbџџџџJџџџџџџџџџџџџTџџџџNџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџOџџџџџџџџ*?џџџџџџџџ—џџџџџџџџџџџџ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ-џџџџџџџџџџџџ=џџџџџџџџџџџџџџџџџџџџLџџџџџџџџџџџџџџџџџџџџџџџџ^џџџџџџџџџџџџuџџџџp)џџџџџџџџ’џџџџŸџџџџџџџџџџџџџџџџџџџџ2џџџџџџџџxџџџџџџџџџџџџџџџџo#џџџџџџџџџџџџЄџџџџiџџџџџџџџЃџџџџџџџџ'џџџџџџџџ6џџџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŽџџџџџџџџЂšџџџџџџџџaџџџџqџџџџџџџџџџџџџџџџџџџџCџџџџџџџџџџџџB–џџџџџџџџџџџџџџџџџџџџџџџџhџџџџџџџџDџџџџSџџџџџџџџџџџџQџџџџ˜Kџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџsџџџџџџџџџџџџ џџџџ0џџџџdџџџџџџџџџџџџџџџџџџџџџџџџœџџџџџџџџџџџџџџџџ•nџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‚џџџџџџџџџџџџџџџџџџџџџџџџџџџџWџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŠU™`џџџџџџџџ8ЂКт: џџџџEfuўч€шFл@0ЃКт: џџџџD“Э0ЩwƒXEл@(ЄКт: џџџџ-Бrўч€Шўн@ ЅКт: џџџџц>јыiваып:ІКт: џџџџW,R‘@cйћ`№п:ЇКт: џџџџл Gwaшчп:ЈКт: џџџџ"єкddЯ‘” ып:ЉКт: џџџџ= Нэќ3ƒЈ в@јЉКт: џџџџхЯ,S€УУ@№ЊКт: лФЮќЇeѓрѕ@˜Dˆп: џџџџyўч€ˆCн@Pгф: џџџџЪ#‘їiИ‘”˜Ѕе@xHˆп: џџџџ‡ŠЩК.-ЇP€п:pIˆп: џџџџпЂ€>bР0ЫС@hJˆп: џџџџКс†Э0Щwƒ …п:`Kˆп: џџџџМЪфЧRВ@‚8ц:XLˆп: џџџџыЯЦ&mЗxп:PMˆп: џџџџА2П“‹хс@С€п:HNˆп: џџџџš$ж‰кцШˆIч@@Oˆп: џџџџ^вЗ€&\oЧPї€п:8Pˆп: џџџџа@2EЪŸшх˜Ш€п:0Qˆп: џџџџ‘Mё Ъ]хн Э€п:(Rˆп: џџџџc_AаžЕŠpј€п: Sˆп: џџџџsKG^0ИˆPњ€п:Tˆп: џџџџG§ kЩ‰М€њ€п:Uˆп: џџџџъKBWarНкћ€п:Vˆп: џџџџ|b—чфK2Њpћ€п:Wˆп: џџџџ.ё;Мd жаћ€п:јWˆп: џџџџS j}џ0ќ€п:№Xˆп: џџџџXwnЩіСћл˜Л…п:шYˆп: џџџџ^n§5 6ЅxМ…п:рZˆп: џџџџрдЈХАт ŸZс:и[ˆп: џџџџŠXc ‹P1€п:а\ˆп: џџџџђъћьдэЅ€1€п:Ш]ˆп: џџџџїšsК§0ЂЋpzъ:Р^ˆп: џџџџvй&ЉЫ:Ю@FЕп:И_ˆп: џџџџgйП3дыiва[в@А`ˆп: џџџџЌі Ї—"ојp“Zс:Јaˆп: џџџџF|y—IЩ)В@2€п: bˆп: џџџџ‚=щdпzЈ€p2€п:˜cˆп: џџџџ :пи—Ћ‰›а2€п:dˆп: џџџџa?ОмŒkcЂРП…п:ˆeˆп: џџџџ5™$Tє….Щ@€п:€fˆп: "Š ец€0МFс:xgˆп: џџџџ9Nxўч€ Г р:phˆп: Gсхlo Ы 4€п:hiˆп: џџџџcL) N€P4€п:`jˆп: џџџџч0N^нЊЪР ѓжр:Xkˆп: џџџџЬ‚Э0Щwƒ№њжр:Plˆп: џџџџ 4 дv’”hћжр:Hmˆп: џџџџmбŒШ‚ш_ А4€п:@nˆп: џџџџЗZ ыpх А6ч@8oˆп: џџџџЬ‡ŒP діKт:0pˆп: џџџџPмЙaпћЯ06€п:Hгф: џџџџ…1Шigк“`6€п:@гф: =Э5фјRйЂA€п:tˆп: џџџџшЎ:/i^”@8€п:uˆп: џџџџAщu-ИЎЁp8€п:vˆп: .Љ{gc @І…п:јvˆп: џџџџџNеѕ9ЙЧ(D€п:№wˆп: џџџџ0дФН?•”@ёKт:шxˆп: џџџџKГfЈјл—<€п:шyˆп: џџџџ”НєNЧ№<€п:рzˆп: џџџџ6‰тШ$‡ѕшE€п:0 $arrMapper['mod'][] = 'comment_form'; // TODO: remove in Contao 5.0 $arrMapper['mod'][] = 'newsletter'; // TODO: remove in Contao 5.0 /** @var TemplateHierarchyInterface $templateHierarchy */ $templateHierarchy = System::getContainer()->get('contao.twig.filesystem_loader'); $identifierPattern = sprintf('/^%s%s/', preg_quote($strPrefix, '/'), substr($strPrefix, -1) !== '_' ? '($|_)' : ''); $prefixedFiles = array_merge( array_filter( array_keys($templateHierarchy->getInheritanceChains()), static fn (string $identifier): bool => 1 === preg_match($identifierPattern, $identifier), ), // Merge with the templates from the TemplateLoader for backwards // compatibility in case someone has added templates manually TemplateLoader::getPrefixedFiles($strPrefix), ); foreach ($prefixedFiles as $strTemplate) { if ($strTemplate != $strPrefix) { list($k, $strKey) = explode('_', $strTemplate, 2); if (isset($arrMapper[$k]) && \in_array($strKey, $arrMapper[$k])) { $arrBundleTemplates[] = $strTemplate; continue; } } $arrTemplates[$strTemplate][] = 'root'; } $strGlobPrefix = $strPrefix; // Backwards compatibility (see #725) if (substr($strGlobPrefix, -1) == '_') { $strGlobPrefix = substr($strGlobPrefix, 0, -1) . '[_-]'; } $projectDir = System::getContainer()->getParameter('kernel.project_dir'); $arrCustomized = self::braceGlob($projectDir . '/templates/' . $strGlobPrefix . '*.html5'); // Add the customized templates if (!empty($arrCustomized) && \is_array($arrCustomized)) { $blnIsGroupPrefix = preg_match('/^[a-z]+_$/', $strPrefix); foreach ($arrCustomized as $strFile) { $strTemplate = basename($strFile, strrchr($strFile, '.')); if (strpos($strTemplate, '-') !== false) { trigger_deprecation('contao/core-bundle', '4.9', 'Using hyphens in the template name "' . $strTemplate . '.html5" has been deprecated and will no longer work in Contao 5.0. Use snake_case instead.'); } // Ignore bundle templates, e.g. mod_article and mod_article_list if (\in_array($strTemplate, $arrBundleTemplates)) { continue; } // Also ignore custom templates belonging to a different bundle template, // e.g. mod_article and mod_article_list_custom if (!$blnIsGroupPrefix) { foreach ($arrBundleTemplates as $strKey) { if (strpos($strTemplate, $strKey . '_') === 0) { continue 2; } } } $arrTemplates[$strTemplate][] = $GLOBALS['TL_LANG']['MSC']['global'] ?? 'global'; } } $arrDefaultPlaces = array(); if ($strDefaultTemplate) { $arrDefaultPlaces[] = $GLOBALS['TL_LANG']['MSC']['default']; if (file_exists($projectDir . '/templates/' . $strDefaultTemplate . '.html5')) { $arrDefaultPlaces[] = $GLOBALS['TL_LANG']['MSC']['global']; } } // Do not look for back end templates in theme folders (see #5379) if ($strPrefix != 'be_' && $strPrefix != 'mail_') { // Try to select the themes (see #5210) try { $objTheme = ThemeModel::findAll(array('order'=>'name')); } catch (\Throwable $e) { $objTheme = null; } // Add the theme templates if ($objTheme !== null) { while ($objTheme->next()) { if (!$objTheme->templates) { continue; } if ($strDefaultTemplate && file_exists($projectDir . '/' . $objTheme->templates . '/' . $strDefaultTemplate . '.html5')) { $arrDefaultPlaces[] = $objTheme->name; } $arrThemeTemplates = self::braceGlob($projectDir . '/' . $objTheme->templates . '/' . $strGlobPrefix . '*.html5'); if (!empty($arrThemeTemplates) && \is_array($arrThemeTemplates)) { foreach ($arrThemeTemplates as $strFile) { $strTemplate = basename($strFile, strrchr($strFile, '.')); $arrTemplates[$strTemplate][] = $objTheme->name; } } } } } // Show the template sources (see #6875) foreach ($arrTemplates as $k=>$v) { $v = array_filter($v, static function ($a) { return $a != 'root'; }); if (empty($v)) { $arrTemplates[$k] = $k; } else { $arrTemplates[$k] = $k . ' (' . implode(', ', $v) . ')'; } } // Sort the template names ksort($arrTemplates); if ($strDefaultTemplate) { if (!empty($arrDefaultPlaces)) { $strDefaultTemplate .= ' (' . implode(', ', $arrDefaultPlaces) . ')'; } $arrTemplates = array('' => $strDefaultTemplate) + $arrTemplates; } return $arrTemplates; } /** * Generate a front end module and return it as string * * @param mixed $intId A module ID or a Model object * @param string $strColumn The name of the column * * @return string The module HTML markup */ public static function getFrontendModule($intId, $strColumn='main') { if (!\is_object($intId) && !\strlen($intId)) { return ''; } /** @var PageModel $objPage */ global $objPage; // Articles if (!\is_object($intId) && $intId == 0) { // Show a particular article only if ($objPage->type == 'regular' && Input::get('articles')) { list($strSection, $strArticle) = explode(':', Input::get('articles')) + array(null, null); if ($strArticle === null) { $strArticle = $strSection; $strSection = 'main'; } if ($strSection == $strColumn) { $objArticle = ArticleModel::findPublishedByIdOrAliasAndPid($strArticle, $objPage->id); // Send a 404 header if there is no published article if (null === $objArticle) { throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); } // Send a 403 header if the article cannot be accessed if (!static::isVisibleElement($objArticle)) { throw new AccessDeniedException('Access denied: ' . Environment::get('uri')); } return static::getArticle($objArticle); } } // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getArticles']) && \is_array($GLOBALS['TL_HOOKS']['getArticles'])) { foreach ($GLOBALS['TL_HOOKS']['getArticles'] as $callback) { $return = static::importStatic($callback[0])->{$callback[1]}($objPage->id, $strColumn); if (\is_string($return)) { return $return; } } } // Show all articles (no else block here, see #4740) $objArticles = ArticleModel::findPublishedByPidAndColumn($objPage->id, $strColumn); if ($objArticles === null) { return ''; } $return = ''; $blnMultiMode = ($objArticles->count() > 1); while ($objArticles->next()) { $return .= static::getArticle($objArticles->current(), $blnMultiMode, false, $strColumn); } return $return; } // Other modules if (\is_object($intId)) { $objRow = $intId; } else { $objRow = ModuleModel::findByPk($intId); if ($objRow === null) { return ''; } } // Check the visibility (see #6311) if (!static::isVisibleElement($objRow)) { return ''; } $strClass = Module::findClass($objRow->type); // Return if the class does not exist if (!class_exists($strClass)) { System::getContainer()->get('monolog.logger.contao.error')->error('Module class "' . $strClass . '" (module "' . $objRow->type . '") does not exist'); return ''; } $strStopWatchId = 'contao.frontend_module.' . $objRow->type . ' (ID ' . $objRow->id . ')'; if (System::getContainer()->getParameter('kernel.debug') && System::getContainer()->has('debug.stopwatch')) { $objStopwatch = System::getContainer()->get('debug.stopwatch'); $objStopwatch->start($strStopWatchId, 'contao.layout'); } $objRow->typePrefix = 'mod_'; /** @var Module $objModule */ $objModule = new $strClass($objRow, $strColumn); $strBuffer = $objModule->generate(); // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getFrontendModule']) && \is_array($GLOBALS['TL_HOOKS']['getFrontendModule'])) { foreach ($GLOBALS['TL_HOOKS']['getFrontendModule'] as $callback) { $strBuffer = static::importStatic($callback[0])->{$callback[1]}($objRow, $strBuffer, $objModule); } } // Disable indexing if protected if ($objModule->protected && !preg_match('/^\s*" . $strBuffer . "\n"; } if (isset($objStopwatch) && $objStopwatch->isStarted($strStopWatchId)) { $objStopwatch->stop($strStopWatchId); } return $strBuffer; } /** * Generate an article and return it as string * * @param mixed $varId The article ID or a Model object * @param boolean $blnMultiMode If true, only teasers will be shown * @param boolean $blnIsInsertTag If true, there will be no page relation * @param string $strColumn The name of the column * * @return string|boolean The article HTML markup or false */ public static function getArticle($varId, $blnMultiMode=false, $blnIsInsertTag=false, $strColumn='main') { /** @var PageModel $objPage */ global $objPage; if (\is_object($varId)) { $objRow = $varId; } else { if (!$varId) { return ''; } $objRow = ArticleModel::findByIdOrAliasAndPid($varId, (!$blnIsInsertTag ? $objPage->id : null)); if ($objRow === null) { return false; } } // Check the visibility (see #6311) if (!static::isVisibleElement($objRow)) { return ''; } // Print the article as PDF if (isset($_GET['pdf']) && Input::get('pdf') == $objRow->id) { // Deprecated since Contao 4.0, to be removed in Contao 5.0 if ($objRow->printable == 1) { trigger_deprecation('contao/core-bundle', '4.0', 'Setting tl_article.printable to "1" has been deprecated and will no longer work in Contao 5.0.'); $objArticle = new ModuleArticle($objRow); $objArticle->generatePdf(); } elseif ($objRow->printable) { $options = StringUtil::deserialize($objRow->printable); if (\is_array($options) && \in_array('pdf', $options)) { $objArticle = new ModuleArticle($objRow); $objArticle->generatePdf(); } } } $objRow->headline = $objRow->title; $objRow->multiMode = $blnMultiMode; // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getArticle']) && \is_array($GLOBALS['TL_HOOKS']['getArticle'])) { foreach ($GLOBALS['TL_HOOKS']['getArticle'] as $callback) { static::importStatic($callback[0])->{$callback[1]}($objRow); } } $strStopWatchId = 'contao.article (ID ' . $objRow->id . ')'; if (System::getContainer()->getParameter('kernel.debug') && System::getContainer()->has('debug.stopwatch')) { $objStopwatch = System::getContainer()->get('debug.stopwatch'); $objStopwatch->start($strStopWatchId, 'contao.layout'); } $objArticle = new ModuleArticle($objRow, $strColumn); $strBuffer = $objArticle->generate($blnIsInsertTag); // Disable indexing if protected if ($objArticle->protected && !preg_match('/^\s*" . $strBuffer . "\n"; } if (isset($objStopwatch) && $objStopwatch->isStarted($strStopWatchId)) { $objStopwatch->stop($strStopWatchId); } return $strBuffer; } /** * Generate a content element and return it as string * * @param mixed $intId A content element ID or a Model object * @param string $strColumn The column the element is in * * @return string The content element HTML markup */ public static function getContentElement($intId, $strColumn='main') { if (\is_object($intId)) { $objRow = $intId; } else { if ($intId < 1 || !\strlen($intId)) { return ''; } $objRow = ContentModel::findByPk($intId); if ($objRow === null) { return ''; } } // Check the visibility (see #6311) if (!static::isVisibleElement($objRow)) { return ''; } $strClass = ContentElement::findClass($objRow->type); // Return if the class does not exist if (!class_exists($strClass)) { System::getContainer()->get('monolog.logger.contao.error')->error('Content element class "' . $strClass . '" (content element "' . $objRow->type . '") does not exist'); return ''; } $objRow->typePrefix = 'ce_'; $strStopWatchId = 'contao.content_element.' . $objRow->type . ' (ID ' . $objRow->id . ')'; if ($objRow->type != 'module' && System::getContainer()->getParameter('kernel.debug') && System::getContainer()->has('debug.stopwatch')) { $objStopwatch = System::getContainer()->get('debug.stopwatch'); $objStopwatch->start($strStopWatchId, 'contao.layout'); } /** @var ContentElement $objElement */ $objElement = new $strClass($objRow, $strColumn); $strBuffer = $objElement->generate(); // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getContentElement']) && \is_array($GLOBALS['TL_HOOKS']['getContentElement'])) { foreach ($GLOBALS['TL_HOOKS']['getContentElement'] as $callback) { $strBuffer = static::importStatic($callback[0])->{$callback[1]}($objRow, $strBuffer, $objElement); } } // Disable indexing if protected if ($objElement->protected && !preg_match('/^\s*" . $strBuffer . "\n"; } if (isset($objStopwatch) && $objStopwatch->isStarted($strStopWatchId)) { $objStopwatch->stop($strStopWatchId); } return $strBuffer; } /** * Generate a form and return it as string * * @param mixed $varId A form ID or a Model object * @param string $strColumn The column the form is in * @param boolean $blnModule Render the form as module * * @return string The form HTML markup */ public static function getForm($varId, $strColumn='main', $blnModule=false) { if (\is_object($varId)) { $objRow = $varId; } else { if (!$varId) { return ''; } $objRow = FormModel::findByIdOrAlias($varId); if ($objRow === null) { return ''; } } $strClass = $blnModule ? Module::findClass('form') : ContentElement::findClass('form'); if (!class_exists($strClass)) { System::getContainer()->get('monolog.logger.contao.error')->error('Form class "' . $strClass . '" does not exist'); return ''; } $objRow->typePrefix = $blnModule ? 'mod_' : 'ce_'; $objRow->form = $objRow->id; /** @var Form $objElement */ $objElement = new $strClass($objRow, $strColumn); $strBuffer = $objElement->generate(); // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getForm']) && \is_array($GLOBALS['TL_HOOKS']['getForm'])) { foreach ($GLOBALS['TL_HOOKS']['getForm'] as $callback) { $strBuffer = static::importStatic($callback[0])->{$callback[1]}($objRow, $strBuffer, $objElement); } } return $strBuffer; } /** * Return the languages for the TinyMCE spellchecker * * @return string The TinyMCE spellchecker language string * * @deprecated Deprecated since Contao 4.13, to be removed in Contao 5.0. */ protected function getSpellcheckerString() { trigger_deprecation('contao/core-bundle', '4.13', 'Using "%s()" has been deprecated and will no longer work in Contao 5.0.', __METHOD__); System::loadLanguageFile('languages'); $return = array(); $langs = Folder::scan(__DIR__ . '/../../languages'); array_unshift($langs, $GLOBALS['TL_LANGUAGE']); foreach ($langs as $lang) { $lang = substr($lang, 0, 2); if (isset($GLOBALS['TL_LANG']['LNG'][$lang])) { $return[$lang] = $GLOBALS['TL_LANG']['LNG'][$lang] . '=' . $lang; } } return '+' . implode(',', array_unique($return)); } /** * Calculate the page status icon name based on the page parameters * * @param PageModel|Result|\stdClass $objPage The page object * * @return string The status icon name */ public static function getPageStatusIcon($objPage) { $sub = 0; $type = \in_array($objPage->type, array('regular', 'root', 'forward', 'redirect', 'error_401', 'error_403', 'error_404', 'error_503'), true) ? $objPage->type : 'regular'; $image = $type . '.svg'; // Page not published or not active if (!$objPage->published || ($objPage->start && $objPage->start > time()) || ($objPage->stop && $objPage->stop <= time())) { ++$sub; } // Page hidden from menu if ($objPage->hide && !\in_array($type, array('root', 'error_401', 'error_403', 'error_404', 'error_503'))) { $sub += 2; } // Page protected if ($objPage->protected && !\in_array($type, array('root', 'error_401', 'error_403', 'error_404', 'error_503'))) { $sub += 4; } // Change icon if root page is published and in maintenance mode if ($sub == 0 && $objPage->type == 'root' && $objPage->maintenanceMode) { $sub = 2; } // Get the image name if ($sub > 0) { $image = $type . '_' . $sub . '.svg'; } // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['getPageStatusIcon']) && \is_array($GLOBALS['TL_HOOKS']['getPageStatusIcon'])) { foreach ($GLOBALS['TL_HOOKS']['getPageStatusIcon'] as $callback) { $image = static::importStatic($callback[0])->{$callback[1]}($objPage, $image); } } return $image; } /** * Check whether an element is visible in the front end * * @param Model|ContentModel|ModuleModel $objElement The element model * * @return boolean True if the element is visible */ public static function isVisibleElement(Model $objElement) { $blnReturn = true; // Only apply the restrictions in the front end if (TL_MODE == 'FE') { $security = System::getContainer()->get('security.helper'); if ($objElement->protected) { $groups = StringUtil::deserialize($objElement->groups, true); $blnReturn = $security->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPS, $groups); } elseif ($objElement->guests) { trigger_deprecation('contao/core-bundle', '4.12', 'Using the "show to guests only" feature has been deprecated an will no longer work in Contao 5.0. Use the "protect page" function instead.'); $blnReturn = !$security->isGranted('ROLE_MEMBER'); // backwards compatibility } } // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['isVisibleElement']) && \is_array($GLOBALS['TL_HOOKS']['isVisibleElement'])) { foreach ($GLOBALS['TL_HOOKS']['isVisibleElement'] as $callback) { $blnReturn = static::importStatic($callback[0])->{$callback[1]}($objElement, $blnReturn); } } return $blnReturn; } /** * Replace insert tags with their values * * @param string $strBuffer The text with the tags to be replaced * @param boolean $blnCache If false, non-cacheable tags will be replaced * * @return string The text with the replaced tags * * @deprecated Deprecated since Contao 4.13, to be removed in Contao 5.0. * Use the InsertTagParser service instead. */ public static function replaceInsertTags($strBuffer, $blnCache=true) { trigger_deprecation('contao/core-bundle', '4.13', 'Using "%s()" has been deprecated and will no longer work in Contao 5.0. Use the InsertTagParser service instead.', __METHOD__); $parser = System::getContainer()->get('contao.insert_tag.parser'); if ($blnCache) { return $parser->replace((string) $strBuffer); } return $parser->replaceInline((string) $strBuffer); } /** * Replace the dynamic script tags (see #4203) * * @param string $strBuffer The string with the tags to be replaced * * @return string The string with the replaced tags */ public static function replaceDynamicScriptTags($strBuffer) { // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['replaceDynamicScriptTags']) && \is_array($GLOBALS['TL_HOOKS']['replaceDynamicScriptTags'])) { foreach ($GLOBALS['TL_HOOKS']['replaceDynamicScriptTags'] as $callback) { $strBuffer = static::importStatic($callback[0])->{$callback[1]}($strBuffer); } } $arrReplace = array(); $strScripts = ''; // Add the internal jQuery scripts if (!empty($GLOBALS['TL_JQUERY']) && \is_array($GLOBALS['TL_JQUERY'])) { $strScripts .= implode('', array_unique($GLOBALS['TL_JQUERY'])); } $nonce = ContaoFramework::getNonce(); $arrReplace["[[TL_JQUERY_$nonce]]"] = $strScripts; $strScripts = ''; // Add the internal MooTools scripts if (!empty($GLOBALS['TL_MOOTOOLS']) && \is_array($GLOBALS['TL_MOOTOOLS'])) { $strScripts .= implode('', array_unique($GLOBALS['TL_MOOTOOLS'])); } $arrReplace["[[TL_MOOTOOLS_$nonce]]"] = $strScripts; $strScripts = ''; // Add the internal tags if (!empty($GLOBALS['TL_BODY']) && \is_array($GLOBALS['TL_BODY'])) { $strScripts .= implode('', array_unique($GLOBALS['TL_BODY'])); } /** @var PageModel|null $objPage */ global $objPage; $objLayout = ($objPage !== null) ? LayoutModel::findByPk($objPage->layoutId) : null; $blnCombineScripts = $objLayout !== null && $objLayout->combineScripts; $arrReplace["[[TL_BODY_$nonce]]"] = $strScripts; $strScripts = ''; $objCombiner = new Combiner(); // Add the CSS framework style sheets if (!empty($GLOBALS['TL_FRAMEWORK_CSS']) && \is_array($GLOBALS['TL_FRAMEWORK_CSS'])) { foreach (array_unique($GLOBALS['TL_FRAMEWORK_CSS']) as $stylesheet) { $objCombiner->add($stylesheet); } } // Add the internal style sheets if (!empty($GLOBALS['TL_CSS']) && \is_array($GLOBALS['TL_CSS'])) { foreach (array_unique($GLOBALS['TL_CSS']) as $stylesheet) { $options = StringUtil::resolveFlaggedUrl($stylesheet); if ($options->static) { $objCombiner->add($stylesheet, $options->mtime, $options->media); } else { $strScripts .= Template::generateStyleTag(static::addAssetsUrlTo($stylesheet), $options->media, $options->mtime); } } } // Add the user style sheets if (!empty($GLOBALS['TL_USER_CSS']) && \is_array($GLOBALS['TL_USER_CSS'])) { foreach (array_unique($GLOBALS['TL_USER_CSS']) as $stylesheet) { $options = StringUtil::resolveFlaggedUrl($stylesheet); if ($options->static) { $objCombiner->add($stylesheet, $options->mtime, $options->media); } else { $strScripts .= Template::generateStyleTag(static::addAssetsUrlTo($stylesheet), $options->media, $options->mtime); } } } // Create the aggregated style sheet if ($objCombiner->hasEntries()) { if ($blnCombineScripts) { $strScripts .= Template::generateStyleTag($objCombiner->getCombinedFile(), 'all'); } else { foreach ($objCombiner->getFileUrls() as $strUrl) { $options = StringUtil::resolveFlaggedUrl($strUrl); $strScripts .= Template::generateStyleTag($strUrl, $options->media, $options->mtime); } } } $arrReplace["[[TL_CSS_$nonce]]"] = $strScripts; $strScripts = ''; // Add the internal scripts if (!empty($GLOBALS['TL_JAVASCRIPT']) && \is_array($GLOBALS['TL_JAVASCRIPT'])) { $objCombiner = new Combiner(); $objCombinerAsync = new Combiner(); foreach (array_unique($GLOBALS['TL_JAVASCRIPT']) as $javascript) { $options = StringUtil::resolveFlaggedUrl($javascript); if ($options->static) { $options->async ? $objCombinerAsync->add($javascript, $options->mtime) : $objCombiner->add($javascript, $options->mtime); } else { $strScripts .= Template::generateScriptTag(static::addAssetsUrlTo($javascript), $options->async, $options->mtime); } } // Create the aggregated script and add it before the non-static scripts (see #4890) if ($objCombiner->hasEntries()) { if ($blnCombineScripts) { $strScripts = Template::generateScriptTag($objCombiner->getCombinedFile()) . $strScripts; } else { $arrReversed = array_reverse($objCombiner->getFileUrls()); foreach ($arrReversed as $strUrl) { $options = StringUtil::resolveFlaggedUrl($strUrl); $strScripts = Template::generateScriptTag($strUrl, false, $options->mtime) . $strScripts; } } } if ($objCombinerAsync->hasEntries()) { if ($blnCombineScripts) { $strScripts = Template::generateScriptTag($objCombinerAsync->getCombinedFile(), true) . $strScripts; } else { $arrReversed = array_reverse($objCombinerAsync->getFileUrls()); foreach ($arrReversed as $strUrl) { $options = StringUtil::resolveFlaggedUrl($strUrl); $strScripts = Template::generateScriptTag($strUrl, true, $options->mtime) . $strScripts; } } } } // Add the internal tags if (!empty($GLOBALS['TL_HEAD']) && \is_array($GLOBALS['TL_HEAD'])) { foreach (array_unique($GLOBALS['TL_HEAD']) as $head) { $strScripts .= $head; } } $arrReplace["[[TL_HEAD_$nonce]]"] = $strScripts; return str_replace(array_keys($arrReplace), $arrReplace, $strBuffer); } /** * Compile the margin format definition based on an array of values * * @param array $arrValues An array of four values and a unit * @param string $strType Either "margin" or "padding" * * @return string The CSS markup * * @deprecated Deprecated since Contao 4.13, to be removed in Contao 5.0. */ public static function generateMargin($arrValues, $strType='margin') { trigger_deprecation('contao/core-bundle', '4.13', 'Using Contao\Controller::generateMargin is deprecated since Contao 4.13 and will be removed in Contao 5.'); // Initialize an empty array (see #5217) if (!\is_array($arrValues)) { $arrValues = array('top'=>'', 'right'=>'', 'bottom'=>'', 'left'=>'', 'unit'=>''); } $top = $arrValues['top']; $right = $arrValues['right']; $bottom = $arrValues['bottom']; $left = $arrValues['left']; // Try to shorten the definition if ($top && $right && $bottom && $left) { if ($top == $right && $top == $bottom && $top == $left) { return $strType . ':' . $top . $arrValues['unit'] . ';'; } if ($top == $bottom && $right == $left) { return $strType . ':' . $top . $arrValues['unit'] . ' ' . $left . $arrValues['unit'] . ';'; } if ($top != $bottom && $right == $left) { return $strType . ':' . $top . $arrValues['unit'] . ' ' . $right . $arrValues['unit'] . ' ' . $bottom . $arrValues['unit'] . ';'; } return $strType . ':' . $top . $arrValues['unit'] . ' ' . $right . $arrValues['unit'] . ' ' . $bottom . $arrValues['unit'] . ' ' . $left . $arrValues['unit'] . ';'; } $return = array(); $arrDir = compact('top', 'right', 'bottom', 'left'); foreach ($arrDir as $k=>$v) { if ($v) { $return[] = $strType . '-' . $k . ':' . $v . $arrValues['unit'] . ';'; } } return implode('', $return); } /** * Add a request string to the current URL * * @param string $strRequest The string to be added * @param boolean $blnAddRef Add the referer ID * @param array $arrUnset An optional array of keys to unset * * @return string The new URL */ public static function addToUrl($strRequest, $blnAddRef=true, $arrUnset=array()) { $pairs = array(); $request = System::getContainer()->get('request_stack')->getCurrentRequest(); if ($request->server->has('QUERY_STRING')) { $cacheKey = md5($request->server->get('QUERY_STRING')); if (!isset(static::$arrQueryCache[$cacheKey])) { parse_str($request->server->get('QUERY_STRING'), $pairs); ksort($pairs); static::$arrQueryCache[$cacheKey] = $pairs; } $pairs = static::$arrQueryCache[$cacheKey]; } // Remove the request token and referer ID unset($pairs['rt'], $pairs['ref'], $pairs['revise']); foreach ($arrUnset as $key) { unset($pairs[$key]); } // Merge the request string to be added if ($strRequest) { parse_str(str_replace('&', '&', $strRequest), $newPairs); $pairs = array_merge($pairs, $newPairs); } // Add the referer ID if ($request->query->has('ref') || ($strRequest && $blnAddRef)) { $pairs['ref'] = $request->attributes->get('_contao_referer_id'); } $uri = ''; if (!empty($pairs)) { $uri = '?' . http_build_query($pairs, '', '&', PHP_QUERY_RFC3986); } return TL_SCRIPT . $uri; } /** * Reload the current page */ public static function reload() { static::redirect(Environment::get('uri')); } /** * Redirect to another page * * @param string $strLocation The target URL * @param integer $intStatus The HTTP status code (defaults to 303) */ public static function redirect($strLocation, $intStatus=303) { $strLocation = str_replace('&', '&', $strLocation); $strLocation = static::replaceOldBePaths($strLocation); // Make the location an absolute URL if (!preg_match('@^https?://@i', $strLocation)) { $strLocation = Environment::get('base') . ltrim($strLocation, '/'); } // Ajax request if (Environment::get('isAjaxRequest')) { throw new AjaxRedirectResponseException($strLocation); } throw new RedirectResponseException($strLocation, $intStatus); } /** * Replace the old back end paths * * @param string $strContext The context * * @return string The modified context */ protected static function replaceOldBePaths($strContext) { $arrCache = &self::$arrOldBePathCache; $arrMapper = array ( 'contao/confirm.php' => 'contao_backend_confirm', 'contao/file.php' => 'contao_backend_file', 'contao/help.php' => 'contao_backend_help', 'contao/index.php' => 'contao_backend_login', 'contao/main.php' => 'contao_backend', 'contao/page.php' => 'contao_backend_page', 'contao/password.php' => 'contao_backend_password', 'contao/popup.php' => 'contao_backend_popup', 'contao/preview.php' => 'contao_backend_preview', ); $replace = static function ($matches) use ($arrMapper, &$arrCache) { $key = $matches[0]; if (!isset($arrCache[$key])) { trigger_deprecation('contao/core-bundle', '4.0', 'Using old backend paths has been deprecated in Contao 4.0 and will be removed in Contao 5. Use the backend routes instead.'); $router = System::getContainer()->get('router'); $arrCache[$key] = substr($router->generate($arrMapper[$key]), \strlen(Environment::get('path')) + 1); } return $arrCache[$key]; }; $regex = '(' . implode('|', array_map('preg_quote', array_keys($arrMapper))) . ')'; return preg_replace_callback($regex, $replace, $strContext); } /** * Generate a front end URL * * @param array $arrRow An array of page parameters * @param string $strParams An optional string of URL parameters * @param string $strForceLang Force a certain language * @param boolean $blnFixDomain Check the domain of the target page and append it if necessary * * @return string A URL that can be used in the front end * * @deprecated Deprecated since Contao 4.2, to be removed in Contao 5.0. * Use PageModel::getFrontendUrl() instead. */ public static function generateFrontendUrl(array $arrRow, $strParams=null, $strForceLang=null, $blnFixDomain=false) { trigger_deprecation('contao/core-bundle', '4.2', 'Using "Contao\Controller::generateFrontendUrl()" has been deprecated and will no longer work in Contao 5.0. Use PageModel::getFrontendUrl() instead.'); $page = new PageModel(); $page->preventSaving(false); $page->setRow($arrRow); if (!isset($arrRow['rootId'])) { $page->loadDetails(); foreach (array('domain', 'rootLanguage', 'rootUseSSL') as $key) { if (isset($arrRow[$key])) { $page->$key = $arrRow[$key]; } else { $arrRow[$key] = $page->$key; } } } // Set the language if ($strForceLang !== null) { $strForceLang = LocaleUtil::formatAsLocale($strForceLang); $page->language = $strForceLang; $page->rootLanguage = $strForceLang; if (System::getContainer()->getParameter('contao.legacy_routing')) { $page->urlPrefix = System::getContainer()->getParameter('contao.prepend_locale') ? $strForceLang : ''; } } // Add the domain if it differs from the current one (see #3765 and #6927) if ($blnFixDomain) { $page->domain = $arrRow['domain']; $page->rootUseSSL = (bool) $arrRow['rootUseSSL']; } $objRouter = System::getContainer()->get('router'); $strUrl = $objRouter->generate(PageRoute::PAGE_BASED_ROUTE_NAME, array(RouteObjectInterface::CONTENT_OBJECT => $page, 'parameters' => $strParams)); // Remove path from absolute URLs if (0 === strncmp($strUrl, '/', 1) && 0 !== strncmp($strUrl, '//', 2)) { $strUrl = substr($strUrl, \strlen(Environment::get('path')) + 1); } // Decode sprintf placeholders if (strpos($strParams, '%') !== false) { $arrMatches = array(); preg_match_all('/%([sducoxXbgGeEfF])/', $strParams, $arrMatches); foreach (array_unique($arrMatches[1]) as $v) { $strUrl = str_replace('%25' . $v, '%' . $v, $strUrl); } } // HOOK: add custom logic if (isset($GLOBALS['TL_HOOKS']['generateFrontendUrl']) && \is_array($GLOBALS['TL_HOOKS']['generateFrontendUrl'])) { foreach ($GLOBALS['TL_HOOKS']['generateFrontendUrl'] as $callback) { $strUrl = static::importStatic($callback[0])->{$callback[1]}($arrRow, $strParams, $strUrl); } } return $strUrl; } /** * Convert relative URLs in href and src attributes to absolute URLs * * @param string $strContent The text with the URLs to be converted * @param string $strBase An optional base URL * @param boolean $blnHrefOnly If true, only href attributes will be converted * * @return string The text with the replaced URLs */ public static function convertRelativeUrls($strContent, $strBase='', $blnHrefOnly=false) { if (!$strBase) { $strBase = Environment::get('base'); } $search = $blnHrefOnly ? 'href' : 'href|src'; $arrUrls = preg_split('/((' . $search . ')="([^"]+)")/i', $strContent, -1, PREG_SPLIT_DELIM_CAPTURE); $strContent = ''; for ($i=0, $c=\count($arrUrls); $i<$c; $i+=4) { $strContent .= $arrUrls[$i]; if (!isset($arrUrls[$i+2])) { continue; } $strAttribute = $arrUrls[$i+2]; $strUrl = $arrUrls[$i+3]; if (!preg_match('@^(?:[a-z0-9]+:|#)@i', $strUrl)) { $strUrl = $strBase . (($strUrl != '/') ? $strUrl : ''); } $strContent .= $strAttribute . '="' . $strUrl . '"'; } return $strContent; } /** * Send a file to the browser so the "save as т€І" dialogue opens * * @param string $strFile The file path * @param boolean $inline Show the file in the browser instead of opening the download dialog * * @throws AccessDeniedException */ public static function sendFileToBrowser($strFile, $inline=false) { // Make sure there are no attempts to hack the file system if (preg_match('@^\.+@', $strFile) || preg_match('@\.+/@', $strFile) || preg_match('@(://)+@', $strFile)) { throw new PageNotFoundException('Invalid file name'); } // Limit downloads to the files directory if (!preg_match('@^' . preg_quote(System::getContainer()->getParameter('contao.upload_path'), '@') . '@i', $strFile)) { throw new PageNotFoundException('Invalid path'); } $projectDir = System::getContainer()->getParameter('kernel.project_dir'); // Check whether the file exists if (!file_exists($projectDir . '/' . $strFile)) { throw new PageNotFoundException('File not found'); } $objFile = new File($strFile); $arrAllowedTypes = StringUtil::trimsplit(',', strtolower(Config::get('allowedDownload'))); // Check whether the file type is allowed to be downloaded if (!\in_array($objFile->extension, $arrAllowedTypes)) { throw new AccessDeniedException(sprintf('File type "%s" is not allowed', $objFile->extension)); } // HOOK: post download callback if (isset($GLOBALS['TL_HOOKS']['postDownload']) && \is_array($GLOBALS['TL_HOOKS']['postDownload'])) { foreach ($GLOBALS['TL_HOOKS']['postDownload'] as $callback) { static::importStatic($callback[0])->{$callback[1]}($strFile); } } // Send the file (will stop the script execution) $objFile->sendToBrowser('', $inline); } /** * Load a set of DCA files * * @param string $strTable The table name * @param boolean $blnNoCache If true, the cache will be bypassed */ public static function loadDataContainer($strTable, $blnNoCache=false) { if (\func_num_args() > 1) { trigger_deprecation('contao/core-bundle', '4.13', 'Calling "%s" with the $blnNoCache parameter has been deprecated and will no longer work in Contao 5.0.', __METHOD__); } $loader = new DcaLoader($strTable); $loader->load(...($blnNoCache ? array(true) : array())); } /** * Do not name this "reset" because it might result in conflicts with child classes * @see https://github.com/contao/contao/issues/4257 * * @internal */ public static function resetControllerCache() { self::$arrQueryCache = array(); self::$arrOldBePathCache = array(); } /** * Redirect to a front end page * * @param integer $intPage The page ID * @param string $strArticle An optional article alias * @param boolean $blnReturn If true, return the URL and don't redirect * * @return string The URL of the target page */ protected function redirectToFrontendPage($intPage, $strArticle=null, $blnReturn=false) { if (($intPage = (int) $intPage) <= 0) { return ''; } $objPage = PageModel::findWithDetails($intPage); if ($objPage === null) { return ''; } $strParams = null; // Add the /article/ fragment (see #673) if ($strArticle !== null && ($objArticle = ArticleModel::findByAlias($strArticle)) !== null) { $strParams = '/articles/' . (($objArticle->inColumn != 'main') ? $objArticle->inColumn . ':' : '') . $strArticle; } $strUrl = $objPage->getPreviewUrl($strParams); if (!$blnReturn) { $this->redirect($strUrl); } return $strUrl; } /** * Get the parent records of an entry and return them as string which can * be used in a log message * * @param string $strTable The table name * @param integer $intId The record ID * * @return string A string that can be used in a log message */ protected function getParentEntries($strTable, $intId) { // No parent table if (empty($GLOBALS['TL_DCA'][$strTable]['config']['ptable'])) { return ''; } $arrParent = array(); do { // Get the pid $objParent = $this->Database->prepare("SELECT pid FROM " . $strTable . " WHERE id=?") ->limit(1) ->execute($intId); if ($objParent->numRows < 1) { break; } // Store the parent table information $strTable = $GLOBALS['TL_DCA'][$strTable]['config']['ptable']; $intId = $objParent->pid; // Add the log entry $arrParent[] = $strTable . '.id=' . $intId; // Load the data container of the parent table $this->loadDataContainer($strTable); } while ($intId && !empty($GLOBALS['TL_DCA'][$strTable]['config']['ptable'])); if (empty($arrParent)) { return ''; } return ' (parent records: ' . implode(', ', $arrParent) . ')'; } /** * Take an array of file paths and eliminate the nested ones * * @param array $arrPaths The array of file paths * * @return array The file paths array without the nested paths */ protected function eliminateNestedPaths($arrPaths) { $arrPaths = array_filter($arrPaths); if (empty($arrPaths) || !\is_array($arrPaths)) { return array(); } $nested = array(); foreach ($arrPaths as $path) { $nested[] = preg_grep('/^' . preg_quote($path, '/') . '\/.+/', $arrPaths); } if (!empty($nested)) { $nested = array_merge(...$nested); } return array_values(array_diff($arrPaths, $nested)); } /** * Take an array of pages and eliminate the nested ones * * @param array $arrPages The array of page IDs * @param string $strTable The table name * @param boolean $blnSorting True if the table has a sorting field * * @return array The page IDs array without the nested IDs */ protected function eliminateNestedPages($arrPages, $strTable=null, $blnSorting=false) { if (empty($arrPages) || !\is_array($arrPages)) { return array(); } if (!$strTable) { $strTable = 'tl_page'; } // Thanks to Andreas Schempp (see #2475 and #3423) $arrPages = array_filter(array_map('intval', $arrPages)); $arrPages = array_values(array_diff($arrPages, $this->Database->getChildRecords($arrPages, $strTable, $blnSorting))); return $arrPages; } /** * Add an image to a template * * @param object $template The template object to add the image to * @param array $rowData The element or module as array * @param integer|null $maxWidth An optional maximum width of the image * @param string|null $lightboxGroupIdentifier An optional lightbox group identifier * @param FilesModel|null $filesModel An optional files model * * @deprecated Deprecated since Contao 4.11, to be removed in Contao 5.0; * use the Contao\CoreBundle\Image\Studio\FigureBuilder instead. */ public static function addImageToTemplate($template, array $rowData, $maxWidth = null, $lightboxGroupIdentifier = null, FilesModel $filesModel = null): void { trigger_deprecation('contao/core-bundle', '4.11', 'Using Controller::addImageToTemplate() is deprecated and will no longer work in Contao 5.0. Use the "Contao\CoreBundle\Image\Studio\FigureBuilder" class instead.'); // Helper: Create metadata from the specified row data $createMetadataOverwriteFromRowData = static function (bool $interpretAsContentModel) use ($rowData) { if ($interpretAsContentModel) { // This will be null if "overwriteMeta" is not set return (new ContentModel())->setRow($rowData)->getOverwriteMetadata(); } // Manually create metadata that always contains certain properties (BC) return new Metadata(array( Metadata::VALUE_ALT => $rowData['alt'] ?? '', Metadata::VALUE_TITLE => $rowData['imageTitle'] ?? '', Metadata::VALUE_URL => System::getContainer()->get('contao.insert_tag.parser')->replaceInline($rowData['imageUrl'] ?? ''), 'linkTitle' => (string) ($rowData['linkTitle'] ?? ''), )); }; // Helper: Create fallback template data with (mostly) empty fields (used if resource acquisition fails) $createFallBackTemplateData = static function () use ($filesModel, $rowData) { $templateData = array( 'width' => null, 'height' => null, 'picture' => array( 'img' => array( 'src' => '', 'srcset' => '', ), 'sources' => array(), 'alt' => '', 'title' => '', ), 'singleSRC' => $rowData['singleSRC'], 'src' => '', 'linkTitle' => '', 'margin' => '', 'addImage' => true, 'addBefore' => true, 'fullsize' => false, ); if (null !== $filesModel) { // Set empty metadata $templateData = array_replace_recursive( $templateData, array( 'alt' => '', 'caption' => '', 'imageTitle' => '', 'imageUrl' => '', ) ); } return $templateData; }; // Helper: Get size and margins and handle legacy $maxWidth option $getSizeAndMargin = static function () use ($rowData, $maxWidth) { $size = $rowData['size'] ?? null; $margin = StringUtil::deserialize($rowData['imagemargin'] ?? null); $maxWidth = (int) ($maxWidth ?? Config::get('maxImageWidth')); if (0 === $maxWidth) { return array($size, $margin); } trigger_deprecation('contao/core-bundle', '4.10', 'Using a maximum front end width has been deprecated and will no longer work in Contao 5.0. Remove the "maxImageWidth" configuration and use responsive images instead.'); // Adjust margins if needed if ('px' === ($margin['unit'] ?? null)) { $horizontalMargin = (int) ($margin['left'] ?? 0) + (int) ($margin['right'] ?? 0); if ($maxWidth - $horizontalMargin < 1) { $margin['left'] = ''; $margin['right'] = ''; } else { $maxWidth -= $horizontalMargin; } } // Normalize size if ($size instanceof PictureConfiguration) { return array($size, $margin); } $size = StringUtil::deserialize($size); if (is_numeric($size)) { $size = array(0, 0, (int) $size); } else { $size = (\is_array($size) ? $size : array()) + array(0, 0, 'crop'); $size[0] = (int) $size[0]; $size[1] = (int) $size[1]; } // Adjust image size configuration if it exceeds the max width if ($size[0] > 0 && $size[1] > 0) { list($width, $height) = $size; } else { $container = System::getContainer(); /** @var BoxInterface $originalSize */ $originalSize = $container ->get('contao.image.factory') ->create($container->getParameter('kernel.project_dir') . '/' . $rowData['singleSRC']) ->getDimensions() ->getSize(); $width = $originalSize->getWidth(); $height = $originalSize->getHeight(); } if ($width <= $maxWidth) { return array($size, $margin); } $size[0] = $maxWidth; $size[1] = (int) floor($maxWidth * ($height / $width)); return array($size, $margin); }; $figureBuilder = System::getContainer()->get('contao.image.studio')->createFigureBuilder(); // Set image resource if (null !== $filesModel) { // Make sure model points to the same resource (BC) $filesModel = clone $filesModel; $filesModel->path = $rowData['singleSRC']; // Use source + metadata from files model (if not overwritten) $figureBuilder ->fromFilesModel($filesModel) ->setMetadata($createMetadataOverwriteFromRowData(true)); $includeFullMetadata = true; } else { // Always ignore file metadata when building from path (BC) $figureBuilder ->fromPath($rowData['singleSRC'], false) ->setMetadata($createMetadataOverwriteFromRowData(false)); $includeFullMetadata = false; } // Set size and lightbox configuration list($size, $margin) = $getSizeAndMargin(); $lightboxSize = StringUtil::deserialize($rowData['lightboxSize'] ?? null) ?: null; $figure = $figureBuilder ->setSize($size) ->setLightboxGroupIdentifier($lightboxGroupIdentifier) ->setLightboxSize($lightboxSize) ->enableLightbox((bool) ($rowData['fullsize'] ?? false)) ->buildIfResourceExists(); if (null === $figure) { System::getContainer()->get('monolog.logger.contao.error')->error('Image "' . $rowData['singleSRC'] . '" could not be processed: ' . $figureBuilder->getLastException()->getMessage()); // Fall back to apply a sparse data set instead of failing (BC) foreach ($createFallBackTemplateData() as $key => $value) { $template->$key = $value; } return; } // Build result and apply it to the template $figure->applyLegacyTemplateData($template, $margin, $rowData['floating'] ?? null, $includeFullMetadata); // Fall back to manually specified link title or empty string if not set (backwards compatibility) $template->linkTitle ??= StringUtil::specialchars($rowData['title'] ?? ''); } /** * Add enclosures to a template * * @param object $objTemplate The template object to add the enclosures to * @param array $arrItem The element or module as array * @param string $strKey The name of the enclosures field in $arrItem */ public static function addEnclosuresToTemplate($objTemplate, $arrItem, $strKey='enclosure') { $arrEnclosures = StringUtil::deserialize($arrItem[$strKey]); if (empty($arrEnclosures) || !\is_array($arrEnclosures)) { return; } $objFiles = FilesModel::findMultipleByUuids($arrEnclosures); if ($objFiles === null) { return; } $file = Input::get('file', true); // Send the file to the browser and do not send a 404 header (see #5178) if ($file) { while ($objFiles->next()) { if ($file == $objFiles->path) { static::sendFileToBrowser($file); } } $objFiles->reset(); } /** @var PageModel $objPage */ global $objPage; $arrEnclosures = array(); $allowedDownload = StringUtil::trimsplit(',', strtolower(Config::get('allowedDownload'))); $projectDir = System::getContainer()->getParameter('kernel.project_dir'); // Add download links while ($objFiles->next()) { if ($objFiles->type == 'file') { if (!\in_array($objFiles->extension, $allowedDownload) || !is_file($projectDir . '/' . $objFiles->path)) { continue; } $objFile = new File($objFiles->path); $strHref = Environment::get('request'); // Remove an existing file parameter (see #5683) if (preg_match('/(&(amp;)?|\?)file=/', $strHref)) { $strHref = preg_replace('/(&(amp;)?|\?)file=[^&]+/', '', $strHref); } $strHref .= ((strpos($strHref, '?') !== false) ? '&' : '?') . 'file=' . System::urlEncode($objFiles->path); $arrMeta = Frontend::getMetaData($objFiles->meta, $objPage->language); if (empty($arrMeta) && $objPage->rootFallbackLanguage !== null) { $arrMeta = Frontend::getMetaData($objFiles->meta, $objPage->rootFallbackLanguage); } // Use the file name as title if none is given if (empty($arrMeta['title'])) { $arrMeta['title'] = StringUtil::specialchars($objFile->basename); } $arrEnclosures[] = array ( 'id' => $objFiles->id, 'uuid' => $objFiles->uuid, 'name' => $objFile->basename, 'title' => StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['download'], $objFile->basename)), 'link' => $arrMeta['title'], 'caption' => $arrMeta['caption'] ?? null, 'href' => $strHref, 'filesize' => static::getReadableSize($objFile->filesize), 'icon' => Image::getPath($objFile->icon), 'mime' => $objFile->mime, 'meta' => $arrMeta, 'extension' => $objFile->extension, 'path' => $objFile->dirname, 'enclosure' => $objFiles->path // backwards compatibility ); } } // Order the enclosures if (!empty($arrItem['orderEnclosure'])) { trigger_deprecation('contao/core-bundle', '4.10', 'Using "orderEnclosure" has been deprecated and will no longer work in Contao 5.0. Use a file tree with "isSortable" instead.'); $arrEnclosures = ArrayUtil::sortByOrderField($arrEnclosures, $arrItem['orderEnclosure']); } $objTemplate->enclosure = $arrEnclosures; } /** * Set the static URL constants * * @deprecated Deprecated since Contao 4.13, to be removed in Contao 5.0. */ public static function setStaticUrls() { if (\defined('TL_FILES_URL')) { return; } if (\func_num_args() > 0) { trigger_deprecation('contao/core-bundle', '4.9', 'Using "Contao\Controller::setStaticUrls()" has been deprecated and will no longer work in Contao 5.0. Use the asset contexts instead.'); if (!isset($GLOBALS['objPage'])) { $GLOBALS['objPage'] = func_get_arg(0); } } \define('TL_ASSETS_URL', System::getContainer()->get('contao.assets.assets_context')->getStaticUrl()); \define('TL_FILES_URL', System::getContainer()->get('contao.assets.files_context')->getStaticUrl()); // Deprecated since Contao 4.0, to be removed in Contao 5.0 \define('TL_SCRIPT_URL', TL_ASSETS_URL); \define('TL_PLUGINS_URL', TL_ASSETS_URL); } /** * Add a static URL to a script * * @param string $script The script path * @param ContaoContext|null $context * * @return string The script path with the static URL */ public static function addStaticUrlTo($script, ContaoContext $context = null) { // Absolute URLs if (preg_match('@^https?://@', $script)) { return $script; } if ($context === null) { $context = System::getContainer()->get('contao.assets.assets_context'); } if ($strStaticUrl = $context->getStaticUrl()) { return $strStaticUrl . $script; } return $script; } /** * Add the assets URL to a script * * @param string $script The script path * * @return string The script path with the assets URL */ public static function addAssetsUrlTo($script) { return static::addStaticUrlTo($script, System::getContainer()->get('contao.assets.assets_context')); } /** * Add the files URL to a script * * @param string $script The script path * * @return string The script path with the files URL */ public static function addFilesUrlTo($script) { return static::addStaticUrlTo($script, System::getContainer()->get('contao.assets.files_context')); } /** * Return the current theme as string * * @return string The name of the theme * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Backend::getTheme() instead. */ public static function getTheme() { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getTheme()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Backend::getTheme()" instead.'); return Backend::getTheme(); } /** * Return the back end themes as array * * @return array An array of available back end themes * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Backend::getThemes() instead. */ public static function getBackendThemes() { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getBackendThemes()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Backend::getThemes()" instead.'); return Backend::getThemes(); } /** * Get the details of a page including inherited parameters * * @param mixed $intId A page ID or a Model object * * @return PageModel The page model or null * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use PageModel::findWithDetails() or PageModel->loadDetails() instead. */ public static function getPageDetails($intId) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getPageDetails()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\PageModel::findWithDetails()" or "Contao\PageModel->loadDetails()" instead.'); if ($intId instanceof PageModel) { return $intId->loadDetails(); } if ($intId instanceof Collection) { /** @var PageModel $objPage */ $objPage = $intId->current(); return $objPage->loadDetails(); } if (\is_object($intId)) { $strKey = __METHOD__ . '-' . $intId->id; // Try to load from cache if (Cache::has($strKey)) { return Cache::get($strKey); } // Create a model from the database result $objPage = new PageModel(); $objPage->setRow($intId->row()); $objPage->loadDetails(); Cache::set($strKey, $objPage); return $objPage; } // Invalid ID if ($intId < 1 || !\strlen($intId)) { return null; } $strKey = __METHOD__ . '-' . $intId; // Try to load from cache if (Cache::has($strKey)) { return Cache::get($strKey); } $objPage = PageModel::findWithDetails($intId); Cache::set($strKey, $objPage); return $objPage; } /** * Remove old XML files from the share directory * * @param boolean $blnReturn If true, only return the finds and don't delete * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Automator::purgeXmlFiles() instead. */ protected function removeOldFeeds($blnReturn=false) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::removeOldFeeds()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Automator::purgeXmlFiles()" instead.'); $this->import(Automator::class, 'Automator'); $this->Automator->purgeXmlFiles($blnReturn); } /** * Return true if a class exists (tries to autoload the class) * * @param string $strClass The class name * * @return boolean True if the class exists * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use the PHP function class_exists() instead. */ protected function classFileExists($strClass) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::classFileExists()" has been deprecated and will no longer work in Contao 5.0. Use the PHP function "class_exists()" instead.'); return class_exists($strClass); } /** * Restore basic entities * * @param string $strBuffer The string with the tags to be replaced * * @return string The string with the original entities * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use StringUtil::restoreBasicEntities() instead. */ public static function restoreBasicEntities($strBuffer) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::restoreBasicEntities()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\StringUtil::restoreBasicEntities()" instead.'); return StringUtil::restoreBasicEntities($strBuffer); } /** * Resize an image and crop it if necessary * * @param string $image The image path * @param integer $width The target width * @param integer $height The target height * @param string $mode An optional resize mode * * @return boolean True if the image has been resized correctly * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Image::resize() instead. */ protected function resizeImage($image, $width, $height, $mode='') { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::resizeImage()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Image::resize()" instead.'); return Image::resize($image, $width, $height, $mode); } /** * Resize an image and crop it if necessary * * @param string $image The image path * @param integer $width The target width * @param integer $height The target height * @param string $mode An optional resize mode * @param string $target An optional target to be replaced * @param boolean $force Override existing target images * * @return string|null The image path or null * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Image::get() instead. */ protected function getImage($image, $width, $height, $mode='', $target=null, $force=false) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getImage()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Image::get()" instead.'); return Image::get($image, $width, $height, $mode, $target, $force); } /** * Generate an image tag and return it as string * * @param string $src The image path * @param string $alt An optional alt attribute * @param string $attributes A string of other attributes * * @return string The image HTML tag * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Image::getHtml() instead. */ public static function generateImage($src, $alt='', $attributes='') { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::generateImage()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Image::getHtml()" instead.'); return Image::getHtml($src, $alt, $attributes); } /** * Return the date picker string (see #3218) * * @return boolean * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Specify "datepicker"=>true in your DCA file instead. */ protected function getDatePickerString() { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getDatePickerString()" has been deprecated and will no longer work in Contao 5.0. Specify "\'datepicker\' => true" in your DCA file instead.'); return true; } /** * Return the installed back end languages as array * * @return array An array of available back end languages * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use the Contao\CoreBundle\Intl\Locales service instead. */ protected function getBackendLanguages() { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getBackendLanguages()" has been deprecated and will no longer work in Contao 5.0. Use the Contao\CoreBundle\Intl\Locales service instead.'); return $this->getLanguages(true); } /** * Parse simple tokens that can be used to personalize newsletters * * @param string $strBuffer The text with the tokens to be replaced * @param array $arrData The replacement data as array * * @return string The text with the replaced tokens * * @deprecated Deprecated since Contao 4.10, to be removed in Contao 5.0; * Use the contao.string.simple_token_parser service instead. */ protected function parseSimpleTokens($strBuffer, $arrData) { trigger_deprecation('contao/core-bundle', '4.10', 'Using "Contao\Controller::parseSimpleTokens()" has been deprecated and will no longer work in Contao 5.0. Use the "contao.string.simple_token_parser" service instead.'); return System::getContainer()->get('contao.string.simple_token_parser')->parse($strBuffer, $arrData); } /** * Convert a DCA file configuration to be used with widgets * * @param array $arrData The field configuration array * @param string $strName The field name in the form * @param mixed $varValue The field value * @param string $strField The field name in the database * @param string $strTable The table name * * @return array An array that can be passed to a widget * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Widget::getAttributesFromDca() instead. */ protected function prepareForWidget($arrData, $strName, $varValue=null, $strField='', $strTable='') { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::prepareForWidget()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Widget::getAttributesFromDca()" instead.'); return Widget::getAttributesFromDca($arrData, $strName, $varValue, $strField, $strTable); } /** * Return the IDs of all child records of a particular record (see #2475) * * @param mixed $arrParentIds An array of parent IDs * @param string $strTable The table name * @param boolean $blnSorting True if the table has a sorting field * @param array $arrReturn The array to be returned * @param string $strWhere Additional WHERE condition * * @return array An array of child record IDs * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Database::getChildRecords() instead. */ protected function getChildRecords($arrParentIds, $strTable, $blnSorting=false, $arrReturn=array(), $strWhere='') { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getChildRecords()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Database::getChildRecords()" instead.'); return $this->Database->getChildRecords($arrParentIds, $strTable, $blnSorting, $arrReturn, $strWhere); } /** * Return the IDs of all parent records of a particular record * * @param integer $intId The ID of the record * @param string $strTable The table name * * @return array An array of parent record IDs * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Database::getParentRecords() instead. */ protected function getParentRecords($intId, $strTable) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getParentRecords()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Database::getParentRecords()" instead.'); return $this->Database->getParentRecords($intId, $strTable); } /** * Print an article as PDF and stream it to the browser * * @param ModuleModel $objArticle An article object * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use ModuleArticle->generatePdf() instead. */ protected function printArticleAsPdf($objArticle) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::printArticleAsPdf()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\ModuleArticle->generatePdf()" instead.'); $objArticle = new ModuleArticle($objArticle); $objArticle->generatePdf(); } /** * Return all page sections as array * * @return array An array of active page sections * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * See https://github.com/contao/core/issues/4693. */ public static function getPageSections() { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::getPageSections()" has been deprecated and will no longer work in Contao 5.0.'); return array('header', 'left', 'right', 'main', 'footer'); } /** * Return a "selected" attribute if the option is selected * * @param string $strOption The option to check * @param mixed $varValues One or more values to check against * * @return string The attribute or an empty string * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Widget::optionSelected() instead. */ public static function optionSelected($strOption, $varValues) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::optionSelected()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Widget::optionSelected()" instead.'); return Widget::optionSelected($strOption, $varValues); } /** * Return a "checked" attribute if the option is checked * * @param string $strOption The option to check * @param mixed $varValues One or more values to check against * * @return string The attribute or an empty string * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Widget::optionChecked() instead. */ public static function optionChecked($strOption, $varValues) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::optionChecked()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Widget::optionChecked()" instead.'); return Widget::optionChecked($strOption, $varValues); } /** * Find a content element in the TL_CTE array and return the class name * * @param string $strName The content element name * * @return string The class name * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use ContentElement::findClass() instead. */ public static function findContentElement($strName) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::findContentElement()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\ContentElement::findClass()" instead.'); return ContentElement::findClass($strName); } /** * Find a front end module in the FE_MOD array and return the class name * * @param string $strName The front end module name * * @return string The class name * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Module::findClass() instead. */ public static function findFrontendModule($strName) { trigger_deprecation('contao/core-bundle', '4.0', 'Using Contao\Controller::findFrontendModule() has been deprecated and will no longer work in Contao 5.0. Use Contao\Module::findClass() instead.'); return Module::findClass($strName); } /** * Create an initial version of a record * * @param string $strTable The table name * @param integer $intId The ID of the element to be versioned * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Versions->initialize() instead. */ protected function createInitialVersion($strTable, $intId) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::createInitialVersion()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Versions->initialize()" instead.'); $objVersions = new Versions($strTable, $intId); $objVersions->initialize(); } /** * Create a new version of a record * * @param string $strTable The table name * @param integer $intId The ID of the element to be versioned * * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0. * Use Versions->create() instead. */ protected function createNewVersion($strTable, $intId) { trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Controller::createNewVersion()" has been deprecated and will no longer work in Contao 5.0. Use "Contao\Versions->create()" instead.'); $objVersions = new Versions($strTable, $intId); $objVersions->create(); } /** * Return the files matching a GLOB pattern * * @param string $pattern * * @return array|false */ protected static function braceGlob($pattern) { // Use glob() if possible if (false === strpos($pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($pattern, '{'))) { return glob($pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0); } $finder = new Finder(); $regex = Glob::toRegex($pattern); // All files in the given template folder $filesIterator = $finder ->files() ->followLinks() ->sortByName() ->in(\dirname($pattern)) ; // Match the actual regex and filter the files $filesIterator = $filesIterator->filter(static function (\SplFileInfo $info) use ($regex) { $path = $info->getPathname(); return preg_match($regex, $path) && $info->isFile(); }); $files = iterator_to_array($filesIterator); return array_keys($files); } } class_alias(Controller::class, 'Controller'); An Error Occurred: Internal Server Error

Oops! An Error Occurred

The server returned a "500 Internal Server Error".

Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.