[FEATURE] Render basic SEO meta tags in frontend 17/57117/4
authorRichard Haeser <richard@maxserv.com>
Sun, 3 Jun 2018 19:06:19 +0000 (21:06 +0200)
committerAndreas Wolf <andreas.wolf@typo3.org>
Tue, 5 Jun 2018 17:58:23 +0000 (19:58 +0200)
The SEO meta tags that can be set in the page properties, will be
rendered in frontend.

Resolves: #85147
Releases: master
Change-Id: I75e6d8e50b7c8616497ee7075fddcb182551bea4
Reviewed-on: https://review.typo3.org/57117
Tested-by: TYPO3com <no-reply@typo3.com>
Tested-by: Riccardo De Contardi <erredeco@gmail.com>
Reviewed-by: Joerg Boesche <typo3@joergboesche.de>
Reviewed-by: Tobi Kretschmann <tobi@tobishome.de>
Tested-by: Tobi Kretschmann <tobi@tobishome.de>
Tested-by: Joerg Boesche <typo3@joergboesche.de>
Reviewed-by: Riny van Tiggelen <info@online-gamer.nl>
Reviewed-by: Steffen Frese <steffenf14@gmail.com>
Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org>
Tested-by: Andreas Wolf <andreas.wolf@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-85147-RenderSEOMetaTagsInFrontend.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
typo3/sysext/frontend/Classes/Page/PageGenerator.php
typo3/sysext/frontend/Tests/Unit/Page/PageGeneratorTest.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-85147-RenderSEOMetaTagsInFrontend.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-85147-RenderSEOMetaTagsInFrontend.rst
new file mode 100644 (file)
index 0000000..979dd40
--- /dev/null
@@ -0,0 +1,22 @@
+.. include:: ../../Includes.txt
+
+==================================================
+Feature: #85147 - Render SEO meta tags in frontend
+==================================================
+
+See :issue:`85147`
+
+Description
+===========
+
+The SEO meta tags that can be set in the page properties, are now rendered in frontend by default.
+
+
+Impact
+======
+
+No addition configuration is needed to render these meta tags. If you want to override the meta tags set by
+the pageproperties, you can use the replace parameter in TypoScript or in the addProperty method of the specific
+MetaTagManager.
+
+.. index:: Frontend, ext:core
\ No newline at end of file
index 5014b51..386b231 100644 (file)
@@ -34,12 +34,16 @@ use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
 use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
 use TYPO3\CMS\Core\Error\Http\ShortcutTargetPageNotFoundException;
+use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
 use TYPO3\CMS\Core\Locking\LockFactory;
 use TYPO3\CMS\Core\Locking\LockingStrategyInterface;
 use TYPO3\CMS\Core\Log\LogManager;
+use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
 use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Resource\FileReference;
+use TYPO3\CMS\Core\Resource\ProcessedFile;
 use TYPO3\CMS\Core\Resource\StorageRepository;
 use TYPO3\CMS\Core\Service\DependencyOrderingService;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
@@ -53,11 +57,13 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
+use TYPO3\CMS\Extbase\Service\ImageService;
 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Frontend\Http\UrlHandlerInterface;
 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
 use TYPO3\CMS\Frontend\Page\PageRepository;
+use TYPO3\CMS\Frontend\Resource\FileCollector;
 
 /**
  * Class for the built TypoScript based frontend. Instantiated in
@@ -3431,6 +3437,10 @@ class TypoScriptFrontendController implements LoggerAwareInterface
         }
 
         $pageTitle = $this->altPageTitle ?: $this->page['title'] ?? '';
+        if (isset($this->page['seo_title']) && !empty($this->page['seo_title'])) {
+            $pageTitle = $this->page['seo_title'];
+        }
+
         $titleTagContent = $this->printTitle(
             $pageTitle,
             (bool)$this->config['config']['noPageTitle'],
@@ -3460,6 +3470,129 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     }
 
     /**
+     * Generate the meta tags that can be set in backend and add them to frontend by using the MetaTag API
+     */
+    public function generateMetaTags()
+    {
+        $metaTagManagerRegistry = MetaTagManagerRegistry::getInstance();
+
+        if (!empty($this->page['description'])) {
+            $manager = $metaTagManagerRegistry->getManagerForProperty('description');
+            $manager->addProperty('description', $this->page['description']);
+        }
+
+        if (!empty($this->page['og_title'])) {
+            $manager = $metaTagManagerRegistry->getManagerForProperty('og:title');
+            $manager->addProperty('og:title', $this->page['og_title']);
+        }
+
+        if (!empty($this->page['og_description'])) {
+            $manager = $metaTagManagerRegistry->getManagerForProperty('og:description');
+            $manager->addProperty('og:description', $this->page['og_description']);
+        }
+
+        if (!empty($this->page['og_image'])) {
+            $fileCollector = GeneralUtility::makeInstance(FileCollector::class);
+            $fileCollector->addFilesFromRelation('pages', 'og_image', $this->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
+                );
+            }
+        }
+
+        if (!empty($this->page['twitter_title'])) {
+            $manager = $metaTagManagerRegistry->getManagerForProperty('twitter:title');
+            $manager->addProperty('twitter:title', $this->page['twitter_title']);
+        }
+
+        if (!empty($this->page['twitter_description'])) {
+            $manager = $metaTagManagerRegistry->getManagerForProperty('twitter:description');
+            $manager->addProperty('twitter:description', $this->page['twitter_description']);
+        }
+
+        if (!empty($this->page['twitter_image'])) {
+            $fileCollector = GeneralUtility::makeInstance(FileCollector::class);
+            $fileCollector->addFilesFromRelation('pages', 'twitter_image', $this->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)$this->page['no_index']) ? 'noindex' : 'index';
+        $noFollow = ((bool)$this->page['no_follow']) ? 'nofollow' : 'follow';
+
+        $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'] ?: 'default';
+            $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;
+    }
+
+    /**
      * Compiles the content for the page <title> tag.
      *
      * @param string $pageTitle The input title string, typically the "title" field of a page's record.
index 37b66e9..ef31940 100644 (file)
@@ -533,6 +533,7 @@ class PageGenerator
             $pageRenderer->addFooterData($tsfe->cObj->cObjGet($tsfe->pSetup['footerData.'], 'footerData.'));
         }
         $tsfe->generatePageTitle();
+        $tsfe->generateMetaTags();
 
         static::generateMetaTagHtml(
             $tsfe->pSetup['meta.'] ?? [],
index 80c850e..8cfec5b 100644 (file)
@@ -185,6 +185,7 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $tmpl = $this->prophesize(TemplateService::class);
         $tsfe = $this->prophesize(TypoScriptFrontendController::class);
         $tsfe->generatePageTitle()->willReturn('');
+        $tsfe->generateMetaTags()->shouldBeCalled();
         $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
         $tsfe->cObj = $cObj->reveal();
         $tsfe->tmpl = $tmpl->reveal();
@@ -220,6 +221,7 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $tmpl = $this->prophesize(TemplateService::class);
         $tsfe = $this->prophesize(TypoScriptFrontendController::class);
         $tsfe->generatePageTitle()->willReturn('');
+        $tsfe->generateMetaTags()->shouldBeCalled();
         $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
         $tsfe->cObj = $cObj->reveal();
         $tsfe->tmpl = $tmpl->reveal();
@@ -257,6 +259,7 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $tmpl = $this->prophesize(TemplateService::class);
         $tsfe = $this->prophesize(TypoScriptFrontendController::class);
         $tsfe->generatePageTitle()->willReturn('');
+        $tsfe->generateMetaTags()->shouldBeCalled();
         $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
         $tsfe->cObj = $cObj->reveal();
         $tsfe->tmpl = $tmpl->reveal();
@@ -348,6 +351,7 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $tmpl = $this->prophesize(TemplateService::class);
         $tsfe = $this->prophesize(TypoScriptFrontendController::class);
         $tsfe->generatePageTitle()->willReturn('');
+        $tsfe->generateMetaTags()->shouldBeCalled();
         $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
         $tsfe->cObj = $cObj->reveal();
         $tsfe->tmpl = $tmpl->reveal();