diff --git a/core-bundle/src/EventListener/DataContainer/AddCanonicalFieldsListener.php b/core-bundle/src/EventListener/DataContainer/AddCanonicalFieldsListener.php new file mode 100644 index 00000000000..7a3ddf234bc --- /dev/null +++ b/core-bundle/src/EventListener/DataContainer/AddCanonicalFieldsListener.php @@ -0,0 +1,49 @@ +contaoFramework = $contaoFramework; + } + + public function __invoke(DataContainer $dc): void + { + /** @var PageModel $pageModel */ + $pageModel = $this->contaoFramework->getAdapter(PageModel::class); + $page = $pageModel->findWithDetails($dc->id); + + if ('regular' !== $page->type || !$page->enableCanonical) { + return; + } + + PaletteManipulator::create() + ->addField('canonicalLink', 'serpPreview') + ->addField('canonicalKeepParams', 'serpPreview') + ->applyToPalette('regular', 'tl_page') + ; + } +} diff --git a/core-bundle/src/Resources/config/listener.yml b/core-bundle/src/Resources/config/listener.yml index 63394218041..b6a81a51837 100644 --- a/core-bundle/src/Resources/config/listener.yml +++ b/core-bundle/src/Resources/config/listener.yml @@ -19,6 +19,10 @@ services: tags: - { name: kernel.event_listener } + Contao\CoreBundle\EventListener\DataContainer\AddCanonicalFieldsListener: + arguments: + - '@contao.framework' + Contao\CoreBundle\EventListener\DataContainer\ContentCompositionListener: arguments: - '@contao.framework' diff --git a/core-bundle/src/Resources/contao/dca/tl_page.php b/core-bundle/src/Resources/contao/dca/tl_page.php index 3feabb985cc..d6c896e5157 100644 --- a/core-bundle/src/Resources/contao/dca/tl_page.php +++ b/core-bundle/src/Resources/contao/dca/tl_page.php @@ -180,8 +180,8 @@ 'regular' => '{title_legend},title,alias,type;{meta_legend},pageTitle,robots,description,serpPreview;{protected_legend:hide},protected;{layout_legend:hide},includeLayout;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass,sitemap,hide,noSearch,guests,requireItem;{tabnav_legend:hide},tabindex,accesskey;{publish_legend},published,start,stop', 'forward' => '{title_legend},title,alias,type;{meta_legend},pageTitle,robots;{redirect_legend},jumpTo,redirect;{protected_legend:hide},protected;{layout_legend:hide},includeLayout;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass,sitemap,hide,guests;{tabnav_legend:hide},tabindex,accesskey;{publish_legend},published,start,stop', 'redirect' => '{title_legend},title,alias,type;{meta_legend},pageTitle,robots;{redirect_legend},redirect,url,target;{protected_legend:hide},protected;{layout_legend:hide},includeLayout;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass,sitemap,hide,guests;{tabnav_legend:hide},tabindex,accesskey;{publish_legend},published,start,stop', - 'root' => '{title_legend},title,alias,type;{meta_legend},pageTitle;{url_legend},dns,useSSL,urlPrefix,urlSuffix,validAliasCharacters,useFolderUrl;{language_legend},language,fallback,disableLanguageRedirect;{global_legend:hide},adminEmail,mailerTransport,dateFormat,timeFormat,datimFormat,staticFiles,staticPlugins;{protected_legend:hide},protected;{layout_legend},includeLayout;{twoFactor_legend:hide},enforceTwoFactor;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{publish_legend},published,start,stop', - 'rootfallback' => '{title_legend},title,alias,type;{meta_legend},pageTitle;{url_legend},dns,useSSL,urlPrefix,urlSuffix,validAliasCharacters,useFolderUrl;{language_legend},language,fallback,disableLanguageRedirect;{website_legend:hide},favicon,robotsTxt;{global_legend:hide},adminEmail,mailerTransport,dateFormat,timeFormat,datimFormat,staticFiles,staticPlugins;{protected_legend:hide},protected;{layout_legend},includeLayout;{twoFactor_legend:hide},enforceTwoFactor;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{publish_legend},published,start,stop', + 'root' => '{title_legend},title,alias,type;{meta_legend},pageTitle,enableCanonical;{url_legend},dns,useSSL,urlPrefix,urlSuffix,validAliasCharacters,useFolderUrl;{language_legend},language,fallback,disableLanguageRedirect;{global_legend:hide},adminEmail,mailerTransport,dateFormat,timeFormat,datimFormat,staticFiles,staticPlugins;{protected_legend:hide},protected;{layout_legend},includeLayout;{twoFactor_legend:hide},enforceTwoFactor;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{publish_legend},published,start,stop', + 'rootfallback' => '{title_legend},title,alias,type;{meta_legend},pageTitle,enableCanonical;{url_legend},dns,useSSL,urlPrefix,urlSuffix,validAliasCharacters,useFolderUrl;{language_legend},language,fallback,disableLanguageRedirect;{website_legend:hide},favicon,robotsTxt;{global_legend:hide},adminEmail,mailerTransport,dateFormat,timeFormat,datimFormat,staticFiles,staticPlugins;{protected_legend:hide},protected;{layout_legend},includeLayout;{twoFactor_legend:hide},enforceTwoFactor;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{publish_legend},published,start,stop', 'logout' => '{title_legend},title,alias,type;{forward_legend},jumpTo,redirectBack;{protected_legend:hide},protected;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass,sitemap,hide;{tabnav_legend:hide},tabindex,accesskey;{publish_legend},published,start,stop', 'error_401' => '{title_legend},title,alias,type;{meta_legend},pageTitle,robots,description;{forward_legend},autoforward;{layout_legend:hide},includeLayout;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass;{publish_legend},published,start,stop', 'error_403' => '{title_legend},title,alias,type;{meta_legend},pageTitle,robots,description;{forward_legend},autoforward;{layout_legend:hide},includeLayout;{cache_legend:hide},includeCache;{chmod_legend:hide},includeChmod;{expert_legend:hide},cssClass;{publish_legend},published,start,stop', @@ -253,6 +253,14 @@ 'eval' => array('maxlength'=>255, 'tl_class'=>'w50'), 'sql' => "varchar(255) NOT NULL default ''" ), + 'enableCanonical' => array + ( + 'exclude' => true, + 'inputType' => 'checkbox', + 'default' => true, + 'eval' => array('tl_class'=>'w50 m12'), + 'sql' => "char(1) NOT NULL default ''" + ), 'language' => array ( 'exclude' => true, @@ -299,6 +307,21 @@ static function ($value) 'eval' => array('url_callback'=>array('tl_page', 'getSerpUrl'), 'title_tag_callback'=>array('tl_page', 'getTitleTag'), 'titleFields'=>array('pageTitle', 'title')), 'sql' => null ), + 'canonicalLink' => array + ( + 'exclude' => true, + 'search' => true, + 'inputType' => 'text', + 'eval' => array('rgxp'=>'url', 'decodeEntities'=>true, 'maxlength'=>255, 'dcaPicker'=>true, 'tl_class'=>'w50'), + 'sql' => "varchar(255) NOT NULL default ''" + ), + 'canonicalKeepParams' => array + ( + 'exclude' => true, + 'inputType' => 'text', + 'eval' => array('decodeEntities'=>true, 'maxlength'=>255, 'tl_class'=>'w50'), + 'sql' => "varchar(255) NOT NULL default ''" + ), 'redirect' => array ( 'exclude' => true, diff --git a/core-bundle/src/Resources/contao/languages/en/tl_page.xlf b/core-bundle/src/Resources/contao/languages/en/tl_page.xlf index 2928729e2c3..70feaa833df 100644 --- a/core-bundle/src/Resources/contao/languages/en/tl_page.xlf +++ b/core-bundle/src/Resources/contao/languages/en/tl_page.xlf @@ -26,6 +26,24 @@ Please enter the page title. + + Enable rel="canonical" support + + + You can disable built-in rel="canonical" support and take control over it yourself. + + + Manual rel="canonical" selection + + + You can manually select the desired target page for rel="canonical" support. + + + Query parameters to keep for rel="canonical" + + + By default, Contao points to itself for rel="canonical". You may add a comma-separated list of query parameters which should be kept. Use "*" as wildcard. + Language diff --git a/core-bundle/src/Resources/contao/models/PageModel.php b/core-bundle/src/Resources/contao/models/PageModel.php index cce68af136f..f529acdbe1b 100644 --- a/core-bundle/src/Resources/contao/models/PageModel.php +++ b/core-bundle/src/Resources/contao/models/PageModel.php @@ -84,6 +84,9 @@ * @property string|integer $stop * @property string|boolean $enforceTwoFactor * @property string|integer $twoFactorJumpTo + * @property string|integer $enableCanonical + * @property string $canonicalLink + * @property string $canonicalKeepParams * * @property array $trail * @property string $mainAlias @@ -172,6 +175,9 @@ * @method static PageModel|null findOneByStop($val, array $opt=array()) * @method static PageModel|null findOneByEnforceTwoFactor($val, array $opt=array()) * @method static PageModel|null findOneByTwoFactorJumpTo($val, array $opt=array()) + * @method static PageModel|null findOneByEnableCanonical($val, array $opt=array()) + * @method static PageModel|null findOneByCanonicalLink($val, array $opt=array()) + * @method static PageModel|null findOneByCanonicalKeepParams($val, array $opt=array()) * * @method static Collection|PageModel[]|PageModel|null findByPid($val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findBySorting($val, array $opt=array()) @@ -228,6 +234,9 @@ * @method static Collection|PageModel[]|PageModel|null findByStop($val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findByEnforceTwoFactor($val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findByTwoFactorJumpTo($val, array $opt=array()) + * @method static Collection|PageModel[]|PageModel|null findByEnableCanonical($val, array $opt=array()) + * @method static Collection|PageModel[]|PageModel|null findByCanonicalLink($val, array $opt=array()) + * @method static Collection|PageModel[]|PageModel|null findByCanonicalKeepParams($val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findMultipleByIds($val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findBy($col, $val, array $opt=array()) * @method static Collection|PageModel[]|PageModel|null findAll(array $opt=array()) @@ -288,6 +297,9 @@ * @method static integer countByStop($val, array $opt=array()) * @method static integer countByEnforceTwoFactor($val, array $opt=array()) * @method static integer countByTwoFactorJumpTo($val, array $opt=array()) + * @method static integer countByEnableCanonical($val, array $opt=array()) + * @method static integer countByCanonicalLink($val, array $opt=array()) + * @method static integer countByCanonicalKeepParams($val, array $opt=array()) * * @author Leo Feyer */ @@ -1157,6 +1169,7 @@ public function loadDetails() $this->twoFactorJumpTo = $objParentPage->twoFactorJumpTo; $this->useFolderUrl = $objParentPage->useFolderUrl; $this->mailerTransport = $objParentPage->mailerTransport; + $this->enableCanonical = $objParentPage->enableCanonical; $this->useAutoItem = Config::get('useAutoItem'); // Store whether the root page has been published