first commit
No files matched your search
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\Canonical;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
use TYPO3\CMS\Frontend\Utility\CanonicalizationUtility;
|
||||
|
||||
/**
|
||||
* Class to add the canonical tag to the page
|
||||
*
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class CanonicalGenerator
|
||||
{
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $typoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* @var PageRepository
|
||||
*/
|
||||
protected $pageRepository;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* CanonicalGenerator constructor
|
||||
*
|
||||
* @param TypoScriptFrontendController $typoScriptFrontendController
|
||||
* @param Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function __construct(TypoScriptFrontendController $typoScriptFrontendController = null, Dispatcher $signalSlotDispatcher = null)
|
||||
{
|
||||
if ($typoScriptFrontendController === null) {
|
||||
$typoScriptFrontendController = $this->getTypoScriptFrontendController();
|
||||
}
|
||||
if ($signalSlotDispatcher === null) {
|
||||
$signalSlotDispatcher = GeneralUtility::makeInstance(Dispatcher::class);
|
||||
}
|
||||
$this->typoScriptFrontendController = $typoScriptFrontendController;
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
$this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotException
|
||||
* @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotReturnException
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
$href = '';
|
||||
$this->signalSlotDispatcher->dispatch(self::class, 'beforeGeneratingCanonical', [&$href]);
|
||||
|
||||
if (empty($href) && (int)$this->typoScriptFrontendController->page['no_index'] === 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (empty($href)) {
|
||||
// 1) Check if page show content from other page
|
||||
$href = $this->checkContentFromPid();
|
||||
}
|
||||
if (empty($href)) {
|
||||
// 2) Check if page has canonical URL set
|
||||
$href = $this->checkForCanonicalLink();
|
||||
}
|
||||
if (empty($href)) {
|
||||
// 3) Fallback, create canonical URL
|
||||
$href = $this->checkDefaultCanonical();
|
||||
}
|
||||
|
||||
if (!empty($href)) {
|
||||
$canonical = '<link ' . GeneralUtility::implodeAttributes([
|
||||
'rel' => 'canonical',
|
||||
'href' => $href
|
||||
], true) . '/>' . LF;
|
||||
$this->typoScriptFrontendController->additionalHeaderData[] = $canonical;
|
||||
return $canonical;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function checkForCanonicalLink(): string
|
||||
{
|
||||
if (!empty($this->typoScriptFrontendController->page['canonical_link'])) {
|
||||
return $this->typoScriptFrontendController->cObj->typoLink_URL([
|
||||
'parameter' => $this->typoScriptFrontendController->page['canonical_link'],
|
||||
'forceAbsoluteUrl' => true,
|
||||
]);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function checkContentFromPid(): string
|
||||
{
|
||||
if (!empty($this->typoScriptFrontendController->page['content_from_pid'])) {
|
||||
$parameter = (int)$this->typoScriptFrontendController->page['content_from_pid'];
|
||||
if ($parameter > 0) {
|
||||
$targetPage = $this->pageRepository->getPage($parameter, true);
|
||||
if (!empty($targetPage['canonical_link'])) {
|
||||
$parameter = $targetPage['canonical_link'];
|
||||
}
|
||||
return $this->typoScriptFrontendController->cObj->typoLink_URL([
|
||||
'parameter' => $parameter,
|
||||
'forceAbsoluteUrl' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function checkDefaultCanonical(): string
|
||||
{
|
||||
return $this->typoScriptFrontendController->cObj->typoLink_URL([
|
||||
'parameter' => $this->typoScriptFrontendController->id . ',' . $this->typoScriptFrontendController->type,
|
||||
'forceAbsoluteUrl' => true,
|
||||
'addQueryString' => true,
|
||||
'addQueryString.' => [
|
||||
'method' => 'GET',
|
||||
'exclude' => implode(
|
||||
',',
|
||||
CanonicalizationUtility::getParamsToExcludeForCanonicalizedUrl(
|
||||
(int)$this->typoScriptFrontendController->id,
|
||||
(array)$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalCanonicalizedUrlParameters']
|
||||
)
|
||||
)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TypoScriptFrontendController
|
||||
*/
|
||||
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
|
||||
{
|
||||
return $GLOBALS['TSFE'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\HrefLang;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Site\Entity\Site;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
use TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor;
|
||||
|
||||
/**
|
||||
* Class to add the hreflang tags to the page
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class HrefLangGenerator
|
||||
{
|
||||
/**
|
||||
* The content object renderer
|
||||
*
|
||||
* @var ContentObjectRenderer
|
||||
*/
|
||||
public $cObj;
|
||||
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $typoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* HreflangGenerator constructor
|
||||
*
|
||||
* @param ContentObjectRenderer|null $cObj
|
||||
* @param TypoScriptFrontendController|null $typoScriptFrontendController
|
||||
*/
|
||||
public function __construct(ContentObjectRenderer $cObj = null, TypoScriptFrontendController $typoScriptFrontendController = null, ServerRequestInterface $request = null)
|
||||
{
|
||||
if ($cObj === null) {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
}
|
||||
if ($typoScriptFrontendController === null) {
|
||||
$typoScriptFrontendController = $this->getTypoScriptFrontendController();
|
||||
}
|
||||
if ($request === null) {
|
||||
$request = $this->getRequest();
|
||||
}
|
||||
|
||||
$this->cObj = $cObj;
|
||||
$this->typoScriptFrontendController = $typoScriptFrontendController;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
$hreflangs = [];
|
||||
if ((int)$this->typoScriptFrontendController->page['no_index'] === 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->request->getAttribute('site') instanceof Site) {
|
||||
$languageMenu = GeneralUtility::makeInstance(LanguageMenuProcessor::class);
|
||||
$languages = $languageMenu->process($this->cObj, [], [], []);
|
||||
foreach ($languages['languagemenu'] as $language) {
|
||||
if ($language['available'] === 1 && !empty($language['link'])) {
|
||||
$href = $this->getAbsoluteUrl($language['link']);
|
||||
$hreflangs[] =
|
||||
'<link rel="alternate" hreflang="' . htmlspecialchars($language['hreflang']) . '" href="' . htmlspecialchars($href) . '"/>';
|
||||
}
|
||||
}
|
||||
|
||||
if (count($hreflangs) > 1) {
|
||||
$href = $this->getAbsoluteUrl($languages['languagemenu'][0]['link']);
|
||||
$hreflangs[] =
|
||||
'<link rel="alternate" hreflang="x-default" href="' . htmlspecialchars($href) . '"/>' . LF;
|
||||
|
||||
$this->getTypoScriptFrontendController()->additionalHeaderData[] = implode(LF, $hreflangs);
|
||||
}
|
||||
}
|
||||
|
||||
return implode(LF, $hreflangs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function getAbsoluteUrl(string $url): string
|
||||
{
|
||||
$uri = new Uri($url);
|
||||
if (empty($uri->getHost())) {
|
||||
$url = $this->getSiteLanguage()->getBase()->withPath($uri->getPath());
|
||||
|
||||
if ($uri->getQuery()) {
|
||||
$url = $url->withQuery($uri->getQuery());
|
||||
}
|
||||
}
|
||||
|
||||
return (string)$url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SiteLanguage
|
||||
*/
|
||||
protected function getSiteLanguage(): SiteLanguage
|
||||
{
|
||||
return $this->request->getAttribute('language');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TypoScriptFrontendController
|
||||
*/
|
||||
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
|
||||
{
|
||||
return $GLOBALS['TSFE'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
protected function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\MetaTag;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Resource\ProcessedFile;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Service\ImageService;
|
||||
use TYPO3\CMS\Frontend\Resource\FileCollector;
|
||||
|
||||
/**
|
||||
* Class to add the metatags for the SEO fields in core
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MetaTagGenerator
|
||||
{
|
||||
/**
|
||||
* Generate the meta tags that can be set in backend and add them to frontend by using the MetaTag API
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function generate(array $params)
|
||||
{
|
||||
$metaTagManagerRegistry = GeneralUtility::makeInstance(MetaTagManagerRegistry::class);
|
||||
|
||||
if (!empty($params['page']['description'])) {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('description');
|
||||
$manager->addProperty('description', $params['page']['description']);
|
||||
}
|
||||
|
||||
if (!empty($params['page']['og_title'])) {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('og:title');
|
||||
$manager->addProperty('og:title', $params['page']['og_title']);
|
||||
}
|
||||
|
||||
if (!empty($params['page']['og_description'])) {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('og:description');
|
||||
$manager->addProperty('og:description', $params['page']['og_description']);
|
||||
}
|
||||
|
||||
if (!empty($params['page']['og_image'])) {
|
||||
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
|
||||
$fileCollector->addFilesFromRelation('pages', 'og_image', $params['page']);
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('og:image');
|
||||
|
||||
$ogImages = $this->generateSocialImages($fileCollector->getFiles());
|
||||
foreach ($ogImages as $ogImage) {
|
||||
$subProperties = [];
|
||||
$subProperties['url'] = $ogImage['url'];
|
||||
$subProperties['width'] = $ogImage['width'];
|
||||
$subProperties['height'] = $ogImage['height'];
|
||||
|
||||
if (!empty($ogImage['alternative'])) {
|
||||
$subProperties['alt'] = $ogImage['alternative'];
|
||||
}
|
||||
|
||||
$manager->addProperty(
|
||||
'og:image',
|
||||
$ogImage['url'],
|
||||
$subProperties
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set type of twitter card to summary. This value can be overridden by TypoScript or the MetaTag API by
|
||||
* using the replace option. In v10 this will be a page property
|
||||
*/
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('twitter:card');
|
||||
$manager->addProperty('twitter:card', 'summary');
|
||||
|
||||
if (!empty($params['page']['twitter_title'])) {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('twitter:title');
|
||||
$manager->addProperty('twitter:title', $params['page']['twitter_title']);
|
||||
}
|
||||
|
||||
if (!empty($params['page']['twitter_description'])) {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('twitter:description');
|
||||
$manager->addProperty('twitter:description', $params['page']['twitter_description']);
|
||||
}
|
||||
|
||||
if (!empty($params['page']['twitter_image'])) {
|
||||
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
|
||||
$fileCollector->addFilesFromRelation('pages', 'twitter_image', $params['page']);
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('twitter:image');
|
||||
|
||||
$twitterImages = $this->generateSocialImages($fileCollector->getFiles());
|
||||
foreach ($twitterImages as $twitterImage) {
|
||||
$subProperties = [];
|
||||
|
||||
if (!empty($twitterImage['alternative'])) {
|
||||
$subProperties['alt'] = $twitterImage['alternative'];
|
||||
}
|
||||
|
||||
$manager->addProperty(
|
||||
'twitter:image',
|
||||
$twitterImage['url'],
|
||||
$subProperties
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$noIndex = ((bool)$params['page']['no_index']) ? 'noindex' : 'index';
|
||||
$noFollow = ((bool)$params['page']['no_follow']) ? 'nofollow' : 'follow';
|
||||
|
||||
if ($noIndex === 'noindex' || $noFollow === 'nofollow') {
|
||||
$manager = $metaTagManagerRegistry->getManagerForProperty('robots');
|
||||
$manager->addProperty('robots', implode(',', [$noIndex, $noFollow]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fileReferences
|
||||
* @return array
|
||||
*/
|
||||
protected function generateSocialImages(array $fileReferences): array
|
||||
{
|
||||
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
||||
|
||||
$socialImages = [];
|
||||
|
||||
/** @var FileReference $file */
|
||||
foreach ($fileReferences as $file) {
|
||||
$arguments = $file->getProperties();
|
||||
$cropVariantCollection = CropVariantCollection::create((string)$arguments['crop']);
|
||||
$cropVariant = $arguments['cropVariant'] ?: 'social';
|
||||
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
|
||||
$crop = $cropArea->makeAbsoluteBasedOnFile($file);
|
||||
|
||||
$cropInformation = $crop->asArray();
|
||||
|
||||
$processingConfiguration = [
|
||||
'crop' => $crop
|
||||
];
|
||||
|
||||
$processedImage = $file->getOriginalFile()->process(
|
||||
ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
|
||||
$processingConfiguration
|
||||
);
|
||||
|
||||
$imageUri = $imageService->getImageUri($processedImage, true);
|
||||
|
||||
$socialImages[] = [
|
||||
'url' => $imageUri,
|
||||
'width' => floor($cropInformation['width']),
|
||||
'height' => floor($cropInformation['height']),
|
||||
'alternative' => $arguments['alternative'],
|
||||
];
|
||||
}
|
||||
|
||||
return $socialImages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\MetaTag;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\MetaTag\AbstractMetaTagManager;
|
||||
|
||||
/**
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class OpenGraphMetaTagManager extends AbstractMetaTagManager
|
||||
{
|
||||
/**
|
||||
* The default attribute that defines the name of the property
|
||||
*
|
||||
* This creates tags like <meta property="" /> by default
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultNameAttribute = 'property';
|
||||
|
||||
/**
|
||||
* Array of properties that can be handled by this manager
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $handledProperties = [
|
||||
'og:type' => [],
|
||||
'og:title' => [],
|
||||
'og:description' => [],
|
||||
'og:site_name' => [],
|
||||
'og:url' => [],
|
||||
'og:audio' => [],
|
||||
'og:video' => [],
|
||||
'og:determiner' => [],
|
||||
'og:locale' => [
|
||||
'allowedSubProperties' => [
|
||||
'alternate' => [
|
||||
'allowMultipleOccurrences' => true
|
||||
],
|
||||
]
|
||||
],
|
||||
'og:image' => [
|
||||
'allowMultipleOccurrences' => true,
|
||||
'allowedSubProperties' => [
|
||||
'url' => [],
|
||||
'secure_url' => [],
|
||||
'type' => [],
|
||||
'width' => [],
|
||||
'height' => [],
|
||||
'alt' => [],
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\MetaTag;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\MetaTag\AbstractMetaTagManager;
|
||||
|
||||
/**
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class TwitterCardMetaTagManager extends AbstractMetaTagManager
|
||||
{
|
||||
/**
|
||||
* Array of properties that can be handled by this manager
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $handledProperties = [
|
||||
'twitter:card' => [],
|
||||
'twitter:site' => [
|
||||
'allowedSubProperties' => [
|
||||
'id' => [],
|
||||
]
|
||||
],
|
||||
'twitter:creator' => [
|
||||
'allowedSubProperties' => [
|
||||
'id' => [],
|
||||
]
|
||||
],
|
||||
'twitter:description' => [],
|
||||
'twitter:title' => [],
|
||||
'twitter:image' => [
|
||||
'allowedSubProperties' => [
|
||||
'alt' => [],
|
||||
]
|
||||
],
|
||||
'twitter:player' => [
|
||||
'allowedSubProperties' => [
|
||||
'width' => [],
|
||||
'height' => [],
|
||||
'stream' => [],
|
||||
]
|
||||
],
|
||||
'twitter:app' => [
|
||||
'allowedSubProperties' => [
|
||||
'name:iphone' => [],
|
||||
'id:iphone' => [],
|
||||
'url:iphone' => [],
|
||||
'name:ipad' => [],
|
||||
'id:ipad' => [],
|
||||
'url:ipad' => [],
|
||||
'name:googleplay' => [],
|
||||
'id:googleplay' => [],
|
||||
'url:googleplay' => [],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\PageTitle;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\PageTitle\AbstractPageTitleProvider;
|
||||
|
||||
/**
|
||||
* This class will take care of the seo title that can be set in the backend
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class SeoTitlePageTitleProvider extends AbstractPageTitleProvider
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->title = (string)$GLOBALS['TSFE']->page['seo_title'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Base class for XmlSitemapProviders to extend
|
||||
*/
|
||||
abstract class AbstractXmlSitemapDataProvider implements XmlSitemapDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lastModified;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var ContentObjectRenderer
|
||||
*/
|
||||
protected $cObj;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $numberOfItemsPerPage = 1000;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* AbstractXmlSitemapDataProvider constructor
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param string $key
|
||||
* @param array $config
|
||||
* @param ContentObjectRenderer $cObj
|
||||
*/
|
||||
public function __construct(ServerRequestInterface $request, string $key, array $config = [], ContentObjectRenderer $cObj = null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->config = $config;
|
||||
$this->request = $request;
|
||||
|
||||
if ($cObj === null) {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
}
|
||||
$this->cObj = $cObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNumberOfPages(): int
|
||||
{
|
||||
return (int)ceil(count($this->items) / $this->numberOfItemsPerPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified(): int
|
||||
{
|
||||
$lastMod = 0;
|
||||
foreach ($this->items as $item) {
|
||||
if ((int)$item['lastMod'] > $lastMod) {
|
||||
$lastMod = (int)$item['lastMod'];
|
||||
}
|
||||
}
|
||||
|
||||
return $lastMod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function defineUrl(array $data): array
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getItems(): array
|
||||
{
|
||||
$pageNumber = (int)($this->request->getQueryParams()['page'] ?? 0);
|
||||
$page = $pageNumber > 0 ? $pageNumber : 0;
|
||||
$items = array_slice(
|
||||
$this->items,
|
||||
$page * $this->numberOfItemsPerPage,
|
||||
$this->numberOfItemsPerPage
|
||||
);
|
||||
|
||||
return array_map([$this, 'defineUrl'], $items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
class InvalidConfigurationException extends \TYPO3\CMS\Core\Resource\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
class MissingConfigurationException extends \TYPO3\CMS\Core\Resource\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\LanguageAspect;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryHelper;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
|
||||
/**
|
||||
* Class to generate a XML sitemap for pages
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class PagesXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
|
||||
{
|
||||
public function __construct(ServerRequestInterface $request, string $key, array $config = [], ContentObjectRenderer $cObj = null)
|
||||
{
|
||||
parent::__construct($request, $key, $config, $cObj);
|
||||
|
||||
$this->generateItems();
|
||||
}
|
||||
|
||||
protected function generateItems(): void
|
||||
{
|
||||
$pageRepository = GeneralUtility::makeInstance(PageRepository::class);
|
||||
$pages = $pageRepository->getPagesOverlay($this->getPages());
|
||||
$languageAspect = $this->getCurrentLanguageAspect();
|
||||
foreach ($pages as $page) {
|
||||
if (!$pageRepository->isPageSuitableForLanguage($page, $languageAspect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->items[] = [
|
||||
'uid' => $page['uid'],
|
||||
'lastMod' => (int)($page['SYS_LASTCHANGED'] ?: $page['tstamp'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getPages(): array
|
||||
{
|
||||
if (!empty($this->config['rootPage'])) {
|
||||
$rootPageId = (int)$this->config['rootPage'];
|
||||
} else {
|
||||
$site = $this->request->getAttribute('site');
|
||||
$rootPageId = $site->getRootPageId();
|
||||
}
|
||||
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$treeList = $cObj->getTreeList(-$rootPageId, 99);
|
||||
$treeListArray = GeneralUtility::intExplode(',', $treeList);
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('pages');
|
||||
|
||||
$constraints = [
|
||||
$queryBuilder->expr()->in('uid', $treeListArray)
|
||||
];
|
||||
|
||||
if (!empty($this->config['additionalWhere'])) {
|
||||
$constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->config['additionalWhere']);
|
||||
}
|
||||
|
||||
if (!empty($this->config['excludedDoktypes'])) {
|
||||
$excludedDoktypes = GeneralUtility::intExplode(',', $this->config['excludedDoktypes']);
|
||||
if (!empty($excludedDoktypes)) {
|
||||
$constraints[] = $queryBuilder->expr()->notIn('doktype', implode(',', $excludedDoktypes));
|
||||
}
|
||||
}
|
||||
$pages = $queryBuilder->select('*')
|
||||
->from('pages')
|
||||
->where(...$constraints)
|
||||
->orderBy('uid', 'ASC')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageAspect
|
||||
*/
|
||||
protected function getCurrentLanguageAspect(): LanguageAspect
|
||||
{
|
||||
return GeneralUtility::makeInstance(Context::class)->getAspect('language');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function defineUrl(array $data): array
|
||||
{
|
||||
$typoLinkConfig = [
|
||||
'parameter' => $data['uid'],
|
||||
'forceAbsoluteUrl' => 1,
|
||||
];
|
||||
|
||||
$data['loc'] = $this->cObj->typoLink_URL($typoLinkConfig);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryHelper;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Seo\XmlSitemap\Exception\MissingConfigurationException;
|
||||
|
||||
/**
|
||||
* XmlSiteDataProvider will provide information for the XML sitemap for a specific database table
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class RecordsXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
|
||||
{
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $key
|
||||
* @param array $config
|
||||
* @param ContentObjectRenderer|null $cObj
|
||||
* @throws MissingConfigurationException
|
||||
*/
|
||||
public function __construct(ServerRequestInterface $request, string $key, array $config = [], ContentObjectRenderer $cObj = null)
|
||||
{
|
||||
parent::__construct($request, $key, $config, $cObj);
|
||||
|
||||
$this->generateItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MissingConfigurationException
|
||||
*/
|
||||
public function generateItems(): void
|
||||
{
|
||||
$table = $this->config['table'];
|
||||
|
||||
if (empty($table)) {
|
||||
throw new MissingConfigurationException(
|
||||
'No configuration found for sitemap ' . $this->getKey(),
|
||||
1535576053
|
||||
);
|
||||
}
|
||||
|
||||
$pids = !empty($this->config['pid']) ? GeneralUtility::intExplode(',', $this->config['pid']) : [];
|
||||
$lastModifiedField = $this->config['lastModifiedField'] ?? 'tstamp';
|
||||
$sortField = $this->config['sortField'] ?? 'sorting';
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$constraints = [];
|
||||
if (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])) {
|
||||
$constraints[] = $queryBuilder->expr()->in(
|
||||
$GLOBALS['TCA'][$table]['ctrl']['languageField'],
|
||||
[
|
||||
-1, // All languages
|
||||
$this->getLanguageId() // Current language
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($pids)) {
|
||||
$recursiveLevel = isset($this->config['recursive']) ? (int)$this->config['recursive'] : 0;
|
||||
if ($recursiveLevel) {
|
||||
$newList = [];
|
||||
foreach ($pids as $pid) {
|
||||
$list = $this->cObj->getTreeList($pid, $recursiveLevel);
|
||||
if ($list) {
|
||||
$newList = array_merge($newList, explode(',', $list));
|
||||
}
|
||||
}
|
||||
$pids = array_merge($pids, $newList);
|
||||
}
|
||||
|
||||
$constraints[] = $queryBuilder->expr()->in('pid', $pids);
|
||||
}
|
||||
|
||||
if (!empty($this->config['additionalWhere'])) {
|
||||
$constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->config['additionalWhere']);
|
||||
}
|
||||
|
||||
$queryBuilder->select('*')
|
||||
->from($table);
|
||||
|
||||
if (!empty($constraints)) {
|
||||
$queryBuilder->where(
|
||||
...$constraints
|
||||
);
|
||||
}
|
||||
|
||||
$rows = $queryBuilder->orderBy($sortField)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$this->items[] = [
|
||||
'data' => $row,
|
||||
'lastMod' => (int)$row[$lastModifiedField]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function defineUrl(array $data): array
|
||||
{
|
||||
$pageId = $this->config['url']['pageId'] ?? $GLOBALS['TSFE']->id;
|
||||
$additionalParams = [];
|
||||
|
||||
$additionalParams = $this->getUrlFieldParameterMap($additionalParams, $data['data']);
|
||||
$additionalParams = $this->getUrlAdditionalParams($additionalParams);
|
||||
|
||||
$additionalParamsString = http_build_query(
|
||||
$additionalParams,
|
||||
'',
|
||||
'&',
|
||||
PHP_QUERY_RFC3986
|
||||
);
|
||||
|
||||
$typoLinkConfig = [
|
||||
'parameter' => $pageId,
|
||||
'additionalParams' => $additionalParamsString ? '&' . $additionalParamsString : '',
|
||||
'forceAbsoluteUrl' => 1,
|
||||
'useCacheHash' => $this->config['url']['useCacheHash'] ?? 0
|
||||
];
|
||||
|
||||
$data['loc'] = $this->cObj->typoLink_URL($typoLinkConfig);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $additionalParams
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function getUrlFieldParameterMap(array $additionalParams, array $data): array
|
||||
{
|
||||
if (!empty($this->config['url']['fieldToParameterMap']) &&
|
||||
\is_array($this->config['url']['fieldToParameterMap'])) {
|
||||
foreach ($this->config['url']['fieldToParameterMap'] as $field => $urlPart) {
|
||||
$additionalParams[$urlPart] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $additionalParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $additionalParams
|
||||
* @return array
|
||||
*/
|
||||
protected function getUrlAdditionalParams(array $additionalParams): array
|
||||
{
|
||||
if (!empty($this->config['url']['additionalGetParameters']) &&
|
||||
is_array($this->config['url']['additionalGetParameters'])) {
|
||||
foreach ($this->config['url']['additionalGetParameters'] as $extension => $extensionConfig) {
|
||||
foreach ($extensionConfig as $key => $value) {
|
||||
$additionalParams[$extension . '[' . $key . ']'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $additionalParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
|
||||
*/
|
||||
protected function getLanguageId(): int
|
||||
{
|
||||
$context = GeneralUtility::makeInstance(Context::class);
|
||||
return (int)$context->getPropertyFromAspect('language', 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Interface for XmlSitemapDataProviders containing the methods that are called by the XmlSitemapRenderer
|
||||
*/
|
||||
interface XmlSitemapDataProviderInterface
|
||||
{
|
||||
public function __construct(ServerRequestInterface $request, string $name, array $config = [], ContentObjectRenderer $cObj = null);
|
||||
public function getKey(): string;
|
||||
public function getItems(): array;
|
||||
public function getLastModified(): int;
|
||||
public function getNumberOfPages(): int;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Seo\XmlSitemap;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
use TYPO3\CMS\Seo\XmlSitemap\Exception\InvalidConfigurationException;
|
||||
|
||||
/**
|
||||
* Class to render the XML Sitemap to be used as a UserFunction
|
||||
* @internal this class is not part of TYPO3's Core API.
|
||||
*/
|
||||
class XmlSitemapRenderer
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Fluid\View\StandaloneView
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->configuration = $this->getConfiguration();
|
||||
$this->view = $this->getStandaloneView();
|
||||
$this->view->assign(
|
||||
'xslFile',
|
||||
PathUtility::stripPathSitePrefix(
|
||||
ExtensionManagementUtility::extPath('seo', 'Resources/Public/CSS/Sitemap.xsl')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws InvalidConfigurationException
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
// Inject request from globals until request will be available to cObj
|
||||
$request = $GLOBALS['TYPO3_REQUEST'];
|
||||
$this->view->assign('type', $GLOBALS['TSFE']->type);
|
||||
if (!empty($sitemap = $request->getQueryParams()['sitemap'])) {
|
||||
return $this->renderSitemap($request, $sitemap);
|
||||
}
|
||||
|
||||
return $this->renderIndex($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
protected function renderIndex(ServerRequestInterface $request): string
|
||||
{
|
||||
$sitemaps = [];
|
||||
foreach ($this->configuration['config']['xmlSitemap']['sitemaps'] ?? [] as $sitemap => $config) {
|
||||
if (class_exists($config['provider']) &&
|
||||
is_subclass_of($config['provider'], XmlSitemapDataProviderInterface::class)) {
|
||||
/** @var XmlSitemapDataProviderInterface $provider */
|
||||
$provider = GeneralUtility::makeInstance(
|
||||
$config['provider'],
|
||||
$request,
|
||||
$sitemap,
|
||||
(array)$config['config']
|
||||
);
|
||||
|
||||
$pages = $provider->getNumberOfPages();
|
||||
|
||||
for ($page = 0; $page < $pages; $page++) {
|
||||
$sitemaps[] = [
|
||||
'key' => $sitemap,
|
||||
'page' => $page,
|
||||
'lastMod' => $provider->getLastModified()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->assign('sitemaps', $sitemaps);
|
||||
$this->view->setTemplate('Index');
|
||||
|
||||
return $this->view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param string $sitemap
|
||||
* @return string
|
||||
* @throws \TYPO3\CMS\Seo\XmlSitemap\Exception\InvalidConfigurationException
|
||||
*/
|
||||
protected function renderSitemap(ServerRequestInterface $request, string $sitemap): string
|
||||
{
|
||||
if (!empty($sitemapConfig = $this->configuration['config']['xmlSitemap']['sitemaps'][$sitemap])) {
|
||||
if (class_exists($sitemapConfig['provider']) &&
|
||||
is_subclass_of($sitemapConfig['provider'], XmlSitemapDataProviderInterface::class)) {
|
||||
/** @var XmlSitemapDataProviderInterface $provider */
|
||||
$provider = GeneralUtility::makeInstance(
|
||||
$sitemapConfig['provider'],
|
||||
$request,
|
||||
$sitemap,
|
||||
(array)$sitemapConfig['config']
|
||||
);
|
||||
|
||||
$items = $provider->getItems();
|
||||
|
||||
$template = $sitemapConfig['config']['template'] ?: 'Sitemap';
|
||||
$this->view->setTemplate($template);
|
||||
$this->view->assign('items', $items);
|
||||
|
||||
return $this->view->render();
|
||||
}
|
||||
throw new InvalidConfigurationException('No valid provider set for ' . $sitemap, 1535578522);
|
||||
}
|
||||
|
||||
throw new InvalidConfigurationException('No valid configuration found for sitemap ' . $sitemap, 1535578569);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Fluid\View\StandaloneView
|
||||
*/
|
||||
protected function getStandaloneView(): StandaloneView
|
||||
{
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setTemplateRootPaths($this->configuration['view']['templateRootPaths']);
|
||||
$view->setLayoutRootPaths($this->configuration['view']['layoutRootPaths']);
|
||||
$view->setPartialRootPaths($this->configuration['view']['partialRootPaths']);
|
||||
$view->setFormat('xml');
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whole typoscript array
|
||||
* @return array
|
||||
*/
|
||||
private function getConfiguration(): array
|
||||
{
|
||||
$configurationManager = GeneralUtility::makeInstance(ObjectManager::class)
|
||||
->get(ConfigurationManagerInterface::class);
|
||||
|
||||
return $configurationManager->getConfiguration(
|
||||
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
|
||||
'seo'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
defined('TYPO3_MODE') or die();
|
||||
|
||||
$openGraphCropConfiguration = [
|
||||
'config' => [
|
||||
'cropVariants' => [
|
||||
'default' => [
|
||||
'disabled' => true,
|
||||
],
|
||||
'social' => [
|
||||
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.social',
|
||||
'coverAreas' => [],
|
||||
'cropArea' => [
|
||||
'x' => '0.0',
|
||||
'y' => '0.0',
|
||||
'width' => '1.0',
|
||||
'height' => '1.0'
|
||||
],
|
||||
'allowedAspectRatios' => [
|
||||
'1.91:1' => [
|
||||
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.191_1',
|
||||
'value' => 1.91 / 1
|
||||
],
|
||||
'NaN' => [
|
||||
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free',
|
||||
'value' => 0.0
|
||||
],
|
||||
],
|
||||
'selectedRatio' => '1.91:1',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$tca = [
|
||||
'palettes' => [
|
||||
'seo' => [
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.palettes.seo',
|
||||
'showitem' => 'seo_title;LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.seo_title',
|
||||
],
|
||||
'robots' => [
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.palettes.robots',
|
||||
'showitem' => 'no_index;LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.no_index_formlabel, no_follow;LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.no_follow_formlabel',
|
||||
],
|
||||
'canonical' => [
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.palettes.canonical',
|
||||
'showitem' => 'canonical_link',
|
||||
],
|
||||
'opengraph' => [
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.palettes.opengraph',
|
||||
'showitem' => 'og_title, --linebreak--, og_description, --linebreak--, og_image',
|
||||
],
|
||||
'twittercards' => [
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.palettes.twittercards',
|
||||
'showitem' => 'twitter_title, --linebreak--, twitter_description, --linebreak--, twitter_image',
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'seo_title' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'prefixLangTitle',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.seo_title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 40,
|
||||
'max' => 255,
|
||||
'eval' => 'trim'
|
||||
]
|
||||
],
|
||||
'no_index' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'exclude',
|
||||
'onChange' => 'reload',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.no_index_formlabel',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
'items' => [
|
||||
[
|
||||
'0' => '',
|
||||
'1' => '',
|
||||
'invertStateDisplay' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'no_follow' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'exclude',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.no_follow_formlabel',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
'items' => [
|
||||
[
|
||||
'0' => '',
|
||||
'1' => '',
|
||||
'invertStateDisplay' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'canonical_link' => [
|
||||
'exclude' => true,
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.canonical_link',
|
||||
'displayCond' => 'FIELD:no_index:=:0',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'renderType' => 'inputLink',
|
||||
'size' => 50,
|
||||
'max' => 1024,
|
||||
'eval' => 'trim',
|
||||
'fieldControl' => [
|
||||
'linkPopup' => [
|
||||
'options' => [
|
||||
'title' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.canonical_link',
|
||||
'blindLinkFields' => 'class,target,title',
|
||||
'blindLinkOptions' => 'mail,folder,file'
|
||||
],
|
||||
],
|
||||
],
|
||||
'softref' => 'typolink'
|
||||
]
|
||||
],
|
||||
'og_title' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'prefixLangTitle',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.og_title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 40,
|
||||
'max' => 255,
|
||||
'eval' => 'trim'
|
||||
]
|
||||
],
|
||||
'og_description' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'prefixLangTitle',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.og_description',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'cols' => 40,
|
||||
'rows' => 3
|
||||
]
|
||||
],
|
||||
'og_image' => [
|
||||
'exclude' => true,
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.og_image',
|
||||
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
|
||||
'og_image',
|
||||
[
|
||||
// Use the imageoverlayPalette instead of the basicoverlayPalette
|
||||
'overrideChildTca' => [
|
||||
'types' => [
|
||||
'0' => [
|
||||
'showitem' => '
|
||||
--palette--;;imageoverlayPalette,
|
||||
--palette--;;filePalette'
|
||||
],
|
||||
\TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [
|
||||
'showitem' => '
|
||||
--palette--;;imageoverlayPalette,
|
||||
--palette--;;filePalette'
|
||||
]
|
||||
],
|
||||
'columns' => [
|
||||
'crop' => $openGraphCropConfiguration
|
||||
]
|
||||
],
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
]
|
||||
],
|
||||
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
|
||||
)
|
||||
],
|
||||
'twitter_title' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'prefixLangTitle',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.twitter_title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 40,
|
||||
'max' => 255,
|
||||
'eval' => 'trim'
|
||||
]
|
||||
],
|
||||
'twitter_description' => [
|
||||
'exclude' => true,
|
||||
'l10n_mode' => 'prefixLangTitle',
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.twitter_description',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'cols' => 40,
|
||||
'rows' => 3
|
||||
]
|
||||
],
|
||||
'twitter_image' => [
|
||||
'exclude' => true,
|
||||
'label' => 'LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.twitter_image',
|
||||
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
|
||||
'twitter_image',
|
||||
[
|
||||
// Use the imageoverlayPalette instead of the basicoverlayPalette
|
||||
'overrideChildTca' => [
|
||||
'types' => [
|
||||
'0' => [
|
||||
'showitem' => '
|
||||
--palette--;;imageoverlayPalette,
|
||||
--palette--;;filePalette'
|
||||
],
|
||||
\TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [
|
||||
'showitem' => '
|
||||
--palette--;;imageoverlayPalette,
|
||||
--palette--;;filePalette'
|
||||
]
|
||||
],
|
||||
'columns' => [
|
||||
'crop' => $openGraphCropConfiguration
|
||||
]
|
||||
],
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
]
|
||||
],
|
||||
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
|
||||
)
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$GLOBALS['TCA']['pages'] = array_replace_recursive($GLOBALS['TCA']['pages'], $tca);
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
|
||||
'pages',
|
||||
'
|
||||
--div--;LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.tabs.seo,
|
||||
--palette--;;seo,
|
||||
--palette--;;robots,
|
||||
--palette--;;canonical,
|
||||
--div--;LLL:EXT:seo/Resources/Private/Language/locallang_tca.xlf:pages.tabs.socialmedia,
|
||||
--palette--;;opengraph,
|
||||
--palette--;;twittercards',
|
||||
(string)\TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_DEFAULT,
|
||||
'after:title'
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
defined('TYPO3_MODE') or die();
|
||||
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
|
||||
'seo',
|
||||
'Configuration/TypoScript/XmlSitemap',
|
||||
'XML Sitemap'
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
plugin.tx_seo {
|
||||
view {
|
||||
# cat=plugin.tx_seo/file; type=string; label=Path to template root (FE)
|
||||
templateRootPath = EXT:seo/Resources/Private/Templates/
|
||||
# cat=plugin.tx_seo/file; type=string; label=Path to template partials (FE)
|
||||
partialRootPath = EXT:seo/Resources/Private/Partials/
|
||||
# cat=plugin.tx_seo/file; type=string; label=Path to template layouts (FE)
|
||||
layoutRootPath = EXT:seo/Resources/Private/Layouts/
|
||||
}
|
||||
|
||||
settings {
|
||||
xmlSitemap {
|
||||
sitemaps {
|
||||
pages {
|
||||
excludedDoktypes = 3, 4, 6, 7, 199, 254, 255
|
||||
additionalWhere = no_index = 0 AND canonical_link = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
seo_sitemap = PAGE
|
||||
seo_sitemap {
|
||||
typeNum = 1533906435
|
||||
|
||||
config {
|
||||
cache_period = 900
|
||||
disableAllHeaderCode = 1
|
||||
admPanel = 0
|
||||
removeDefaultJS = 1
|
||||
removeDefaultCss = 1
|
||||
removePageCss = 1
|
||||
additionalHeaders {
|
||||
10.header = Content-Type:application/xml;charset=utf-8
|
||||
20.header = X-Robots-Tag:noindex
|
||||
}
|
||||
}
|
||||
|
||||
10 = USER
|
||||
10.userFunc = TYPO3\CMS\Seo\XmlSitemap\XmlSitemapRenderer->render
|
||||
}
|
||||
|
||||
plugin.tx_seo {
|
||||
view {
|
||||
templateRootPaths {
|
||||
0 = EXT:seo/Resources/Private/Templates/XmlSitemap
|
||||
10 = {$plugin.tx_seo.view.templateRootPath}
|
||||
}
|
||||
partialRootPaths {
|
||||
0 = EXT:seo/Resources/Private/Partials/XmlSitemap
|
||||
10 = {$plugin.tx_seo.view.partialRootPath}
|
||||
}
|
||||
layoutRootPaths {
|
||||
0 = EXT:seo/Resources/Private/Layouts/XmlSitemap
|
||||
10 = {$plugin.tx_seo.view.layoutRootPath}
|
||||
}
|
||||
}
|
||||
|
||||
config {
|
||||
xmlSitemap {
|
||||
sitemaps {
|
||||
pages {
|
||||
provider = TYPO3\CMS\Seo\XmlSitemap\PagesXmlSitemapDataProvider
|
||||
config {
|
||||
excludedDoktypes = {$plugin.tx_seo.settings.xmlSitemap.sitemaps.pages.excludedDoktypes}
|
||||
additionalWhere = {$plugin.tx_seo.settings.xmlSitemap.sitemaps.pages.additionalWhere}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
.. _configuration:
|
||||
|
||||
=============
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Target group: **Developers, Integrators**
|
||||
|
||||
|
||||
TypoScript Settings
|
||||
===================
|
||||
|
||||
There are a couple of TypoScript settings that can influence the output regarding search engine optimization.
|
||||
|
||||
* `config.pageTitleFirst <https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#pagetitlefirst>`__
|
||||
* `config.pageTitleSeparator <https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#pagetitleseparator>`__
|
||||
* `config.pageTitleProviders <https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#pagetitleproviders>`__
|
||||
* `config.noPageTitle <https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Setup/Config/Index.html#nopagetitle>`__
|
||||
|
||||
Site configuration
|
||||
==================
|
||||
|
||||
From version 9 of TYPO3, the configuration of sites is done with the Site Management module. As the settings for
|
||||
your websites are important for SEO purposes as well, please make sure you check the following fields.
|
||||
|
||||
.. figure:: ../Images/site.png
|
||||
:class: with-shadow
|
||||
:alt: example site
|
||||
|
||||
Example site
|
||||
|
||||
To get more in depth information about the site handling please refer to the :ref:`t3coreapi:sitehandling` docs.
|
||||
|
||||
Domains
|
||||
-------
|
||||
|
||||
Please ensure, that you have configured your sites so that they all have an entry point. This is used for
|
||||
generating the canonical tags, for example.
|
||||
|
||||
.. warning::
|
||||
|
||||
Please be aware that for SEO purposes it is best practise to use a fully qualified domain (for example: https://www.example.com).
|
||||
Therefor we don't support the SEO enhancements in TYPO3 without a full domain. It might work, but it is not officially
|
||||
supported.
|
||||
|
||||
Language
|
||||
--------
|
||||
|
||||
Ensure, that you setup the languages correctly. All languages should have the right information in the :guilabel:`Locale`
|
||||
and :guilabel:`Language Tag` field. When set correctly, TYPO3 will automatically connect your page in the different languages
|
||||
for search engines. This it to ensure that the search engine knows which page to show when someone is searching in a
|
||||
specific language.
|
||||
|
||||
.. hint::
|
||||
|
||||
Even if you have only one language, make sure your :guilabel:`Locale` and :guilabel:`Language Tag` fields are set correctly.
|
||||
Giving wrong information to search engines will not help you to rank higher.
|
||||
|
||||
See :ref:`t3coreapi:sitehandling-addingLanguages` for more details.
|
||||
|
||||
Error pages
|
||||
-----------
|
||||
|
||||
Although TYPO3 will respond with a HTTP status code 404 (Not found) when a page is not found, it is best practise to
|
||||
have a proper message telling the user that the page they requested is not available and to guide them to another
|
||||
page or for example to a search function of your website.
|
||||
|
||||
See :ref:`t3coreapi:sitehandling-errorHandling` for more details.
|
||||
|
||||
robots.txt
|
||||
----------
|
||||
|
||||
The robots.txt is a powerful tool and should be used with care. It will deny or allow search engines to access your pages.
|
||||
By blocking access to your pages, search engines won't crawl these pages. You should make sure that this will not
|
||||
prevent the search engines from finding important pages.
|
||||
|
||||
It is best practise to keep your robots.txt as clean as possible. An example of a minimal version of your robots.txt:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
# This space intentionally left blank. Only add entries when you know how powerful the robots.txt is.
|
||||
User-agent: *
|
||||
|
||||
On :ref:`t3coreapi:sitehandling-staticRoutes` you can find more details on how to create a static route that will show
|
||||
this information when visiting https://www.example.com/robots.txt.
|
||||
|
||||
When you want to disallow specific URLs, you can use the :ref:`index-page` option in the backend or set the robot HTTP
|
||||
header `X-Robots-tag` manually.
|
||||
|
||||
Redirects
|
||||
---------
|
||||
|
||||
Having correct redirects is a very important part of SEO. Especially the status code that is used for redirects is
|
||||
really important. Please use the appropriate status code for your use case.
|
||||
|
||||
External Resources:
|
||||
|
||||
* See `Tutorial of redirect module in TYPO3 <https://www.toujou.de/en/service/tutorials/redirects/>`__ by toujou
|
||||
* An `Overview which redirect to use in your situation <https://yoast.com/which-redirect/>`__ by Yoast
|
||||
|
||||
.. _config-tags:
|
||||
|
||||
Tags
|
||||
====
|
||||
|
||||
.. _config-hreflang-tags:
|
||||
|
||||
Hreflang Tags
|
||||
-------------
|
||||
|
||||
The generation of the :html:`<link rel="alternate" hreflang="" href="" />`
|
||||
tags is done automatically if the page is available in other languages.
|
||||
This feature should work correctly in almost all cases.
|
||||
If you have a specific edge case, and you don't want TYPO3 to render those tags, you can disable rendering of the those tags completely.
|
||||
You just have to put this line in the :file:`ext_localconf.php` of an extension and make sure your extension is loaded after EXT:seo.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags']['hreflang']);
|
||||
|
||||
.. _config-canonical-tag:
|
||||
|
||||
Canonical Tag
|
||||
-------------
|
||||
|
||||
Just like the hreflang tags, the :html:`<link rel="canonical" href="" />` tag is also generated automatically.
|
||||
If you have a specific edge case, and you don't want TYPO3 to render the tag, you can disable rendering completely.
|
||||
You just have to put this line in the :file:`ext_localconf.php` of an extension and make sure your extension is loaded after EXT:seo.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags']['canonical']);
|
||||
|
||||
Working links
|
||||
=============
|
||||
|
||||
Links in your website are quite important. You can use third party applications to check all your links, but you can
|
||||
also use the core extension linkvalidator to ensure, all the links in your site are working as expected.
|
||||
|
||||
Please check the documentation of :ref:`linkvalidator:start` .
|
||||
|
||||
TypoScript examples
|
||||
===================
|
||||
|
||||
This section will provide you examples on how to configure several behaviours in the frontend.
|
||||
|
||||
Setting fallbacks for og:image and twitter:image
|
||||
------------------------------------------------
|
||||
|
||||
If you want to have a fallback og:image or twitter:image, you can use this little snippet.
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
page {
|
||||
meta {
|
||||
og:image.stdWrap.cObject = IMG_RESOURCE
|
||||
og:image.stdWrap.cObject {
|
||||
file = EXT:your_extension/Resources/Public/Backend/OgImage.svg
|
||||
}
|
||||
twitter:image.stdWrap.cObject = IMG_RESOURCE
|
||||
twitter:image.stdWrap.cObject {
|
||||
file = EXT:your_extension/Resources/Public/Backend/TwitterCardImage.svg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
More information about the Meta Tag Api can be found on:
|
||||
|
||||
* PHP :ref:`t3coreapi:metatagapi`
|
||||
* TypoScript :ref:`t3tsref:meta`
|
||||
|
||||
Setting defaults for the author on meta tags
|
||||
--------------------------------------------
|
||||
|
||||
This example shows how to set a default author based on the TypoScript constant :ts:`{$my.default.author}`:
|
||||
|
||||
.. code-block:: typoscript
|
||||
|
||||
page {
|
||||
meta {
|
||||
author = {$my.default.author}
|
||||
}
|
||||
}
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`recommendations_field_description`
|
||||
@@ -0,0 +1,15 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
.. _developer:
|
||||
|
||||
================
|
||||
Developer Corner
|
||||
================
|
||||
|
||||
When you work with a pages-only approach you most likely don't need to use the meta tag APIs. If you have a special
|
||||
edge-case or have a detail view to show a record, you need the APIs to render the corresponding meta information for your
|
||||
page. You can refer to the documentation of the following APIs:
|
||||
|
||||
- The MetaTagApi (see :ref:`t3coreapi:metatagapi`) to define what metatags should be rendered on your page
|
||||
- The PageTitleAPI (see :ref:`t3coreapi:pagetitle`) will give you the possibility to set the title of the page
|
||||
@@ -0,0 +1,192 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
.. _for-editors:
|
||||
|
||||
===========
|
||||
For Editors
|
||||
===========
|
||||
|
||||
Target group: **Editors**
|
||||
|
||||
Welcome to our small SEO introduction.
|
||||
We will explain the basic fields the TYPO3 core provides and introduce the SEO tools this way.
|
||||
|
||||
All the following fields are part of the page record.
|
||||
|
||||
You can reach it by:
|
||||
|
||||
* Open the Web > Page module
|
||||
* Use the pen in the top bar icon to edit the page record
|
||||
|
||||
An alternative way is to use the context menu.
|
||||
|
||||
* Open Web > Page module
|
||||
* Right click the page you want to edit in the page tree
|
||||
* Select the Edit action
|
||||
|
||||
General tab
|
||||
===========
|
||||
|
||||
.. figure:: ../Images/tab-general.png
|
||||
:class: with-shadow
|
||||
:alt: General Tab in the TYPO3 Backend
|
||||
|
||||
General Tab in the TYPO3 Backend
|
||||
|
||||
Page Title
|
||||
----------
|
||||
|
||||
The page title field is used for several purposes. From a SEO perspective it is mainly used to define the default
|
||||
URL segment and as a fallback title if you don't set a specific SEO title. More information about the usage of the title
|
||||
fields can be found at :ref:`title-for-search-engines`.
|
||||
|
||||
URL Segment
|
||||
-----------
|
||||
|
||||
The URL segment is the part of the URL the user enters to access your page. Depending on your use case URLs have to
|
||||
fulfill certain criteria.
|
||||
|
||||
By default the URL segment is based on the page tree and page title, but you can override this and set it yourself.
|
||||
|
||||
If you want to optimize your page for search engines, it is recommended to have at least the keyword you want to be
|
||||
found on in your URL segment. Please make sure to check other best practises with your SEO specialist.
|
||||
|
||||
SEO Tab
|
||||
=======
|
||||
|
||||
.. figure:: ../Images/tab-seo.png
|
||||
:class: with-shadow
|
||||
:alt: SEO Tab in the TYPO3 Backend
|
||||
|
||||
SEO Tab in the TYPO3 Backend
|
||||
|
||||
.. _title-for-search-engines:
|
||||
|
||||
Title for search engines
|
||||
------------------------
|
||||
|
||||
The :guilabel:`Title for search engines` field is used in the `<title>...</title>` tag of your web page. This title is used in the
|
||||
search results of search engines. There are some best practices for the value of this field.
|
||||
|
||||
* Use a speaking title, which targets your audience
|
||||
* If possible, use the keywords you want to be found on in the title
|
||||
* Please make sure your title fits in a Google Snippet. You can use 3rd party extensions to help you with this.
|
||||
|
||||
When no value for the :guilabel:`Title for search engines` field is defined, it will have a fallback to the field
|
||||
:guilabel:`Page title`. It will show the page title in the search results in that case.
|
||||
|
||||
The title can have the name of the site as a prefix or suffix. The behaviour of this can be defined by an integrator.
|
||||
By default the title will have the name of the site as a suffix, separated by a dash.
|
||||
|
||||
.. figure:: ../Images/google-preview.png
|
||||
:class: with-shadow
|
||||
:alt: Google result preview
|
||||
|
||||
Google result preview
|
||||
|
||||
.. _index-page:
|
||||
|
||||
Index this page
|
||||
---------------
|
||||
|
||||
Defines whether the page should be indexed by a search engine or not.
|
||||
|
||||
Follow this page
|
||||
----------------
|
||||
|
||||
Defines whether search engines should follow the links on this page.
|
||||
|
||||
Canonical link
|
||||
--------------
|
||||
|
||||
You want to avoid duplicate content because your ranking can suffer from this. If you have duplicate content, you have to
|
||||
link to the origin of the content. This can be an internal page or an external page. You can use the default link wizard
|
||||
of TYPO3 to select the destination. If you don't set a canonical link yourself, TYPO3 will generate a link to the current
|
||||
page.
|
||||
|
||||
.. hint::
|
||||
|
||||
Avoid duplicate content warnings by supplying the original source of this content.
|
||||
|
||||
Metadata Tab
|
||||
============
|
||||
|
||||
.. figure:: ../Images/tab-meta-data.png
|
||||
:class: with-shadow
|
||||
:alt: Metadata Tab in the TYPO3 Backend
|
||||
|
||||
Metadata tab in the TYPO3 Backend
|
||||
|
||||
.. _description:
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The description field is important for SEO purposes. This field is used to instruct search engines to use
|
||||
this text to show as a teaser of your page in the search result snippets. With this description, you can
|
||||
give the user a clear overview of the topic of this page.
|
||||
|
||||
.. figure:: ../Images/google-preview.png
|
||||
:class: with-shadow
|
||||
:alt: Google result preview
|
||||
|
||||
Google result preview
|
||||
|
||||
.. hint::
|
||||
|
||||
It is best practises to at least use the keywords you want to be found on in this description. Make sure the
|
||||
description is not too long, so it fits in the search result snippets.
|
||||
|
||||
Social media
|
||||
============
|
||||
|
||||
.. figure:: ../Images/tab-social-media.png
|
||||
:class: with-shadow
|
||||
:alt: Social Media Tab in the TYPO3 Backend
|
||||
|
||||
Social Media Tab in the TYPO3 Backend
|
||||
|
||||
The social media tab contains information which is used to enrich the snippet that is shown when visitors share your URLs
|
||||
in social networks, modern browsers etc.
|
||||
|
||||
TYPO3 offers to have different content for OpenGraph (used for example by Facebook and LinkedIn) and Twitter Cards
|
||||
(used on Twitter of course). Both implementations allow you to set a title, a description and to provide an image. When this
|
||||
information is not set manually, the social network will decide itself what to show. Most probably it will use the
|
||||
information from :ref:`title-for-search-engines` and :ref:`description`.
|
||||
|
||||
.. figure:: ../Images/social-preview-no-image.png
|
||||
:class: with-shadow
|
||||
:alt: Facebook preview without image
|
||||
|
||||
Facebook preview without image
|
||||
|
||||
Title
|
||||
-----
|
||||
|
||||
This title can be specified to be shown as the title of the snippet preview when your page is shared on the social
|
||||
network. You only have to fill this field when you want a different title as in the search results.
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
Used to adjust the description of the preview snippet when your page is shared at a social network. You only have to
|
||||
fill this field when you want a different description as the description in the search results.
|
||||
|
||||
Image
|
||||
-----
|
||||
|
||||
You can specify the image that should be shown when your page is shared on a social network. Please be aware that the
|
||||
different social networks need specific dimensions for an image. TYPO3 ships with the most used dimension. If you want
|
||||
to have more dimensions, please let your integrator add more dimensions.
|
||||
|
||||
.. hint::
|
||||
|
||||
It is best practise to add an image for social networks so your result will stand out and will be easily recognizable.
|
||||
|
||||
|
||||
.. figure:: ../Images/social-preview-image.png
|
||||
:class: with-shadow
|
||||
:alt: Facebook preview with image
|
||||
|
||||
Facebook preview with image
|
||||
@@ -0,0 +1,51 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
.. _recommendations:
|
||||
|
||||
=======================
|
||||
General Recommendations
|
||||
=======================
|
||||
|
||||
.. _recommendations_extensions:
|
||||
|
||||
Recommendations for additional seo extensions
|
||||
=============================================
|
||||
|
||||
The TYPO3 core ships the whole API and the needed fields to have all necessary technical SEO.
|
||||
|
||||
Besides that, there are lots of tools you can use to optimize your rankings.
|
||||
|
||||
If you install additional SEO extensions in TYPO3 make sure you check the following recommendations:
|
||||
|
||||
* The extension **should** stick to the core fields where possible
|
||||
* The extension **should** stick to the core behaviour where possible
|
||||
* The extension **could** extend TCA with additional helpers for your editors, like:
|
||||
|
||||
* Readability checks
|
||||
* Keyword and content checks
|
||||
* Previews
|
||||
* Page speed insights
|
||||
|
||||
Some of these tools might need external services, others can work completely on-premise.
|
||||
|
||||
.. _recommendations_field_description:
|
||||
|
||||
Recommendations for the description field
|
||||
=========================================
|
||||
|
||||
.. danger::
|
||||
|
||||
Current state of SEO research states that the description should be either lovely crafted by hand,
|
||||
of left our completely. (end of 2019)
|
||||
|
||||
If you are unsure contact an expert.
|
||||
|
||||
.. youtube:: W4gr88oHb-k
|
||||
|
||||
* Duplicate meta descriptions should only be used under specific circumstances
|
||||
* Duplicate meta descriptions should only be used on a few pages
|
||||
* Leave the description empty if you do not can provide one
|
||||
* Provide an engaging explanation of your page, to ensure people will be motivated to visit your site
|
||||
* If SEO is not your thing, get professional help
|
||||
|
||||
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 549 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,23 @@
|
||||
.. ==================================================
|
||||
.. FOR YOUR INFORMATION
|
||||
.. --------------------------------------------------
|
||||
.. -*- coding: utf-8 -*- with BOM.
|
||||
|
||||
.. This is 'Includes.txt'. It is included at the very top of each and
|
||||
every ReST source file in this documentation project (= manual).
|
||||
|
||||
|
||||
.. ==================================================
|
||||
.. DEFINE SOME TEXT ROLES
|
||||
.. --------------------------------------------------
|
||||
|
||||
.. role:: typoscript(code)
|
||||
|
||||
.. role:: ts(typoscript)
|
||||
:class: typoscript
|
||||
|
||||
.. role:: php(code)
|
||||
|
||||
.. role:: html(code)
|
||||
|
||||
.. highlight:: php
|
||||
@@ -0,0 +1,70 @@
|
||||
.. every .rst file should include Includes.txt
|
||||
.. use correct path!
|
||||
|
||||
.. include:: Includes.txt
|
||||
|
||||
|
||||
.. Every manual should have a start label for cross-referencing to
|
||||
.. start page. Do not remove this!
|
||||
|
||||
.. _start:
|
||||
|
||||
==================
|
||||
SEO Core extension
|
||||
==================
|
||||
|
||||
:Extension key:
|
||||
seo
|
||||
|
||||
:Version:
|
||||
|release|
|
||||
|
||||
:Language:
|
||||
en
|
||||
|
||||
:Description:
|
||||
Manual for our SEO extension, which adds basic SEO features to TYPO3.
|
||||
|
||||
:Keywords:
|
||||
seo, google, meta tags, search engine optimization, sitemap
|
||||
|
||||
:Copyright:
|
||||
2018-2021
|
||||
|
||||
:Author:
|
||||
TYPO3 CMS Core Development Team
|
||||
|
||||
:License:
|
||||
Open Content License available from `www.opencontent.org/opl.shtml
|
||||
<http://www.opencontent.org/opl.shtml>`_
|
||||
|
||||
:Rendered:
|
||||
|today|
|
||||
|
||||
The content of this document is related to TYPO3,
|
||||
|
||||
a GNU/GPL CMS/Framework available from `www.typo3.org
|
||||
<http://www.typo3.org/>`_
|
||||
|
||||
**TYPO3**
|
||||
|
||||
The content of this document is related to TYPO3 CMS,
|
||||
a GNU/GPL CMS/Framework available from `typo3.org
|
||||
<https://typo3.org/>`_ .
|
||||
|
||||
**For Contributors**
|
||||
|
||||
You are welcome to help improve this guide. Just click on "Edit me on GitHub"
|
||||
on the top right to submit your change request.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
Introduction/Index
|
||||
Editor/Index
|
||||
Installation/Index
|
||||
Configuration/Index
|
||||
Developer/Index
|
||||
GeneralRecommendations/Index
|
||||
@@ -0,0 +1,56 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
|
||||
.. _installation:
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Target group: **Administrators**
|
||||
|
||||
This extension is part of the TYPO3 core.
|
||||
|
||||
Installation with composer
|
||||
==========================
|
||||
|
||||
Check whether you are already using the extension with
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
composer show | grep seo
|
||||
|
||||
This should either give you no result or something similar to:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
typo3/cms-seo v9.5.10 SEO features for TYPO3.
|
||||
|
||||
If it is not yet installed, use the ``composer require`` command to install the extension
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
composer require typo3/cms-seo ^9.5
|
||||
|
||||
The given version depends on the version of the TYPO3 core you are using.
|
||||
|
||||
Now head over to the extension manager and activate the extension.
|
||||
|
||||
.. figure:: ../Images/em.png
|
||||
:class: with-shadow
|
||||
:alt: Extension manager with filtered seo ext
|
||||
|
||||
Extension manager with filtered seo ext
|
||||
|
||||
Installation without composer
|
||||
=============================
|
||||
|
||||
In an installation without composer, the code of EXT:seo is already shipped. You just have to activate the extension.
|
||||
Head over to the extension manager and activate the extension.
|
||||
|
||||
.. figure:: ../Images/em.png
|
||||
:class: with-shadow
|
||||
:alt: Extension manager with filtered seo ext
|
||||
|
||||
Extension manager with filtered seo ext
|
||||
@@ -0,0 +1,85 @@
|
||||
.. include:: ../Includes.txt
|
||||
|
||||
|
||||
.. _introduction:
|
||||
|
||||
============
|
||||
Introduction
|
||||
============
|
||||
|
||||
What does it do?
|
||||
================
|
||||
|
||||
The goal of the core extension "seo" is to make sure basic technical Search Engine Optimization (SEO) is done right
|
||||
out of the box. Specific fields for SEO purposes are added, as well as the rendering of HTML meta tags in the frontend.
|
||||
|
||||
There are several things you can do to optimize your site for search engines.
|
||||
Some tasks are only solvable with good human writers, others are technical tasks, which just need to be implemented.
|
||||
|
||||
The goal of the extension seo is to help you with the following parts of your seo journey.
|
||||
|
||||
* Give editors all the fields needed to set the necessary meta tags
|
||||
* Provide the rendering of meta tags in the frontend for SEO related information, to avoid reimplementing it again and again
|
||||
* Provide stable APIs for developers to give them the chance to change the behaviour when needed in edge cases
|
||||
|
||||
This manual will not cover all the developer information, as these are covered in the TYPO3 API documentation.
|
||||
|
||||
.. _screenshots:
|
||||
|
||||
Screenshots
|
||||
===========
|
||||
|
||||
.. figure:: ../Images/tab-general.png
|
||||
:class: with-shadow
|
||||
:alt: General Tab in the TYPO3 Backend
|
||||
|
||||
General Tab in the TYPO3 Backend
|
||||
|
||||
.. figure:: ../Images/tab-seo.png
|
||||
:class: with-shadow
|
||||
:alt: SEO Tab in the TYPO3 Backend
|
||||
|
||||
SEO Tab in the TYPO3 Backend
|
||||
|
||||
.. figure:: ../Images/tab-meta-data.png
|
||||
:class: with-shadow
|
||||
:alt: Metadata Tab in the TYPO3 Backend
|
||||
|
||||
Metadata Tab in the TYPO3 Backend
|
||||
|
||||
Hreflang Tags
|
||||
=============
|
||||
"hreflang" tags are added automatically for multi language websites based on the one-tree principle. The links are
|
||||
based on the Site configuration and depending on translations of a page.
|
||||
|
||||
The :html:`x-default` href is the link of the current page in the default language.
|
||||
|
||||
The value of :html:`hreflang` is set for each language in the :guilabel:`Sites module` (see :ref:`t3coreapi:sitehandling-addingLanguages`)
|
||||
|
||||
Canonical Tags
|
||||
==============
|
||||
|
||||
TYPO3 provides built-in support for the canonical tag.
|
||||
|
||||
If the core extension "seo" is installed, it will automatically add the canonical link to the page.
|
||||
|
||||
The canonical link is basically the same absolute link as the link to the current hreflang and is meant
|
||||
to indicate where the original source of the content is. It is a tool to prevent duplicate content
|
||||
penalties.
|
||||
|
||||
In the page properties, the canonical link can be overwritten per language. The link wizard offers all
|
||||
possibilities including external links and link handler configurations.
|
||||
|
||||
If no canonical link is given manually, TYPO3 will generate a canonicalized URL to the current page in the current
|
||||
language.
|
||||
|
||||
.. warning::
|
||||
If you have other SEO extensions installed that generate canonical links, you have to make sure only one creates it.
|
||||
If both core and an extension are generating a canonical link, it will result in 2 canonical links which might
|
||||
cause confusion for search engines.
|
||||
|
||||
XML Sitemap
|
||||
===========
|
||||
|
||||
See :ref:`t3coreapi:xmlsitemap` for the documentation how to configure your XML sitemaps.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
[general]
|
||||
|
||||
; endless list of all of the general simple settings
|
||||
; you can use in 'conf.py'
|
||||
|
||||
project = Search engine optimization (seo)
|
||||
version = latest (10-dev)
|
||||
release = latest (10-dev)
|
||||
t3author = TYPO3 Core Team + Contributors, TYPO3 Documentation Team
|
||||
copyright = 2018-2021
|
||||
|
||||
description = This documentation is about TYPO3's system extension
|
||||
"seo" which provides provides search engine optimizations
|
||||
|
||||
[html_theme_options]
|
||||
|
||||
; for theme t3SphinxThemeRtd
|
||||
|
||||
# Add 'Edit me on Github' button
|
||||
github_branch = master
|
||||
github_repository = typo3/typo3
|
||||
path_to_documentation_dir = typo3/sysext/seo/Documentation
|
||||
|
||||
# show as related links
|
||||
project_contact =
|
||||
project_discussions = http://lists.typo3.org/cgi-bin/mailman/listinfo/typo3-project-content-rendering
|
||||
project_home = https://forge.typo3.org/projects/typo3cms-core
|
||||
project_issues = https://forge.typo3.org/projects/typo3cms-core/issues
|
||||
project_repository = https://github.com/typo3/typo3
|
||||
|
||||
|
||||
[intersphinx_mapping]
|
||||
|
||||
# These are common mappings. Uncomment only what you really use!
|
||||
# see https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/GeneralConventions/DirectoryFilenames.html#intersphinx-mapping
|
||||
|
||||
h2document = https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/
|
||||
|
||||
t3coreapi = https://docs.typo3.org/m/typo3/reference-coreapi/9.5/en-us/
|
||||
t3install = https://docs.typo3.org/m/typo3/guide-installation/9.5/en-us/
|
||||
t3tsref = https://docs.typo3.org/m/typo3/reference-typoscript/9.5/en-us/
|
||||
|
||||
linkvalidator = https://docs.typo3.org/c/typo3/cms-linkvalidator/9.5/en-us/
|
||||
@@ -0,0 +1,345 @@
|
||||
Some icons used in the TYPO3 project are retrieved from the "Silk" icon set of
|
||||
Mark James, which can be found at http://famfamfam.com/lab/icons/silk/. This
|
||||
set is distributed under a Creative Commons Attribution 2.5 License. The
|
||||
license can be found at http://creativecommons.org/licenses/by/2.5/.
|
||||
---------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
|
||||
<file t3:id="1528554340" source-language="en" datatype="plaintext" original="EXT:seo/Resources/Private/Language/locallang_tca.xlf" date="2018-08-09T16:22:32Z"
|
||||
product-name="seo">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="pages.tabs.seo" resname="pages.tabs.seo">
|
||||
<source>SEO</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.seo" resname="pages.palettes.seo">
|
||||
<source>General SEO settings</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.seo_title" resname="pages.seo_title">
|
||||
<source>Title for search engines</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.robots" resname="pages.palettes.robots">
|
||||
<source>Robot instructions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.no_index" resname="pages.no_index">
|
||||
<source>No index</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.no_index_formlabel" resname="pages.no_index_formlabel">
|
||||
<source>Index this page</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.no_follow" resname="pages.no_follow">
|
||||
<source>No follow</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.no_follow_formlabel" resname="pages.no_follow_formlabel">
|
||||
<source>Follow this page</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.opengraph" resname="pages.palettes.opengraph">
|
||||
<source>Open Graph (Facebook)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.og_title" resname="pages.og_title">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.og_description" resname="pages.og_description">
|
||||
<source>Description</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.og_image" resname="pages.og_image">
|
||||
<source>Image</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.twittercards" resname="pages.palettes.twittercards">
|
||||
<source>Twitter Cards</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.twitter_title" resname="pages.twitter_title">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.twitter_description" resname="pages.twitter_description">
|
||||
<source>Description</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.twitter_image" resname="pages.twitter_image">
|
||||
<source>Image</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.canonical" resname="pages.palettes.canonical">
|
||||
<source>Canonical</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.canonical_link" resname="pages.canonical_link">
|
||||
<source>Canonical link</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.tabs.socialmedia" resname="pages.tabs.socialmedia">
|
||||
<source>Social media</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="{xslFile}"?>
|
||||
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<f:for each="{sitemaps}" as="sitemap">
|
||||
<sitemap>
|
||||
<f:spaceless>
|
||||
<f:if condition="{sitemap.page}">
|
||||
<f:then><loc><f:uri.page pageType="{type}" additionalParams="{sitemap: sitemap.key, page: sitemap.page}" absolute="true" noCacheHash="false" /></loc></f:then>
|
||||
<f:else><loc><f:uri.page pageType="{type}" additionalParams="{sitemap: sitemap.key}" absolute="true" noCacheHash="false" /></loc></f:else>
|
||||
</f:if>
|
||||
</f:spaceless>
|
||||
<lastmod>{sitemap.lastMod -> f:format.date(format: 'c')}</lastmod>
|
||||
</sitemap>
|
||||
</f:for>
|
||||
</sitemapindex>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="{xslFile}"?>
|
||||
|
||||
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<f:for each="{items}" as="item">
|
||||
<f:if condition="{item.loc}">
|
||||
<url>
|
||||
<loc>{item.loc}</loc>
|
||||
<lastmod>{item.lastMod -> f:format.date(format: 'c')}</lastmod>
|
||||
</url>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</urlset>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="2.0"
|
||||
xmlns:html="http://www.w3.org/TR/REC-html40"
|
||||
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
||||
xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||
<xsl:template match="/">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>TYPO3 XML Sitemap</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #545353;
|
||||
}
|
||||
table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#sitemap tr:nth-child(odd) td {
|
||||
background-color: #eee !important;
|
||||
}
|
||||
#sitemap tbody tr:hover td {
|
||||
background-color: #ccc;
|
||||
}
|
||||
#sitemap tbody tr:hover td, #sitemap tbody tr:hover td a {
|
||||
color: #000;
|
||||
}
|
||||
#content {
|
||||
margin: 0 auto;
|
||||
width: 1000px;
|
||||
}
|
||||
.expl {
|
||||
margin: 18px 3px;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
color: #777;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
td {
|
||||
font-size:11px;
|
||||
}
|
||||
th {
|
||||
text-align:left;
|
||||
padding-right:30px;
|
||||
font-size:11px;
|
||||
}
|
||||
thead th {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<h1>TYPO3 XML Sitemap</h1>
|
||||
<xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) > 0">
|
||||
<p class="expl">
|
||||
This XML Sitemap Index file contains <xsl:value-of select="count(sitemap:sitemapindex/sitemap:sitemap)"/> sitemaps.
|
||||
</p>
|
||||
<table id="sitemap" cellpadding="3" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sitemap</th>
|
||||
<th>Last modified</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<xsl:for-each select="sitemap:sitemapindex/sitemap:sitemap">
|
||||
<xsl:variable name="sitemapURL">
|
||||
<xsl:value-of select="sitemap:loc"/>
|
||||
</xsl:variable>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{$sitemapURL}"><xsl:value-of select="sitemap:loc"/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{$sitemapURL}"><xsl:value-of select="sitemap:lastmod"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:for-each>
|
||||
</tbody>
|
||||
</table>
|
||||
</xsl:if>
|
||||
<xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) < 1">
|
||||
<p class="expl">
|
||||
This XML Sitemap contains <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> URLs.
|
||||
</p>
|
||||
<table id="sitemap" cellpadding="3" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80%">URL</th>
|
||||
<th title="Last Modification Time" width="20%">Last Mod.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
|
||||
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
|
||||
<xsl:for-each select="sitemap:urlset/sitemap:url">
|
||||
<tr>
|
||||
<td>
|
||||
<xsl:variable name="itemURL">
|
||||
<xsl:value-of select="sitemap:loc"/>
|
||||
</xsl:variable>
|
||||
<a href="{$itemURL}">
|
||||
<xsl:value-of select="sitemap:loc"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<xsl:value-of select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)),concat(' ', substring(sitemap:lastmod,20,6)))"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:for-each>
|
||||
</tbody>
|
||||
</table>
|
||||
</xsl:if>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "typo3/cms-seo",
|
||||
"type": "typo3-cms-framework",
|
||||
"description": "SEO features for TYPO3.",
|
||||
"homepage": "https://typo3.org",
|
||||
"license": ["GPL-2.0-or-later"],
|
||||
"authors": [{
|
||||
"name": "TYPO3 Core Team",
|
||||
"email": "typo3cms@typo3.org",
|
||||
"role": "Developer"
|
||||
}],
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"typo3/cms-core": "9.5.31",
|
||||
"typo3/cms-frontend": "9.5.31",
|
||||
"typo3/cms-extbase": "9.5.31"
|
||||
},
|
||||
"conflict": {
|
||||
"typo3/cms": "*"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "seo",
|
||||
"Package": {
|
||||
"partOfFactoryDefault": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"TYPO3\\CMS\\Seo\\": "Classes/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"TYPO3\\CMS\\Seo\\Tests\\": "Tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$EM_CONF[$_EXTKEY] = [
|
||||
'title' => 'SEO',
|
||||
'description' => 'Add basic SEO features to TYPO3',
|
||||
'category' => 'fe',
|
||||
'author' => 'TYPO3 Core Team',
|
||||
'author_email' => 'typo3cms@typo3.org',
|
||||
'author_company' => '',
|
||||
'state' => 'stable',
|
||||
'uploadfolder' => 0,
|
||||
'createDirs' => '',
|
||||
'clearCacheOnLoad' => 0,
|
||||
'version' => '9.5.31',
|
||||
'constraints' => [
|
||||
'depends' => [
|
||||
'typo3' => '9.5.31'
|
||||
],
|
||||
'conflicts' => [],
|
||||
'suggests' => [],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
defined('TYPO3_MODE') or die();
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags']['metatag'] =
|
||||
\TYPO3\CMS\Seo\MetaTag\MetaTagGenerator::class . '->generate';
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags']['hreflang'] =
|
||||
\TYPO3\CMS\Seo\HrefLang\HrefLangGenerator::class . '->generate';
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags']['canonical'] =
|
||||
\TYPO3\CMS\Seo\Canonical\CanonicalGenerator::class . '->generate';
|
||||
|
||||
$metaTagManagerRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry::class);
|
||||
$metaTagManagerRegistry->registerManager(
|
||||
'opengraph',
|
||||
\TYPO3\CMS\Seo\MetaTag\OpenGraphMetaTagManager::class
|
||||
);
|
||||
$metaTagManagerRegistry->registerManager(
|
||||
'twitter',
|
||||
\TYPO3\CMS\Seo\MetaTag\TwitterCardMetaTagManager::class
|
||||
);
|
||||
unset($metaTagManagerRegistry);
|
||||
|
||||
// Add module configuration
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup(trim('
|
||||
config.pageTitleProviders {
|
||||
seo {
|
||||
provider = TYPO3\CMS\Seo\PageTitle\SeoTitlePageTitleProvider
|
||||
before = record
|
||||
after = altPageTitle
|
||||
}
|
||||
}
|
||||
'));
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Table structure for table 'pages'
|
||||
#
|
||||
CREATE TABLE pages (
|
||||
seo_title varchar(255) DEFAULT '' NOT NULL,
|
||||
no_index tinyint(4) DEFAULT '0' NOT NULL,
|
||||
no_follow tinyint(4) DEFAULT '0' NOT NULL,
|
||||
og_title varchar(255) DEFAULT '' NOT NULL,
|
||||
og_description text,
|
||||
og_image int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
twitter_title varchar(255) DEFAULT '' NOT NULL,
|
||||
twitter_description text,
|
||||
twitter_image int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
canonical_link varchar(2048) DEFAULT '' NOT NULL,
|
||||
);
|
||||