vendor/contao/core-bundle/src/Resources/contao/controllers/FrontendIndex.php line 98

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\AccessDeniedException;
  11. use Contao\CoreBundle\Exception\InsufficientAuthenticationException;
  12. use Contao\CoreBundle\Exception\PageNotFoundException;
  13. use Contao\CoreBundle\Exception\ResponseException;
  14. use Contao\CoreBundle\Security\ContaoCorePermissions;
  15. use Contao\CoreBundle\Util\LocaleUtil;
  16. use Contao\Model\Collection;
  17. use Symfony\Component\HttpFoundation\Response;
  18. /**
  19. * Main front end controller.
  20. */
  21. class FrontendIndex extends Frontend
  22. {
  23. /**
  24. * Initialize the object
  25. */
  26. public function __construct()
  27. {
  28. // Load the user object before calling the parent constructor
  29. $this->import(FrontendUser::class, 'User');
  30. parent::__construct();
  31. }
  32. /**
  33. * Run the controller
  34. *
  35. * @return Response
  36. *
  37. * @throws PageNotFoundException
  38. */
  39. public function run()
  40. {
  41. trigger_deprecation('contao/core-bundle', '4.10', 'Using "Contao\FrontendIndex::run()" has been deprecated and will no longer work in Contao 5.0. Use the Symfony routing instead.');
  42. $pageId = $this->getPageIdFromUrl();
  43. // Load a website root page object if there is no page ID
  44. if ($pageId === null)
  45. {
  46. $objRootPage = $this->getRootPageFromUrl();
  47. /** @var PageRoot $objHandler */
  48. $objHandler = new $GLOBALS['TL_PTY']['root']();
  49. $pageId = $objHandler->generate($objRootPage->id, true, true);
  50. }
  51. // Throw a 404 error if the request is not a Contao request (see #2864)
  52. elseif ($pageId === false)
  53. {
  54. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  55. }
  56. $pageModel = PageModel::findPublishedByIdOrAlias($pageId);
  57. // Throw a 404 error if the page could not be found
  58. if ($pageModel === null)
  59. {
  60. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  61. }
  62. return $this->renderPage($pageModel);
  63. }
  64. /**
  65. * Render a page
  66. *
  67. * @param Collection|PageModel[]|PageModel $pageModel
  68. *
  69. * @return Response
  70. *
  71. * @throws \LogicException
  72. * @throws PageNotFoundException
  73. * @throws AccessDeniedException
  74. */
  75. public function renderPage($pageModel)
  76. {
  77. /** @var PageModel $objPage */
  78. global $objPage;
  79. $objPage = $pageModel;
  80. // Check the URL and language of each page if there are multiple results
  81. if ($objPage instanceof Collection && $objPage->count() > 1)
  82. {
  83. trigger_deprecation('contao/core-bundle', '4.7', 'Using "Contao\FrontendIndex::renderPage()" with a model collection has been deprecated and will no longer work Contao 5.0. Use the Symfony routing instead.');
  84. $arrPages = array();
  85. // Order by domain and language
  86. while ($objPage->next())
  87. {
  88. /** @var PageModel $objModel */
  89. $objModel = $objPage->current();
  90. $objCurrentPage = $objModel->loadDetails();
  91. $domain = $objCurrentPage->domain ?: '*';
  92. $arrPages[$domain][$objCurrentPage->rootLanguage] = $objCurrentPage;
  93. // Also store the fallback language
  94. if ($objCurrentPage->rootIsFallback)
  95. {
  96. $arrPages[$domain]['*'] = $objCurrentPage;
  97. }
  98. }
  99. $strHost = Environment::get('host');
  100. // Look for a root page whose domain name matches the host name
  101. $arrLangs = $arrPages[$strHost] ?? ($arrPages['*'] ?: array());
  102. // Throw an exception if there are no matches (see #1522)
  103. if (empty($arrLangs))
  104. {
  105. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  106. }
  107. // Use the first result (see #4872)
  108. if (!System::getContainer()->getParameter('contao.legacy_routing') || !System::getContainer()->getParameter('contao.prepend_locale'))
  109. {
  110. $objNewPage = current($arrLangs);
  111. }
  112. // Try to find a page matching the language parameter
  113. elseif (($lang = Input::get('language')) && isset($arrLangs[$lang]))
  114. {
  115. $objNewPage = $arrLangs[$lang];
  116. }
  117. // Use the fallback language (see #8142)
  118. elseif (isset($arrLangs['*']))
  119. {
  120. $objNewPage = $arrLangs['*'];
  121. }
  122. // Throw an exception if there is no matching page (see #1522)
  123. else
  124. {
  125. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  126. }
  127. // Store the page object
  128. if (\is_object($objNewPage))
  129. {
  130. $objPage = $objNewPage;
  131. }
  132. }
  133. // Throw a 500 error if the result is still ambiguous
  134. if ($objPage instanceof Collection && $objPage->count() > 1)
  135. {
  136. System::getContainer()->get('monolog.logger.contao.error')->error('More than one page matches ' . Environment::get('base') . Environment::get('request'));
  137. throw new \LogicException('More than one page found: ' . Environment::get('uri'));
  138. }
  139. // Make sure $objPage is a Model
  140. if ($objPage instanceof Collection)
  141. {
  142. $objPage = $objPage->current();
  143. }
  144. // If the page has an alias, it can no longer be called via ID (see #7661)
  145. if ($objPage->alias)
  146. {
  147. $language = $objPage->urlPrefix ? preg_quote($objPage->urlPrefix . '/', '#') : '';
  148. $suffix = preg_quote($objPage->urlSuffix, '#');
  149. if (preg_match('#^' . $language . $objPage->id . '(' . $suffix . '($|\?)|/)#', Environment::get('relativeRequest')))
  150. {
  151. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  152. }
  153. }
  154. // Trigger the 404 page if an item is required but not given (see #8361)
  155. if ($objPage->requireItem)
  156. {
  157. $hasItem = false;
  158. if (Config::get('useAutoItem'))
  159. {
  160. $hasItem = isset($_GET['auto_item']);
  161. }
  162. else
  163. {
  164. foreach ($GLOBALS['TL_AUTO_ITEM'] as $item)
  165. {
  166. if (isset($_GET[$item]))
  167. {
  168. $hasItem = true;
  169. break;
  170. }
  171. }
  172. }
  173. if (!$hasItem)
  174. {
  175. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  176. }
  177. }
  178. // Inherit the settings from the parent pages
  179. $objPage->loadDetails();
  180. $blnShowUnpublished = System::getContainer()->get('contao.security.token_checker')->isPreviewMode();
  181. // Trigger the 404 page if the page is not published and the front end preview is not active (see #374)
  182. if (!$blnShowUnpublished && !$objPage->isPublic)
  183. {
  184. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  185. }
  186. // Load a website root page object (will redirect to the first active regular page)
  187. if ($objPage->type == 'root')
  188. {
  189. /** @var PageRoot $objHandler */
  190. $objHandler = new $GLOBALS['TL_PTY']['root']();
  191. throw new ResponseException($objHandler->getResponse($objPage->id));
  192. }
  193. // Set the admin e-mail address
  194. if ($objPage->adminEmail)
  195. {
  196. list($GLOBALS['TL_ADMIN_NAME'], $GLOBALS['TL_ADMIN_EMAIL']) = StringUtil::splitFriendlyEmail($objPage->adminEmail);
  197. }
  198. else
  199. {
  200. list($GLOBALS['TL_ADMIN_NAME'], $GLOBALS['TL_ADMIN_EMAIL']) = StringUtil::splitFriendlyEmail(Config::get('adminEmail'));
  201. }
  202. // Exit if the root page has not been published (see #2425)
  203. // Do not try to load the 404 page, it can cause an infinite loop!
  204. if (!$blnShowUnpublished && !$objPage->rootIsPublic)
  205. {
  206. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  207. }
  208. // Check whether the language matches the root page language
  209. if (isset($_GET['language']) && $objPage->urlPrefix && Input::get('language') != LocaleUtil::formatAsLanguageTag($objPage->rootLanguage))
  210. {
  211. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  212. }
  213. // Check whether there are domain name restrictions
  214. if ($objPage->domain && $objPage->domain != Environment::get('host'))
  215. {
  216. System::getContainer()->get('monolog.logger.contao.error')->error('Page ID "' . $objPage->id . '" was requested via "' . Environment::get('host') . '" but can only be accessed via "' . $objPage->domain . '" (' . Environment::get('base') . Environment::get('request') . ')');
  217. throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
  218. }
  219. // Authenticate the user if the page is protected
  220. if ($objPage->protected)
  221. {
  222. $security = System::getContainer()->get('security.helper');
  223. if (!$security->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPS, $objPage->groups))
  224. {
  225. if (($token = $security->getToken()) === null || System::getContainer()->get('security.authentication.trust_resolver')->isAnonymous($token))
  226. {
  227. throw new InsufficientAuthenticationException('Not authenticated: ' . Environment::get('uri'));
  228. }
  229. $user = $security->getUser();
  230. if ($user instanceof FrontendUser)
  231. {
  232. System::getContainer()->get('monolog.logger.contao.error')->error('Page ID "' . $objPage->id . '" can only be accessed by groups "' . implode(', ', $objPage->groups) . '" (current user groups: ' . implode(', ', StringUtil::deserialize($user->groups, true)) . ')');
  233. }
  234. throw new AccessDeniedException('Access denied: ' . Environment::get('uri'));
  235. }
  236. }
  237. // Backup some globals (see #7659)
  238. $arrBackup = array(
  239. $GLOBALS['TL_HEAD'] ?? array(),
  240. $GLOBALS['TL_BODY'] ?? array(),
  241. $GLOBALS['TL_MOOTOOLS'] ?? array(),
  242. $GLOBALS['TL_JQUERY'] ?? array(),
  243. $GLOBALS['TL_USER_CSS'] ?? array(),
  244. $GLOBALS['TL_FRAMEWORK_CSS'] ?? array()
  245. );
  246. try
  247. {
  248. $pageType = $GLOBALS['TL_PTY'][$objPage->type] ?? PageRegular::class;
  249. $objHandler = new $pageType();
  250. // Backwards compatibility
  251. if (!method_exists($objHandler, 'getResponse'))
  252. {
  253. ob_start();
  254. try
  255. {
  256. $objHandler->generate($objPage, true);
  257. $objResponse = new Response(ob_get_contents(), http_response_code());
  258. }
  259. finally
  260. {
  261. ob_end_clean();
  262. }
  263. return $objResponse;
  264. }
  265. return $objHandler->getResponse($objPage, true);
  266. }
  267. // Render the error page (see #5570)
  268. catch (UnusedArgumentsException $e)
  269. {
  270. // Restore the globals (see #7659)
  271. list(
  272. $GLOBALS['TL_HEAD'],
  273. $GLOBALS['TL_BODY'],
  274. $GLOBALS['TL_MOOTOOLS'],
  275. $GLOBALS['TL_JQUERY'],
  276. $GLOBALS['TL_USER_CSS'],
  277. $GLOBALS['TL_FRAMEWORK_CSS']
  278. ) = $arrBackup;
  279. throw $e;
  280. }
  281. }
  282. /**
  283. * Try to load the page from the cache
  284. *
  285. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  286. * Use the kernel.request event instead.
  287. */
  288. protected function outputFromCache()
  289. {
  290. trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\FrontendIndex::outputFromCache()" has been deprecated and will no longer work in Contao 5.0. Use the "kernel.request" event instead.');
  291. }
  292. }
  293. class_alias(FrontendIndex::class, 'FrontendIndex');