vendor/contao/core-bundle/src/Resources/contao/models/PageModel.php line 294

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\NoRootPageFoundException;
  11. use Contao\CoreBundle\Routing\ResponseContext\HtmlHeadBag\HtmlHeadBag;
  12. use Contao\CoreBundle\Routing\ResponseContext\JsonLd\ContaoPageSchema;
  13. use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager;
  14. use Contao\CoreBundle\Routing\ResponseContext\ResponseContext;
  15. use Contao\CoreBundle\Routing\ResponseContext\ResponseContextAccessor;
  16. use Contao\CoreBundle\Util\LocaleUtil;
  17. use Contao\Model\Collection;
  18. use Contao\Model\Registry;
  19. use Symfony\Cmf\Component\Routing\RouteObjectInterface;
  20. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  21. /**
  22.  * Reads and writes pages
  23.  *
  24.  * @property string|integer         $id
  25.  * @property string|integer         $pid
  26.  * @property string|integer         $sorting
  27.  * @property string|integer         $tstamp
  28.  * @property string                 $title
  29.  * @property string                 $alias
  30.  * @property string                 $type
  31.  * @property string                 $pageTitle
  32.  * @property string                 $language
  33.  * @property string                 $robots
  34.  * @property string|null            $description
  35.  * @property string                 $redirect
  36.  * @property string|integer         $jumpTo
  37.  * @property string|boolean         $redirectBack
  38.  * @property string                 $url
  39.  * @property string|boolean         $target
  40.  * @property string                 $dns
  41.  * @property string                 $staticFiles
  42.  * @property string                 $staticPlugins
  43.  * @property string|boolean         $fallback
  44.  * @property string|boolean         $disableLanguageRedirect
  45.  * @property string|null            $favicon
  46.  * @property string|null            $robotsTxt
  47.  * @property string                 $mailerTransport
  48.  * @property string                 $adminEmail
  49.  * @property string                 $dateFormat
  50.  * @property string                 $timeFormat
  51.  * @property string                 $datimFormat
  52.  * @property string                 $validAliasCharacters
  53.  * @property string|boolean         $useFolderUrl
  54.  * @property string                 $urlPrefix
  55.  * @property string                 $urlSuffix
  56.  * @property string|boolean         $useSSL
  57.  * @property string|boolean         $autoforward
  58.  * @property string|boolean         $protected
  59.  * @property string|array|null      $groups
  60.  * @property string|boolean         $includeLayout
  61.  * @property string|integer         $layout
  62.  * @property string|boolean         $includeCache
  63.  * @property string|integer|boolean $cache
  64.  * @property string|boolean         $alwaysLoadFromCache
  65.  * @property string|integer|boolean $clientCache
  66.  * @property string|boolean         $includeChmod
  67.  * @property string|integer         $cuser
  68.  * @property string|integer         $cgroup
  69.  * @property string                 $chmod
  70.  * @property string|boolean         $noSearch
  71.  * @property string|boolean         $requireItem
  72.  * @property string                 $cssClass
  73.  * @property string                 $sitemap
  74.  * @property string|boolean         $hide
  75.  * @property string|boolean         $guests
  76.  * @property string|integer         $tabindex
  77.  * @property string                 $accesskey
  78.  * @property string|boolean         $published
  79.  * @property string|integer         $start
  80.  * @property string|integer         $stop
  81.  * @property string|boolean         $enforceTwoFactor
  82.  * @property string|integer         $twoFactorJumpTo
  83.  *
  84.  * @property array          $trail
  85.  * @property string         $mainAlias
  86.  * @property string         $mainTitle
  87.  * @property string         $mainPageTitle
  88.  * @property string         $parentAlias
  89.  * @property string         $parentTitle
  90.  * @property string         $parentPageTitle
  91.  * @property string         $folderUrl
  92.  * @property boolean        $isPublic
  93.  * @property integer        $rootId
  94.  * @property string         $rootAlias
  95.  * @property string         $rootTitle
  96.  * @property string         $rootPageTitle
  97.  * @property integer        $rootSorting
  98.  * @property string         $domain
  99.  * @property string         $rootLanguage
  100.  * @property boolean        $rootIsPublic
  101.  * @property boolean        $rootIsFallback
  102.  * @property string|boolean $rootUseSSL
  103.  * @property string         $rootFallbackLanguage
  104.  * @property boolean        $minifyMarkup
  105.  * @property integer        $layoutId
  106.  * @property boolean        $hasJQuery
  107.  * @property boolean        $hasMooTools
  108.  * @property string         $template
  109.  * @property string         $templateGroup
  110.  * @property boolean        $useAutoItem
  111.  *
  112.  * @method static PageModel|null findById($id, array $opt=array())
  113.  * @method static PageModel|null findByPk($id, array $opt=array())
  114.  * @method static PageModel|null findByIdOrAlias($val, array $opt=array())
  115.  * @method static PageModel|null findOneBy($col, $val, array $opt=array())
  116.  * @method static PageModel|null findOneByPid($val, array $opt=array())
  117.  * @method static PageModel|null findOneBySorting($val, array $opt=array())
  118.  * @method static PageModel|null findOneByTstamp($val, array $opt=array())
  119.  * @method static PageModel|null findOneByTitle($val, array $opt=array())
  120.  * @method static PageModel|null findOneByAlias($val, array $opt=array())
  121.  * @method static PageModel|null findOneByType($val, array $opt=array())
  122.  * @method static PageModel|null findOneByPageTitle($val, array $opt=array())
  123.  * @method static PageModel|null findOneByLanguage($val, array $opt=array())
  124.  * @method static PageModel|null findOneByRobots($val, array $opt=array())
  125.  * @method static PageModel|null findOneByDescription($val, array $opt=array())
  126.  * @method static PageModel|null findOneByRedirect($val, array $opt=array())
  127.  * @method static PageModel|null findOneByJumpTo($val, array $opt=array())
  128.  * @method static PageModel|null findOneByRedirectBack($val, array $opt=array())
  129.  * @method static PageModel|null findOneByUrl($val, array $opt=array())
  130.  * @method static PageModel|null findOneByTarget($val, array $opt=array())
  131.  * @method static PageModel|null findOneByDns($val, array $opt=array())
  132.  * @method static PageModel|null findOneByStaticFiles($val, array $opt=array())
  133.  * @method static PageModel|null findOneByStaticPlugins($val, array $opt=array())
  134.  * @method static PageModel|null findOneByFallback($val, array $opt=array())
  135.  * @method static PageModel|null findOneByDisableLanguageRedirect($val, array $opt=array())
  136.  * @method static PageModel|null findOneByFavicon($val, array $opt=array())
  137.  * @method static PageModel|null findOneByRobotsTxt($val, array $opt=array())
  138.  * @method static PageModel|null findOneByMailerTransport($val, array $opt=array())
  139.  * @method static PageModel|null findOneByAdminEmail($val, array $opt=array())
  140.  * @method static PageModel|null findOneByDateFormat($val, array $opt=array())
  141.  * @method static PageModel|null findOneByTimeFormat($val, array $opt=array())
  142.  * @method static PageModel|null findOneByDatimFormat($val, array $opt=array())
  143.  * @method static PageModel|null findOneByValidAliasCharacters($val, array $opt=array())
  144.  * @method static PageModel|null findOneByUseFolderUrl($val, array $opt=array())
  145.  * @method static PageModel|null findOneByUrlPrefix($val, array $opt=array())
  146.  * @method static PageModel|null findOneByUrlSuffix($val, array $opt=array())
  147.  * @method static PageModel|null findOneByUseSSL($val, array $opt=array())
  148.  * @method static PageModel|null findOneByAutoforward($val, array $opt=array())
  149.  * @method static PageModel|null findOneByProtected($val, array $opt=array())
  150.  * @method static PageModel|null findOneByGroups($val, array $opt=array())
  151.  * @method static PageModel|null findOneByIncludeLayout($val, array $opt=array())
  152.  * @method static PageModel|null findOneByLayout($val, array $opt=array())
  153.  * @method static PageModel|null findOneByIncludeCache($val, array $opt=array())
  154.  * @method static PageModel|null findOneByCache($val, array $opt=array())
  155.  * @method static PageModel|null findOneByIncludeChmod($val, array $opt=array())
  156.  * @method static PageModel|null findOneByCuser($val, array $opt=array())
  157.  * @method static PageModel|null findOneByCgroup($val, array $opt=array())
  158.  * @method static PageModel|null findOneByChmod($val, array $opt=array())
  159.  * @method static PageModel|null findOneByNoSearch($val, array $opt=array())
  160.  * @method static PageModel|null findOneByCssClass($val, array $opt=array())
  161.  * @method static PageModel|null findOneBySitemap($val, array $opt=array())
  162.  * @method static PageModel|null findOneByHide($val, array $opt=array())
  163.  * @method static PageModel|null findOneByGuests($val, array $opt=array())
  164.  * @method static PageModel|null findOneByTabindex($val, array $opt=array())
  165.  * @method static PageModel|null findOneByAccesskey($val, array $opt=array())
  166.  * @method static PageModel|null findOneByPublished($val, array $opt=array())
  167.  * @method static PageModel|null findOneByStart($val, array $opt=array())
  168.  * @method static PageModel|null findOneByStop($val, array $opt=array())
  169.  * @method static PageModel|null findOneByEnforceTwoFactor($val, array $opt=array())
  170.  * @method static PageModel|null findOneByTwoFactorJumpTo($val, array $opt=array())
  171.  *
  172.  * @method static Collection|PageModel[]|PageModel|null findByPid($val, array $opt=array())
  173.  * @method static Collection|PageModel[]|PageModel|null findBySorting($val, array $opt=array())
  174.  * @method static Collection|PageModel[]|PageModel|null findByTstamp($val, array $opt=array())
  175.  * @method static Collection|PageModel[]|PageModel|null findByTitle($val, array $opt=array())
  176.  * @method static Collection|PageModel[]|PageModel|null findByAlias($val, array $opt=array())
  177.  * @method static Collection|PageModel[]|PageModel|null findByType($val, array $opt=array())
  178.  * @method static Collection|PageModel[]|PageModel|null findByPageTitle($val, array $opt=array())
  179.  * @method static Collection|PageModel[]|PageModel|null findByLanguage($val, array $opt=array())
  180.  * @method static Collection|PageModel[]|PageModel|null findByRobots($val, array $opt=array())
  181.  * @method static Collection|PageModel[]|PageModel|null findByDescription($val, array $opt=array())
  182.  * @method static Collection|PageModel[]|PageModel|null findByRedirect($val, array $opt=array())
  183.  * @method static Collection|PageModel[]|PageModel|null findByJumpTo($val, array $opt=array())
  184.  * @method static Collection|PageModel[]|PageModel|null findByRedirectBack($val, array $opt=array())
  185.  * @method static Collection|PageModel[]|PageModel|null findByUrl($val, array $opt=array())
  186.  * @method static Collection|PageModel[]|PageModel|null findByTarget($val, array $opt=array())
  187.  * @method static Collection|PageModel[]|PageModel|null findByDns($val, array $opt=array())
  188.  * @method static Collection|PageModel[]|PageModel|null findByStaticFiles($val, array $opt=array())
  189.  * @method static Collection|PageModel[]|PageModel|null findByStaticPlugins($val, array $opt=array())
  190.  * @method static Collection|PageModel[]|PageModel|null findByFallback($val, array $opt=array())
  191.  * @method static Collection|PageModel[]|PageModel|null findByDisableLanguageRedirect($val, array $opt=array())
  192.  * @method static Collection|PageModel[]|PageModel|null findByFavicon($val, array $opt=array())
  193.  * @method static Collection|PageModel[]|PageModel|null findByRobotsTxt($val, array $opt=array())
  194.  * @method static Collection|PageModel[]|PageModel|null findByMailerTransport($val, array $opt=array())
  195.  * @method static Collection|PageModel[]|PageModel|null findByAdminEmail($val, array $opt=array())
  196.  * @method static Collection|PageModel[]|PageModel|null findByDateFormat($val, array $opt=array())
  197.  * @method static Collection|PageModel[]|PageModel|null findByTimeFormat($val, array $opt=array())
  198.  * @method static Collection|PageModel[]|PageModel|null findByDatimFormat($val, array $opt=array())
  199.  * @method static Collection|PageModel[]|PageModel|null findByValidAliasCharacters($val, array $opt=array())
  200.  * @method static Collection|PageModel[]|PageModel|null findByUseFolderUrl($val, array $opt=array())
  201.  * @method static Collection|PageModel[]|PageModel|null findByUrlPrefix($val, array $opt=array())
  202.  * @method static Collection|PageModel[]|PageModel|null findByUrlSuffix($val, array $opt=array())
  203.  * @method static Collection|PageModel[]|PageModel|null findByUseSSL($val, array $opt=array())
  204.  * @method static Collection|PageModel[]|PageModel|null findByAutoforward($val, array $opt=array())
  205.  * @method static Collection|PageModel[]|PageModel|null findByProtected($val, array $opt=array())
  206.  * @method static Collection|PageModel[]|PageModel|null findByGroups($val, array $opt=array())
  207.  * @method static Collection|PageModel[]|PageModel|null findByIncludeLayout($val, array $opt=array())
  208.  * @method static Collection|PageModel[]|PageModel|null findByLayout($val, array $opt=array())
  209.  * @method static Collection|PageModel[]|PageModel|null findByIncludeCache($val, array $opt=array())
  210.  * @method static Collection|PageModel[]|PageModel|null findByCache($val, array $opt=array())
  211.  * @method static Collection|PageModel[]|PageModel|null findByIncludeChmod($val, array $opt=array())
  212.  * @method static Collection|PageModel[]|PageModel|null findByCuser($val, array $opt=array())
  213.  * @method static Collection|PageModel[]|PageModel|null findByCgroup($val, array $opt=array())
  214.  * @method static Collection|PageModel[]|PageModel|null findByChmod($val, array $opt=array())
  215.  * @method static Collection|PageModel[]|PageModel|null findByNoSearch($val, array $opt=array())
  216.  * @method static Collection|PageModel[]|PageModel|null findByCssClass($val, array $opt=array())
  217.  * @method static Collection|PageModel[]|PageModel|null findBySitemap($val, array $opt=array())
  218.  * @method static Collection|PageModel[]|PageModel|null findByHide($val, array $opt=array())
  219.  * @method static Collection|PageModel[]|PageModel|null findByGuests($val, array $opt=array())
  220.  * @method static Collection|PageModel[]|PageModel|null findByTabindex($val, array $opt=array())
  221.  * @method static Collection|PageModel[]|PageModel|null findByAccesskey($val, array $opt=array())
  222.  * @method static Collection|PageModel[]|PageModel|null findByPublished($val, array $opt=array())
  223.  * @method static Collection|PageModel[]|PageModel|null findByStart($val, array $opt=array())
  224.  * @method static Collection|PageModel[]|PageModel|null findByStop($val, array $opt=array())
  225.  * @method static Collection|PageModel[]|PageModel|null findByEnforceTwoFactor($val, array $opt=array())
  226.  * @method static Collection|PageModel[]|PageModel|null findByTwoFactorJumpTo($val, array $opt=array())
  227.  * @method static Collection|PageModel[]|PageModel|null findMultipleByIds($val, array $opt=array())
  228.  * @method static Collection|PageModel[]|PageModel|null findBy($col, $val, array $opt=array())
  229.  * @method static Collection|PageModel[]|PageModel|null findAll(array $opt=array())
  230.  *
  231.  * @method static integer countById($id, array $opt=array())
  232.  * @method static integer countByPid($val, array $opt=array())
  233.  * @method static integer countBySorting($val, array $opt=array())
  234.  * @method static integer countByTstamp($val, array $opt=array())
  235.  * @method static integer countByTitle($val, array $opt=array())
  236.  * @method static integer countByAlias($val, array $opt=array())
  237.  * @method static integer countByType($val, array $opt=array())
  238.  * @method static integer countByPageTitle($val, array $opt=array())
  239.  * @method static integer countByLanguage($val, array $opt=array())
  240.  * @method static integer countByRobots($val, array $opt=array())
  241.  * @method static integer countByDescription($val, array $opt=array())
  242.  * @method static integer countByRedirect($val, array $opt=array())
  243.  * @method static integer countByJumpTo($val, array $opt=array())
  244.  * @method static integer countByRedirectBack($val, array $opt=array())
  245.  * @method static integer countByUrl($val, array $opt=array())
  246.  * @method static integer countByTarget($val, array $opt=array())
  247.  * @method static integer countByDns($val, array $opt=array())
  248.  * @method static integer countByStaticFiles($val, array $opt=array())
  249.  * @method static integer countByStaticPlugins($val, array $opt=array())
  250.  * @method static integer countByFallback($val, array $opt=array())
  251.  * @method static integer countByDisableLanguageRedirect($val, array $opt=array())
  252.  * @method static integer countByFavicon($val, array $opt=array())
  253.  * @method static integer countByRobotsTxt($val, array $opt=array())
  254.  * @method static integer countByMailerTransport($val, array $opt=array())
  255.  * @method static integer countByAdminEmail($val, array $opt=array())
  256.  * @method static integer countByDateFormat($val, array $opt=array())
  257.  * @method static integer countByTimeFormat($val, array $opt=array())
  258.  * @method static integer countByDatimFormat($val, array $opt=array())
  259.  * @method static integer countByValidAliasCharacters($val, array $opt=array())
  260.  * @method static integer countByUseFolderUrl($val, array $opt=array())
  261.  * @method static integer countByUrlPrefix($val, array $opt=array())
  262.  * @method static integer countByUrlSuffix($val, array $opt=array())
  263.  * @method static integer countByUseSSL($val, array $opt=array())
  264.  * @method static integer countByAutoforward($val, array $opt=array())
  265.  * @method static integer countByProtected($val, array $opt=array())
  266.  * @method static integer countByGroups($val, array $opt=array())
  267.  * @method static integer countByIncludeLayout($val, array $opt=array())
  268.  * @method static integer countByLayout($val, array $opt=array())
  269.  * @method static integer countByIncludeCache($val, array $opt=array())
  270.  * @method static integer countByCache($val, array $opt=array())
  271.  * @method static integer countByIncludeChmod($val, array $opt=array())
  272.  * @method static integer countByCuser($val, array $opt=array())
  273.  * @method static integer countByCgroup($val, array $opt=array())
  274.  * @method static integer countByChmod($val, array $opt=array())
  275.  * @method static integer countByNoSearch($val, array $opt=array())
  276.  * @method static integer countByCssClass($val, array $opt=array())
  277.  * @method static integer countBySitemap($val, array $opt=array())
  278.  * @method static integer countByHide($val, array $opt=array())
  279.  * @method static integer countByGuests($val, array $opt=array())
  280.  * @method static integer countByTabindex($val, array $opt=array())
  281.  * @method static integer countByAccesskey($val, array $opt=array())
  282.  * @method static integer countByPublished($val, array $opt=array())
  283.  * @method static integer countByStart($val, array $opt=array())
  284.  * @method static integer countByStop($val, array $opt=array())
  285.  * @method static integer countByEnforceTwoFactor($val, array $opt=array())
  286.  * @method static integer countByTwoFactorJumpTo($val, array $opt=array())
  287.  *
  288.  * @author Leo Feyer <https://github.com/leofeyer>
  289.  */
  290. class PageModel extends Model
  291. {
  292.     /**
  293.      * Table name
  294.      * @var string
  295.      */
  296.     protected static $strTable 'tl_page';
  297.     /**
  298.      * Details loaded
  299.      * @var boolean
  300.      */
  301.     protected $blnDetailsLoaded false;
  302.     public function __set($strKey$varValue)
  303.     {
  304.         // Deprecate setting dynamic page attributes if they are set on the global $objPage
  305.         if (\in_array($strKey, array('pageTitle''description''robots''noSearch'), true) && ($GLOBALS['objPage'] ?? null) === $this)
  306.         {
  307.             trigger_deprecation('contao/core-bundle''4.12'sprintf('Overriding "%s" is deprecated and will not work in Contao 5.0 anymore. Use the ResponseContext instead.'$strKey));
  308.             /** @var ResponseContext|null $responseContext */
  309.             $responseContext System::getContainer()->get(ResponseContextAccessor::class)->getResponseContext();
  310.             if (!$responseContext)
  311.             {
  312.                 parent::__set($strKey$varValue);
  313.                 return;
  314.             }
  315.             if (\in_array($strKey, array('pageTitle''description''robots')) && $responseContext->has(HtmlHeadBag::class))
  316.             {
  317.                 /** @var HtmlHeadBag $htmlHeadBag */
  318.                 $htmlHeadBag $responseContext->get(HtmlHeadBag::class);
  319.                 switch ($strKey)
  320.                 {
  321.                     case 'pageTitle':
  322.                         $htmlHeadBag->setTitle(StringUtil::inputEncodedToPlainText($varValue ?? ''));
  323.                         break;
  324.                     case 'description':
  325.                         $htmlHeadBag->setMetaDescription(StringUtil::inputEncodedToPlainText($varValue ?? ''));
  326.                         break;
  327.                     case 'robots':
  328.                         $htmlHeadBag->setMetaRobots($varValue);
  329.                         break;
  330.                 }
  331.             }
  332.             if ('noSearch' === $strKey && $responseContext->has(JsonLdManager::class))
  333.             {
  334.                 /** @var JsonLdManager $jsonLdManager */
  335.                 $jsonLdManager $responseContext->get(JsonLdManager::class);
  336.                 if ($jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_CONTAO)->has(ContaoPageSchema::class))
  337.                 {
  338.                     /** @var ContaoPageSchema $schema */
  339.                     $schema $jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_CONTAO)->get(ContaoPageSchema::class);
  340.                     $schema->setNoSearch((bool) $varValue);
  341.                 }
  342.             }
  343.         }
  344.         parent::__set($strKey$varValue);
  345.     }
  346.     /**
  347.      * Find a published page by its ID
  348.      *
  349.      * @param integer $intId      The page ID
  350.      * @param array   $arrOptions An optional options array
  351.      *
  352.      * @return PageModel|null The model or null if there is no published page
  353.      */
  354.     public static function findPublishedById($intId, array $arrOptions=array())
  355.     {
  356.         $t = static::$strTable;
  357.         $arrColumns = array("$t.id=?");
  358.         if (!static::isPreviewMode($arrOptions))
  359.         {
  360.             $time Date::floorToMinute();
  361.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  362.         }
  363.         return static::findOneBy($arrColumns$intId$arrOptions);
  364.     }
  365.     /**
  366.      * Find published pages by their PID
  367.      *
  368.      * @param integer $intPid     The parent ID
  369.      * @param array   $arrOptions An optional options array
  370.      *
  371.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  372.      */
  373.     public static function findPublishedByPid($intPid, array $arrOptions=array())
  374.     {
  375.         $t = static::$strTable;
  376.         $arrColumns = array("$t.pid=?");
  377.         if (!static::isPreviewMode($arrOptions))
  378.         {
  379.             $time Date::floorToMinute();
  380.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  381.         }
  382.         return static::findBy($arrColumns$intPid$arrOptions);
  383.     }
  384.     /**
  385.      * Find the first published root page by its host name and language
  386.      *
  387.      * @param string $strHost     The host name
  388.      * @param mixed  $varLanguage An ISO language code or an array of ISO language codes
  389.      * @param array  $arrOptions  An optional options array
  390.      *
  391.      * @return PageModel|null The model or null if there is no matching root page
  392.      *
  393.      * @deprecated Deprecated since Contao 4.7, to be removed in Contao 5.0.
  394.      */
  395.     public static function findFirstPublishedRootByHostAndLanguage($strHost$varLanguage, array $arrOptions=array())
  396.     {
  397.         trigger_deprecation('contao/core-bundle''4.7''Using "Contao\PageModel::findFirstPublishedRootByHostAndLanguage()" has been deprecated and will no longer work Contao 5.0.');
  398.         $t = static::$strTable;
  399.         $objDatabase Database::getInstance();
  400.         if (\is_array($varLanguage))
  401.         {
  402.             $arrColumns = array("$t.type='root' AND ($t.dns=? OR $t.dns='')");
  403.             if (!empty($varLanguage))
  404.             {
  405.                 $arrColumns[] = "($t.language IN('" implode("','"$varLanguage) . "') OR $t.fallback='1')";
  406.             }
  407.             else
  408.             {
  409.                 $arrColumns[] = "$t.fallback='1'";
  410.             }
  411.             if (!isset($arrOptions['order']))
  412.             {
  413.                 $arrOptions['order'] = "$t.dns DESC" . (!empty($varLanguage) ? ", " $objDatabase->findInSet("$t.language"array_reverse($varLanguage)) . " DESC" "") . ", $t.sorting";
  414.             }
  415.             if (!static::isPreviewMode($arrOptions))
  416.             {
  417.                 $time Date::floorToMinute();
  418.                 $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  419.             }
  420.             return static::findOneBy($arrColumns$strHost$arrOptions);
  421.         }
  422.         $arrColumns = array("$t.type='root' AND ($t.dns=? OR $t.dns='') AND ($t.language=? OR $t.fallback='1')");
  423.         $arrValues = array($strHost$varLanguage);
  424.         if (!isset($arrOptions['order']))
  425.         {
  426.             $arrOptions['order'] = "$t.dns DESC, $t.fallback";
  427.         }
  428.         if (!static::isPreviewMode($arrOptions))
  429.         {
  430.             $time Date::floorToMinute();
  431.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  432.         }
  433.         return static::findOneBy($arrColumns$arrValues$arrOptions);
  434.     }
  435.     /**
  436.      * Find the first published page by its parent ID
  437.      *
  438.      * @param integer $intPid     The parent page's ID
  439.      * @param array   $arrOptions An optional options array
  440.      *
  441.      * @return PageModel|null The model or null if there is no published page
  442.      */
  443.     public static function findFirstPublishedByPid($intPid, array $arrOptions=array())
  444.     {
  445.         $t = static::$strTable;
  446.         $arrColumns = array("$t.pid=? AND $t.type!='root' AND $t.type!='error_401' AND $t.type!='error_403' AND $t.type!='error_404'");
  447.         if (!static::isPreviewMode($arrOptions))
  448.         {
  449.             $time Date::floorToMinute();
  450.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  451.         }
  452.         if (!isset($arrOptions['order']))
  453.         {
  454.             $arrOptions['order'] = "$t.sorting";
  455.         }
  456.         return static::findOneBy($arrColumns$intPid$arrOptions);
  457.     }
  458.     /**
  459.      * Find the first published regular page by its parent ID
  460.      *
  461.      * @param integer $intPid     The parent page's ID
  462.      * @param array   $arrOptions An optional options array
  463.      *
  464.      * @return PageModel|null The model or null if there is no published regular page
  465.      */
  466.     public static function findFirstPublishedRegularByPid($intPid, array $arrOptions=array())
  467.     {
  468.         $t = static::$strTable;
  469.         $arrColumns = array("$t.pid=? AND $t.type='regular'");
  470.         if (!static::isPreviewMode($arrOptions))
  471.         {
  472.             $time Date::floorToMinute();
  473.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  474.         }
  475.         if (!isset($arrOptions['order']))
  476.         {
  477.             $arrOptions['order'] = "$t.sorting";
  478.         }
  479.         return static::findOneBy($arrColumns$intPid$arrOptions);
  480.     }
  481.     /**
  482.      * Find an error 401 page by its parent ID
  483.      *
  484.      * @param integer $intPid     The parent page's ID
  485.      * @param array   $arrOptions An optional options array
  486.      *
  487.      * @return PageModel|null The model or null if there is no 401 page
  488.      */
  489.     public static function find401ByPid($intPid, array $arrOptions=array())
  490.     {
  491.         $t = static::$strTable;
  492.         $arrColumns = array("$t.pid=? AND $t.type='error_401'");
  493.         if (!static::isPreviewMode($arrOptions))
  494.         {
  495.             $time Date::floorToMinute();
  496.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  497.         }
  498.         if (!isset($arrOptions['order']))
  499.         {
  500.             $arrOptions['order'] = "$t.sorting";
  501.         }
  502.         return static::findOneBy($arrColumns$intPid$arrOptions);
  503.     }
  504.     /**
  505.      * Find an error 403 page by its parent ID
  506.      *
  507.      * @param integer $intPid     The parent page's ID
  508.      * @param array   $arrOptions An optional options array
  509.      *
  510.      * @return PageModel|null The model or null if there is no 403 page
  511.      */
  512.     public static function find403ByPid($intPid, array $arrOptions=array())
  513.     {
  514.         $t = static::$strTable;
  515.         $arrColumns = array("$t.pid=? AND $t.type='error_403'");
  516.         if (!static::isPreviewMode($arrOptions))
  517.         {
  518.             $time Date::floorToMinute();
  519.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  520.         }
  521.         if (!isset($arrOptions['order']))
  522.         {
  523.             $arrOptions['order'] = "$t.sorting";
  524.         }
  525.         return static::findOneBy($arrColumns$intPid$arrOptions);
  526.     }
  527.     /**
  528.      * Find an error 404 page by its parent ID
  529.      *
  530.      * @param integer $intPid     The parent page's ID
  531.      * @param array   $arrOptions An optional options array
  532.      *
  533.      * @return PageModel|null The model or null if there is no 404 page
  534.      */
  535.     public static function find404ByPid($intPid, array $arrOptions=array())
  536.     {
  537.         $t = static::$strTable;
  538.         $arrColumns = array("$t.pid=? AND $t.type='error_404'");
  539.         if (!static::isPreviewMode($arrOptions))
  540.         {
  541.             $time Date::floorToMinute();
  542.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  543.         }
  544.         if (!isset($arrOptions['order']))
  545.         {
  546.             $arrOptions['order'] = "$t.sorting";
  547.         }
  548.         return static::findOneBy($arrColumns$intPid$arrOptions);
  549.     }
  550.     /**
  551.      * Find pages matching a list of possible alias names
  552.      *
  553.      * @param array $arrAliases An array of possible alias names
  554.      * @param array $arrOptions An optional options array
  555.      *
  556.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  557.      */
  558.     public static function findByAliases($arrAliases, array $arrOptions=array())
  559.     {
  560.         if (empty($arrAliases) || !\is_array($arrAliases))
  561.         {
  562.             return null;
  563.         }
  564.         // Remove everything that is not an alias
  565.         $arrAliases array_filter(array_map(static function ($v) { return preg_match('/^[\w\/.-]+$/u'$v) ? $v null; }, $arrAliases));
  566.         // Return if nothing is left
  567.         if (empty($arrAliases))
  568.         {
  569.             return null;
  570.         }
  571.         $t = static::$strTable;
  572.         $arrColumns = array("$t.alias IN('" implode("','"array_filter($arrAliases)) . "')");
  573.         // Check the publication status (see #4652)
  574.         if (!static::isPreviewMode($arrOptions))
  575.         {
  576.             $time Date::floorToMinute();
  577.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  578.         }
  579.         if (!isset($arrOptions['order']))
  580.         {
  581.             $arrOptions['order'] = Database::getInstance()->findInSet("$t.alias"$arrAliases);
  582.         }
  583.         return static::findBy($arrColumnsnull$arrOptions);
  584.     }
  585.     /**
  586.      * Find published pages by their ID or aliases
  587.      *
  588.      * @param mixed $varId      The numeric ID or the alias name
  589.      * @param array $arrOptions An optional options array
  590.      *
  591.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  592.      */
  593.     public static function findPublishedByIdOrAlias($varId, array $arrOptions=array())
  594.     {
  595.         $t = static::$strTable;
  596.         $arrColumns = !preg_match('/^[1-9]\d*$/'$varId) ? array("BINARY $t.alias=?") : array("$t.id=?");
  597.         if (!static::isPreviewMode($arrOptions))
  598.         {
  599.             $time Date::floorToMinute();
  600.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  601.         }
  602.         return static::findBy($arrColumns$varId$arrOptions);
  603.     }
  604.     /**
  605.      * Find all published subpages by their parent ID and exclude pages only visible for guests
  606.      *
  607.      * @param integer $intPid        The parent page's ID
  608.      * @param boolean $blnShowHidden If true, hidden pages will be included
  609.      * @param boolean $blnIsSitemap  If true, the sitemap settings apply
  610.      *
  611.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  612.      *
  613.      * @deprecated Deprecated since Contao 4.9, to be removed in Contao 5.0;
  614.      *             use PageModel::findPublishedByPid() instead and filter the guests pages yourself
  615.      */
  616.     public static function findPublishedSubpagesWithoutGuestsByPid($intPid$blnShowHidden=false$blnIsSitemap=false)
  617.     {
  618.         @trigger_error('Using PageModel::findPublishedSubpagesWithoutGuestsByPid() has been deprecated and will no longer work Contao 5.0. Use PageModel::findPublishedByPid() instead and filter the guests pages yourself.'E_USER_DEPRECATED);
  619.         $time Date::floorToMinute();
  620.         $tokenChecker System::getContainer()->get('contao.security.token_checker');
  621.         $blnFeUserLoggedIn $tokenChecker->hasFrontendUser();
  622.         $blnBeUserLoggedIn $tokenChecker->hasBackendUser() && $tokenChecker->isPreviewMode();
  623.         $objSubpages Database::getInstance()->prepare("SELECT p1.*, (SELECT COUNT(*) FROM tl_page p2 WHERE p2.pid=p1.id AND p2.type!='root' AND p2.type!='error_401' AND p2.type!='error_403' AND p2.type!='error_404'" . (!$blnShowHidden ? ($blnIsSitemap " AND (p2.hide='' OR sitemap='map_always')" " AND p2.hide=''") : "") . ($blnFeUserLoggedIn " AND p2.guests=''" "") . (!$blnBeUserLoggedIn " AND p2.published='1' AND (p2.start='' OR p2.start<='$time') AND (p2.stop='' OR p2.stop>'$time')" "") . ") AS subpages FROM tl_page p1 WHERE p1.pid=? AND p1.type!='root' AND p1.type!='error_401' AND p1.type!='error_403' AND p1.type!='error_404'" . (!$blnShowHidden ? ($blnIsSitemap " AND (p1.hide='' OR sitemap='map_always')" " AND p1.hide=''") : "") . ($blnFeUserLoggedIn " AND p1.guests=''" "") . (!$blnBeUserLoggedIn " AND p1.published='1' AND (p1.start='' OR p1.start<='$time') AND (p1.stop='' OR p1.stop>'$time')" "") . " ORDER BY p1.sorting")
  624.                                               ->execute($intPid);
  625.         if ($objSubpages->numRows 1)
  626.         {
  627.             return null;
  628.         }
  629.         return static::createCollectionFromDbResult($objSubpages'tl_page');
  630.     }
  631.     /**
  632.      * Find all published regular pages by their IDs
  633.      *
  634.      * @param array $arrIds     An array of page IDs
  635.      * @param array $arrOptions An optional options array
  636.      *
  637.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  638.      */
  639.     public static function findPublishedRegularByIds($arrIds, array $arrOptions=array())
  640.     {
  641.         if (empty($arrIds) || !\is_array($arrIds))
  642.         {
  643.             return null;
  644.         }
  645.         $t = static::$strTable;
  646.         $arrColumns = array("$t.id IN(" implode(','array_map('\intval'$arrIds)) . ") AND $t.type!='error_401' AND $t.type!='error_403' AND $t.type!='error_404'");
  647.         if (empty($arrOptions['includeRoot']))
  648.         {
  649.             $arrColumns[] = "$t.type!='root'";
  650.         }
  651.         if (!static::isPreviewMode($arrOptions))
  652.         {
  653.             $time Date::floorToMinute();
  654.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  655.         }
  656.         if (!isset($arrOptions['order']))
  657.         {
  658.             $arrOptions['order'] = Database::getInstance()->findInSet("$t.id"$arrIds);
  659.         }
  660.         return static::findBy($arrColumnsnull$arrOptions);
  661.     }
  662.     /**
  663.      * Find all published regular pages by their IDs and exclude pages only visible for guests
  664.      *
  665.      * @param array $arrIds     An array of page IDs
  666.      * @param array $arrOptions An optional options array
  667.      *
  668.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  669.      *
  670.      * @deprecated Deprecated since Contao 4.12, to be removed in Contao 5;
  671.      *             use PageModel::findPublishedRegularByIds() instead.
  672.      */
  673.     public static function findPublishedRegularWithoutGuestsByIds($arrIds, array $arrOptions=array())
  674.     {
  675.         trigger_deprecation('contao/core-bundle''4.12''Using PageModel::findPublishedRegularWithoutGuestsByIds() has been deprecated and will no longer work in Contao 5.0. Use PageModel::findPublishedRegularByIds() instead.');
  676.         if (empty($arrIds) || !\is_array($arrIds))
  677.         {
  678.             return null;
  679.         }
  680.         $t = static::$strTable;
  681.         $arrColumns = array("$t.id IN(" implode(','array_map('\intval'$arrIds)) . ") AND $t.type!='error_401' AND $t.type!='error_403' AND $t.type!='error_404'");
  682.         if (empty($arrOptions['includeRoot']))
  683.         {
  684.             $arrColumns[] = "$t.type!='root'";
  685.         }
  686.         if (System::getContainer()->get('contao.security.token_checker')->hasFrontendUser())
  687.         {
  688.             $arrColumns[] = "$t.guests=''";
  689.         }
  690.         if (!static::isPreviewMode($arrOptions))
  691.         {
  692.             $time Date::floorToMinute();
  693.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  694.         }
  695.         if (!isset($arrOptions['order']))
  696.         {
  697.             $arrOptions['order'] = Database::getInstance()->findInSet("$t.id"$arrIds);
  698.         }
  699.         return static::findBy($arrColumnsnull$arrOptions);
  700.     }
  701.     /**
  702.      * Find all published regular pages by their parent IDs
  703.      *
  704.      * @param integer $intPid     The parent page's ID
  705.      * @param array   $arrOptions An optional options array
  706.      *
  707.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  708.      */
  709.     public static function findPublishedRegularByPid($intPid, array $arrOptions=array())
  710.     {
  711.         $t = static::$strTable;
  712.         $arrColumns = array("$t.pid=? AND $t.type!='root' AND $t.type!='error_401' AND $t.type!='error_403' AND $t.type!='error_404'");
  713.         if (!static::isPreviewMode($arrOptions))
  714.         {
  715.             $time Date::floorToMinute();
  716.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  717.         }
  718.         if (!isset($arrOptions['order']))
  719.         {
  720.             $arrOptions['order'] = "$t.sorting";
  721.         }
  722.         return static::findBy($arrColumns$intPid$arrOptions);
  723.     }
  724.     /**
  725.      * Find all published regular pages by their parent IDs and exclude pages only visible for guests
  726.      *
  727.      * @param integer $intPid     The parent page's ID
  728.      * @param array   $arrOptions An optional options array
  729.      *
  730.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no pages
  731.      *
  732.      * @deprecated Deprecated since Contao 4.12, to be removed in Contao 5;
  733.      *             use PageModel::findPublishedRegularByPid() instead.
  734.      */
  735.     public static function findPublishedRegularWithoutGuestsByPid($intPid, array $arrOptions=array())
  736.     {
  737.         trigger_deprecation('contao/core-bundle''4.12''Using PageModel::findPublishedRegularWithoutGuestsByPid() has been deprecated and will no longer work in Contao 5.0. Use PageModel::findPublishedRegularByPid() instead.');
  738.         $t = static::$strTable;
  739.         $arrColumns = array("$t.pid=? AND $t.type!='root' AND $t.type!='error_401' AND $t.type!='error_403' AND $t.type!='error_404'");
  740.         if (System::getContainer()->get('contao.security.token_checker')->hasFrontendUser())
  741.         {
  742.             $arrColumns[] = "$t.guests=''";
  743.         }
  744.         if (!static::isPreviewMode($arrOptions))
  745.         {
  746.             $time Date::floorToMinute();
  747.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  748.         }
  749.         if (!isset($arrOptions['order']))
  750.         {
  751.             $arrOptions['order'] = "$t.sorting";
  752.         }
  753.         return static::findBy($arrColumns$intPid$arrOptions);
  754.     }
  755.     /**
  756.      * Find the language fallback page by hostname
  757.      *
  758.      * @param string $strHost    The hostname
  759.      * @param array  $arrOptions An optional options array
  760.      *
  761.      * @return PageModel|Model|null The model or null if there is not fallback page
  762.      */
  763.     public static function findPublishedFallbackByHostname($strHost, array $arrOptions=array())
  764.     {
  765.         // Try to load from the registry (see #8544)
  766.         if (empty($arrOptions))
  767.         {
  768.             $objModel Registry::getInstance()->fetch(static::$strTable$strHost'contao.dns-fallback');
  769.             if ($objModel !== null)
  770.             {
  771.                 return $objModel;
  772.             }
  773.         }
  774.         $t = static::$strTable;
  775.         $arrColumns = array("$t.dns=? AND $t.fallback='1'");
  776.         if (isset($arrOptions['fallbackToEmpty']) && $arrOptions['fallbackToEmpty'] === true)
  777.         {
  778.             $arrColumns = array("($t.dns=? OR $t.dns='') AND $t.fallback='1'");
  779.         }
  780.         if (!static::isPreviewMode($arrOptions))
  781.         {
  782.             $time Date::floorToMinute();
  783.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  784.         }
  785.         return static::findOneBy($arrColumns$strHost$arrOptions);
  786.     }
  787.     /**
  788.      * Finds the published root pages
  789.      *
  790.      * @param array $arrOptions An optional options array
  791.      *
  792.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no parent pages
  793.      */
  794.     public static function findPublishedRootPages(array $arrOptions=array())
  795.     {
  796.         $t = static::$strTable;
  797.         $arrColumns = array("$t.type='root'");
  798.         if (isset($arrOptions['dns']))
  799.         {
  800.             $arrColumns = array("$t.type='root' AND $t.dns=?");
  801.         }
  802.         if (!static::isPreviewMode($arrOptions))
  803.         {
  804.             $time Date::floorToMinute();
  805.             $arrColumns[] = "$t.published='1' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time')";
  806.         }
  807.         return static::findBy($arrColumns$arrOptions['dns'] ?? null$arrOptions);
  808.     }
  809.     /**
  810.      * Find the parent pages of a page
  811.      *
  812.      * @param integer $intId The page's ID
  813.      *
  814.      * @return Collection|PageModel[]|PageModel|null A collection of models or null if there are no parent pages
  815.      */
  816.     public static function findParentsById($intId)
  817.     {
  818.         $arrModels = array();
  819.         while ($intId && ($objPage = static::findByPk($intId)) !== null)
  820.         {
  821.             $intId $objPage->pid;
  822.             $arrModels[] = $objPage;
  823.         }
  824.         if (empty($arrModels))
  825.         {
  826.             return null;
  827.         }
  828.         return static::createCollection($arrModels'tl_page');
  829.     }
  830.     /**
  831.      * Find the first active page by its member groups
  832.      *
  833.      * @param array $arrIds An array of member group IDs
  834.      *
  835.      * @return PageModel|null The model or null if there is no matching member group
  836.      */
  837.     public static function findFirstActiveByMemberGroups($arrIds)
  838.     {
  839.         if (empty($arrIds) || !\is_array($arrIds))
  840.         {
  841.             return null;
  842.         }
  843.         $time Date::floorToMinute();
  844.         $objDatabase Database::getInstance();
  845.         $arrIds array_map('\intval'$arrIds);
  846.         $objResult $objDatabase->prepare("SELECT p.* FROM tl_member_group g LEFT JOIN tl_page p ON g.jumpTo=p.id WHERE g.id IN(" implode(','$arrIds) . ") AND g.jumpTo>0 AND g.redirect='1' AND g.disable!='1' AND (g.start='' OR g.start<='$time') AND (g.stop='' OR g.stop>'$time') AND p.published='1' AND (p.start='' OR p.start<='$time') AND (p.stop='' OR p.stop>'$time') ORDER BY " $objDatabase->findInSet('g.id'$arrIds))
  847.                                  ->limit(1)
  848.                                  ->execute();
  849.         if ($objResult->numRows 1)
  850.         {
  851.             return null;
  852.         }
  853.         $objRegistry Registry::getInstance();
  854.         /** @var PageModel|Model $objPage */
  855.         if ($objPage $objRegistry->fetch('tl_page'$objResult->id))
  856.         {
  857.             return $objPage;
  858.         }
  859.         return new static($objResult);
  860.     }
  861.     /**
  862.      * Find a page by its ID and return it with the inherited details
  863.      *
  864.      * @param integer|string $intId The page's ID
  865.      *
  866.      * @return PageModel|null The model or null if there is no matching page
  867.      */
  868.     public static function findWithDetails($intId)
  869.     {
  870.         $objPage = static::findByPk($intId);
  871.         if ($objPage === null)
  872.         {
  873.             return null;
  874.         }
  875.         return $objPage->loadDetails();
  876.     }
  877.     /**
  878.      * Register the contao.dns-fallback alias when the model is attached to the registry
  879.      *
  880.      * @param Registry $registry The model registry
  881.      */
  882.     public function onRegister(Registry $registry)
  883.     {
  884.         parent::onRegister($registry);
  885.         // Register this model as being the fallback page for a given dns
  886.         if ($this->fallback && $this->type == 'root' && !$registry->isRegisteredAlias($this'contao.dns-fallback'$this->dns))
  887.         {
  888.             $registry->registerAlias($this'contao.dns-fallback'$this->dns);
  889.         }
  890.     }
  891.     /**
  892.      * Unregister the contao.dns-fallback alias when the model is detached from the registry
  893.      *
  894.      * @param Registry $registry The model registry
  895.      */
  896.     public function onUnregister(Registry $registry)
  897.     {
  898.         parent::onUnregister($registry);
  899.         // Unregister the fallback page
  900.         if ($this->fallback && $this->type == 'root' && $registry->isRegisteredAlias($this'contao.dns-fallback'$this->dns))
  901.         {
  902.             $registry->unregisterAlias($this'contao.dns-fallback'$this->dns);
  903.         }
  904.     }
  905.     /**
  906.      * Get the details of a page including inherited parameters
  907.      *
  908.      * @return PageModel The page model
  909.      *
  910.      * @throws NoRootPageFoundException If no root page is found
  911.      */
  912.     public function loadDetails()
  913.     {
  914.         // Loaded already
  915.         if ($this->blnDetailsLoaded)
  916.         {
  917.             return $this;
  918.         }
  919.         // Set some default values
  920.         $this->protected = (bool) $this->protected;
  921.         $this->groups $this->protected StringUtil::deserialize($this->groupstrue) : array();
  922.         $this->layout $this->includeLayout $this->layout false;
  923.         $this->cache $this->includeCache $this->cache false;
  924.         $this->alwaysLoadFromCache $this->includeCache $this->alwaysLoadFromCache false;
  925.         $this->clientCache $this->includeCache $this->clientCache false;
  926.         $pid $this->pid;
  927.         $type $this->type;
  928.         $alias $this->alias;
  929.         $name $this->title;
  930.         $title $this->pageTitle ?: $this->title;
  931.         $folderUrl '';
  932.         $palias '';
  933.         $pname '';
  934.         $ptitle '';
  935.         $trail = array($this->id$pid);
  936.         $time time();
  937.         // Inherit the settings
  938.         if ($this->type == 'root')
  939.         {
  940.             $objParentPage $this// see #4610
  941.         }
  942.         else
  943.         {
  944.             // Load all parent pages
  945.             $objParentPage self::findParentsById($pid);
  946.             if ($objParentPage !== null)
  947.             {
  948.                 while ($pid && $type != 'root' && $objParentPage->next())
  949.                 {
  950.                     $pid $objParentPage->pid;
  951.                     $type $objParentPage->type;
  952.                     // Parent title
  953.                     if (!$ptitle)
  954.                     {
  955.                         $palias $objParentPage->alias;
  956.                         $pname $objParentPage->title;
  957.                         $ptitle $objParentPage->pageTitle ?: $objParentPage->title;
  958.                     }
  959.                     // Page title
  960.                     if ($type != 'root')
  961.                     {
  962.                         // If $folderUrl is not yet set, use the alias of the first
  963.                         // parent page if it is not a root page (see #2129)
  964.                         if (!$folderUrl && $objParentPage->alias)
  965.                         {
  966.                             $folderUrl $objParentPage->alias '/';
  967.                         }
  968.                         $alias $objParentPage->alias;
  969.                         $name $objParentPage->title;
  970.                         $title $objParentPage->pageTitle ?: $objParentPage->title;
  971.                         $trail[] = $objParentPage->pid;
  972.                     }
  973.                     // Cache
  974.                     if ($objParentPage->includeCache)
  975.                     {
  976.                         $this->cache $this->cache !== false $this->cache $objParentPage->cache;
  977.                         $this->alwaysLoadFromCache $this->alwaysLoadFromCache !== false $this->alwaysLoadFromCache $objParentPage->alwaysLoadFromCache;
  978.                         $this->clientCache $this->clientCache !== false $this->clientCache $objParentPage->clientCache;
  979.                     }
  980.                     // Layout
  981.                     if ($objParentPage->includeLayout && $this->layout === false)
  982.                     {
  983.                         $this->layout $objParentPage->layout;
  984.                     }
  985.                     // Protection
  986.                     if ($objParentPage->protected && $this->protected === false)
  987.                     {
  988.                         $this->protected true;
  989.                         $this->groups StringUtil::deserialize($objParentPage->groupstrue);
  990.                     }
  991.                 }
  992.             }
  993.             // Set the titles
  994.             $this->mainAlias $alias;
  995.             $this->mainTitle $name;
  996.             $this->mainPageTitle $title;
  997.             $this->parentAlias $palias;
  998.             $this->parentTitle $pname;
  999.             $this->parentPageTitle $ptitle;
  1000.             $this->folderUrl $folderUrl;
  1001.         }
  1002.         // Set the root ID and title
  1003.         if ($objParentPage !== null && $objParentPage->type == 'root')
  1004.         {
  1005.             $this->rootId $objParentPage->id;
  1006.             $this->rootAlias $objParentPage->alias;
  1007.             $this->rootTitle $objParentPage->title;
  1008.             $this->rootPageTitle $objParentPage->pageTitle ?: $objParentPage->title;
  1009.             $this->rootSorting $objParentPage->sorting;
  1010.             $this->domain $objParentPage->dns;
  1011.             $this->rootLanguage $objParentPage->language;
  1012.             $this->language $objParentPage->language;
  1013.             $this->staticFiles $objParentPage->staticFiles;
  1014.             $this->staticPlugins $objParentPage->staticPlugins;
  1015.             $this->dateFormat $objParentPage->dateFormat;
  1016.             $this->timeFormat $objParentPage->timeFormat;
  1017.             $this->datimFormat $objParentPage->datimFormat;
  1018.             $this->validAliasCharacters $objParentPage->validAliasCharacters;
  1019.             $this->urlPrefix $objParentPage->urlPrefix;
  1020.             $this->urlSuffix $objParentPage->urlSuffix;
  1021.             $this->disableLanguageRedirect $objParentPage->disableLanguageRedirect;
  1022.             $this->adminEmail $objParentPage->adminEmail;
  1023.             $this->enforceTwoFactor $objParentPage->enforceTwoFactor;
  1024.             $this->twoFactorJumpTo $objParentPage->twoFactorJumpTo;
  1025.             $this->useFolderUrl $objParentPage->useFolderUrl;
  1026.             $this->mailerTransport $objParentPage->mailerTransport;
  1027.             $this->useAutoItem Config::get('useAutoItem');
  1028.             // Store whether the root page has been published
  1029.             $this->rootIsPublic = ($objParentPage->published && (!$objParentPage->start || $objParentPage->start <= $time) && (!$objParentPage->stop || $objParentPage->stop $time));
  1030.             $this->rootIsFallback = (bool) $objParentPage->fallback;
  1031.             $this->rootUseSSL $objParentPage->useSSL;
  1032.             $this->rootFallbackLanguage $objParentPage->language;
  1033.             // Store the fallback language (see #6874)
  1034.             if (!$objParentPage->fallback)
  1035.             {
  1036.                 $this->rootFallbackLanguage null;
  1037.                 $objFallback = static::findPublishedFallbackByHostname($objParentPage->dns);
  1038.                 if ($objFallback !== null)
  1039.                 {
  1040.                     $this->rootFallbackLanguage $objFallback->language;
  1041.                 }
  1042.             }
  1043.             if (System::getContainer()->getParameter('contao.legacy_routing'))
  1044.             {
  1045.                 $this->urlPrefix System::getContainer()->getParameter('contao.prepend_locale') ? LocaleUtil::formatAsLanguageTag($objParentPage->language) : '';
  1046.                 $this->urlSuffix System::getContainer()->getParameter('contao.url_suffix');
  1047.             }
  1048.         }
  1049.         // No root page found
  1050.         elseif (TL_MODE == 'FE' && $this->type != 'root')
  1051.         {
  1052.             System::log('Page ID "' $this->id '" does not belong to a root page'__METHOD__TL_ERROR);
  1053.             throw new NoRootPageFoundException('No root page found');
  1054.         }
  1055.         $this->trail array_reverse($trail);
  1056.         // Use the global date format if none is set (see #6104)
  1057.         if (!$this->dateFormat)
  1058.         {
  1059.             $this->dateFormat Config::get('dateFormat');
  1060.         }
  1061.         if (!$this->timeFormat)
  1062.         {
  1063.             $this->timeFormat Config::get('timeFormat');
  1064.         }
  1065.         if (!$this->datimFormat)
  1066.         {
  1067.             $this->datimFormat Config::get('datimFormat');
  1068.         }
  1069.         $this->isPublic = ($this->published && (!$this->start || $this->start <= $time) && (!$this->stop || $this->stop $time));
  1070.         // HOOK: add custom logic
  1071.         if (!empty($GLOBALS['TL_HOOKS']['loadPageDetails']) && \is_array($GLOBALS['TL_HOOKS']['loadPageDetails']))
  1072.         {
  1073.             $parentModels = array();
  1074.             if ($objParentPage instanceof Collection)
  1075.             {
  1076.                 $parentModels $objParentPage->getModels();
  1077.             }
  1078.             foreach ($GLOBALS['TL_HOOKS']['loadPageDetails'] as $callback)
  1079.             {
  1080.                 System::importStatic($callback[0])->{$callback[1]}($parentModels$this);
  1081.             }
  1082.         }
  1083.         // Prevent saving (see #6506 and #7199)
  1084.         $this->preventSaving();
  1085.         $this->blnDetailsLoaded true;
  1086.         return $this;
  1087.     }
  1088.     /**
  1089.      * Generate a front end URL
  1090.      *
  1091.      * @param string $strParams    An optional string of URL parameters
  1092.      * @param string $strForceLang Force a certain language
  1093.      *
  1094.      * @return string An URL that can be used in the front end
  1095.      */
  1096.     public function getFrontendUrl($strParams=null$strForceLang=null)
  1097.     {
  1098.         $page $this;
  1099.         $page->loadDetails();
  1100.         if ($strForceLang !== null)
  1101.         {
  1102.             trigger_deprecation('contao/core-bundle''4.0''Using "Contao\PageModel::getFrontendUrl()" with $strForceLang has been deprecated and will no longer work in Contao 5.0.');
  1103.             $strForceLang LocaleUtil::formatAsLanguageTag($strForceLang);
  1104.             $page $page->cloneOriginal();
  1105.             $page->preventSaving(false);
  1106.             $page->language $strForceLang;
  1107.             $page->rootLanguage $strForceLang;
  1108.             if (System::getContainer()->getParameter('contao.legacy_routing'))
  1109.             {
  1110.                 $page->urlPrefix System::getContainer()->getParameter('contao.prepend_locale') ? $strForceLang '';
  1111.             }
  1112.         }
  1113.         $objRouter System::getContainer()->get('router');
  1114.         $strUrl $objRouter->generate(RouteObjectInterface::OBJECT_BASED_ROUTE_NAME, array(RouteObjectInterface::CONTENT_OBJECT => $this'parameters' => $strParams));
  1115.         // Make the URL relative to the base path
  1116.         if (=== strncmp($strUrl'/'1) && !== strncmp($strUrl'//'2))
  1117.         {
  1118.             $strUrl substr($strUrl, \strlen(Environment::get('path')) + 1);
  1119.         }
  1120.         return $this->applyLegacyLogic($strUrl$strParams);
  1121.     }
  1122.     /**
  1123.      * Generate an absolute URL depending on the current rewriteURL setting
  1124.      *
  1125.      * @param string $strParams An optional string of URL parameters
  1126.      *
  1127.      * @return string An absolute URL that can be used in the front end
  1128.      */
  1129.     public function getAbsoluteUrl($strParams=null)
  1130.     {
  1131.         $this->loadDetails();
  1132.         $objRouter System::getContainer()->get('router');
  1133.         $strUrl $objRouter->generate(RouteObjectInterface::OBJECT_BASED_ROUTE_NAME, array(RouteObjectInterface::CONTENT_OBJECT => $this'parameters' => $strParams), UrlGeneratorInterface::ABSOLUTE_URL);
  1134.         return $this->applyLegacyLogic($strUrl$strParams);
  1135.     }
  1136.     /**
  1137.      * Generate the front end preview URL
  1138.      *
  1139.      * @param string $strParams An optional string of URL parameters
  1140.      *
  1141.      * @return string The front end preview URL
  1142.      */
  1143.     public function getPreviewUrl($strParams=null)
  1144.     {
  1145.         $container System::getContainer();
  1146.         if (!$previewScript $container->getParameter('contao.preview_script'))
  1147.         {
  1148.             return $this->getAbsoluteUrl($strParams);
  1149.         }
  1150.         $this->loadDetails();
  1151.         $context $container->get('router')->getContext();
  1152.         $baseUrl $context->getBaseUrl();
  1153.         // Add the preview script
  1154.         $context->setBaseUrl($previewScript);
  1155.         $objRouter System::getContainer()->get('router');
  1156.         $strUrl $objRouter->generate(RouteObjectInterface::OBJECT_BASED_ROUTE_NAME, array(RouteObjectInterface::CONTENT_OBJECT => $this'parameters' => $strParams), UrlGeneratorInterface::ABSOLUTE_URL);
  1157.         $context->setBaseUrl($baseUrl);
  1158.         return $this->applyLegacyLogic($strUrl$strParams);
  1159.     }
  1160.     /**
  1161.      * Return the slug options
  1162.      *
  1163.      * @return array The slug options
  1164.      */
  1165.     public function getSlugOptions()
  1166.     {
  1167.         $slugOptions = array('locale'=>$this->language);
  1168.         if ($this->validAliasCharacters)
  1169.         {
  1170.             $slugOptions['validChars'] = $this->validAliasCharacters;
  1171.         }
  1172.         return $slugOptions;
  1173.     }
  1174.     /**
  1175.      * Modifies a URL from the URL generator.
  1176.      *
  1177.      * @param string      $strUrl
  1178.      * @param string|null $strParams
  1179.      *
  1180.      * @return string
  1181.      */
  1182.     private function applyLegacyLogic($strUrl$strParams)
  1183.     {
  1184.         // Decode sprintf placeholders
  1185.         if ($strParams !== null && strpos($strParams'%') !== false)
  1186.         {
  1187.             trigger_deprecation('contao/core-bundle''4.2''Using sprintf placeholders in URLs has been deprecated and will no longer work in Contao 5.0.');
  1188.             $arrMatches = array();
  1189.             preg_match_all('/%([sducoxXbgGeEfF])/'$strParams$arrMatches);
  1190.             foreach (array_unique($arrMatches[1]) as $v)
  1191.             {
  1192.                 $strUrl str_replace('%25' $v'%' $v$strUrl);
  1193.             }
  1194.         }
  1195.         // HOOK: add custom logic
  1196.         if (isset($GLOBALS['TL_HOOKS']['generateFrontendUrl']) && \is_array($GLOBALS['TL_HOOKS']['generateFrontendUrl']))
  1197.         {
  1198.             trigger_deprecation('contao/core-bundle''4.0''Using the "generateFrontendUrl" hook has been deprecated and will no longer work in Contao 5.0.');
  1199.             foreach ($GLOBALS['TL_HOOKS']['generateFrontendUrl'] as $callback)
  1200.             {
  1201.                 $strUrl System::importStatic($callback[0])->{$callback[1]}($this->row(), $strParams ?? ''$strUrl);
  1202.             }
  1203.             return $strUrl;
  1204.         }
  1205.         return $strUrl;
  1206.     }
  1207. }
  1208. class_alias(PageModel::class, 'PageModel');