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

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