Skip to content

Commit

Permalink
Merge pull request #511 from Yoast/feature/507-configure-records
Browse files Browse the repository at this point in the history
[FEATURE] Possibility to automatically add Yoast SEO functionality to custom records
  • Loading branch information
RinyVT authored Aug 16, 2022
2 parents 6ce23d6 + 4448a67 commit 08d39d2
Show file tree
Hide file tree
Showing 32 changed files with 1,774 additions and 335 deletions.
7 changes: 2 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ jobs:
max-parallel: 6
fail-fast: false
matrix:
typo3: ['9', '10']
php: ['php7.2', 'php7.3', 'php7.4']
typo3: ['9', '10', '11']
php: ['php7.4']
experimental: [false]
include:
- typo3: '11'
php: 'php7.4'
experimental: false
- typo3: '11'
php: 'php8.0'
experimental: false
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ We will follow [Semantic Versioning](http://semver.org/).
## Yoast SEO Premium for TYPO3
Besides the free version of our plugin, we also have a premium version. The free version enables you to do all necessary optimizations. With the premium version, we make it even easier to do! More information can be found on https://www.maxserv.com/yoast.

## 9.0.0-alpha-1 August 16, 2022
### Breaking
- Dropped support for PHP <7.4

### Added
- New feature to automatically activate Yoast SEO functionality on custom records

## 8.3.1 August 3, 2022
### Fixed
- Github CI pipelines by adding the correct "allow-plugins" to composer.json
Expand Down
20 changes: 20 additions & 0 deletions Classes/EventListener/AbstractListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\EventListener;

use YoastSeoForTypo3\YoastSeo\Record\Builder\AbstractBuilder;

abstract class AbstractListener
{
/**
* @var \YoastSeoForTypo3\YoastSeo\Record\Builder\AbstractBuilder
*/
protected AbstractBuilder $builder;

public function __construct(AbstractBuilder $builder)
{
$this->builder = $builder;
}
}
51 changes: 51 additions & 0 deletions Classes/EventListener/RecordCanonicalListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\EventListener;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3\CMS\Seo\Event\ModifyUrlForCanonicalTagEvent;
use YoastSeoForTypo3\YoastSeo\Record\Record;
use YoastSeoForTypo3\YoastSeo\Record\RecordService;

class RecordCanonicalListener
{
protected TypoScriptFrontendController $typoScriptFrontendController;

protected RecordService $recordService;

public function __construct(TypoScriptFrontendController $typoScriptFrontendController = null, RecordService $recordService = null)
{
if ($typoScriptFrontendController === null) {
$typoScriptFrontendController = GeneralUtility::makeInstance(TypoScriptFrontendController::class);
}
if ($recordService === null) {
$recordService = GeneralUtility::makeInstance(RecordService::class);
}

$this->typoScriptFrontendController = $typoScriptFrontendController;
$this->recordService = $recordService;
}

public function setCanonical(ModifyUrlForCanonicalTagEvent $event): void
{
$activeRecord = $this->recordService->getActiveRecord();
if (!$activeRecord instanceof Record) {
return;
}

$canonicalLink = $activeRecord->getRecordData()['canonical_link'] ?? '';
if (empty($canonicalLink)) {
return;
}

$event->setUrl(
$this->typoScriptFrontendController->cObj->typoLink_URL([
'parameter' => $canonicalLink,
'forceAbsoluteUrl' => true,
])
);
}
}
21 changes: 21 additions & 0 deletions Classes/EventListener/TableDefinitionsListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\EventListener;

use TYPO3\CMS\Core\Database\Event\AlterTableDefinitionStatementsEvent;
use YoastSeoForTypo3\YoastSeo\Record\RecordRegistry;

class TableDefinitionsListener extends AbstractListener
{
public function addDatabaseSchema(AlterTableDefinitionStatementsEvent $event): void
{
foreach (RecordRegistry::getInstance()->getRecords() as $record) {
$this->builder
->setRecord($record)
->build();
}
$event->addSqlData(implode(LF . LF, $this->builder->getResult()));
}
}
22 changes: 22 additions & 0 deletions Classes/EventListener/TcaBuiltListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\EventListener;

use TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent;
use YoastSeoForTypo3\YoastSeo\Record\RecordRegistry;

class TcaBuiltListener extends AbstractListener
{
public function addRecordTca(AfterTcaCompilationEvent $event): void
{
$GLOBALS['TCA'] = $event->getTca();
foreach (RecordRegistry::getInstance()->getRecords() as $record) {
$this->builder
->setRecord($record)
->build();
}
$event->setTca($GLOBALS['TCA']);
}
}
9 changes: 9 additions & 0 deletions Classes/Exception/UnsupportedVersionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\Exception;

class UnsupportedVersionException extends \Exception
{
}
70 changes: 70 additions & 0 deletions Classes/MetaTag/Generator/AbstractGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\MetaTag\Generator;

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 YoastSeoForTypo3\YoastSeo\Record\Record;

abstract class AbstractGenerator
{
protected MetaTagManagerRegistry $managerRegistry;

public function __construct(MetaTagManagerRegistry $managerRegistry = null)
{
if ($managerRegistry === null) {
$managerRegistry = GeneralUtility::makeInstance(MetaTagManagerRegistry::class);
}
$this->managerRegistry = $managerRegistry;
}

/**
* @see \TYPO3\CMS\Seo\MetaTag\MetaTagGenerator
* @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'] ?? false) ?: 'social';
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
$crop = $cropArea->makeAbsoluteBasedOnFile($file);

$processingConfiguration = [
'crop' => $crop,
'maxWidth' => 2000,
];

$processedImage = $file->getOriginalFile()->process(
ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
$processingConfiguration
);

$imageUri = $imageService->getImageUri($processedImage, true);

$socialImages[] = [
'url' => $imageUri,
'width' => floor($processedImage->getProperty('width')),
'height' => floor($processedImage->getProperty('height')),
'alternative' => $arguments['alternative'],
];
}

return $socialImages;
}

abstract public function generate(Record $record): void;
}
21 changes: 21 additions & 0 deletions Classes/MetaTag/Generator/DescriptionGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\MetaTag\Generator;

use YoastSeoForTypo3\YoastSeo\Record\Record;

class DescriptionGenerator extends AbstractGenerator
{
public function generate(Record $record): void
{
$description = $record->getRecordData()[$record->getDescriptionField()] ?? '';

if (!empty($description)) {
$manager = $this->managerRegistry->getManagerForProperty('description');
$manager->removeProperty('description');
$manager->addProperty('description', $description);
}
}
}
56 changes: 56 additions & 0 deletions Classes/MetaTag/Generator/OpenGraphGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\MetaTag\Generator;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Resource\FileCollector;
use YoastSeoForTypo3\YoastSeo\Record\Record;

class OpenGraphGenerator extends AbstractGenerator
{
public function generate(Record $record): void
{
$ogTitle = $record->getRecordData()['og_title'] ?? '';
if (!empty($ogTitle)) {
$manager = $this->managerRegistry->getManagerForProperty('og:title');
$manager->removeProperty('og:title');
$manager->addProperty('og:title', $ogTitle);
}

$ogDescription = $record->getRecordData()['og_description'] ?? '';
if ($ogDescription) {
$manager = $this->managerRegistry->getManagerForProperty('og:description');
$manager->removeProperty('og:description');
$manager->addProperty('og:description', $ogDescription);
}

if ($record->getRecordData()['og_image']) {
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
$fileCollector->addFilesFromRelation($record->getTableName(), 'og_image', $record->getRecordData());
$manager = $this->managerRegistry->getManagerForProperty('og:image');

$ogImages = $this->generateSocialImages($fileCollector->getFiles());
if (count($ogImages) > 0) {
$manager->removeProperty('og:image');
}
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
);
}
}
}
}
26 changes: 26 additions & 0 deletions Classes/MetaTag/Generator/RobotsGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\MetaTag\Generator;

use YoastSeoForTypo3\YoastSeo\Record\Record;

class RobotsGenerator extends AbstractGenerator
{
public function generate(Record $record): void
{
if (!isset($record->getRecordData()['no_index'], $record->getRecordData()['no_follow'])) {
return;
}

$noIndex = ((bool)$record->getRecordData()['no_index']) ? 'noindex' : 'index';
$noFollow = ((bool)$record->getRecordData()['no_follow']) ? 'nofollow' : 'follow';

if ($noIndex === 'noindex' || $noFollow === 'nofollow') {
$manager = $this->managerRegistry->getManagerForProperty('robots');
$manager->removeProperty('robots');
$manager->addProperty('robots', implode(',', [$noIndex, $noFollow]));
}
}
}
60 changes: 60 additions & 0 deletions Classes/MetaTag/Generator/TwitterGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace YoastSeoForTypo3\YoastSeo\MetaTag\Generator;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Resource\FileCollector;
use YoastSeoForTypo3\YoastSeo\Record\Record;

class TwitterGenerator extends AbstractGenerator
{
public function generate(Record $record): void
{
$twitterCard = $record->getRecordData()['twitter_card'] ?? '';
if (!empty($twitterCard)) {
$manager = $this->managerRegistry->getManagerForProperty('twitter:card');
$manager->removeProperty('twitter:card');
$manager->addProperty('twitter:card', $twitterCard);
}

$twitterTitle = $record->getRecordData()['twitter_title'] ?? '';
if (!empty($twitterTitle)) {
$manager = $this->managerRegistry->getManagerForProperty('twitter:title');
$manager->removeProperty('twitter:title');
$manager->addProperty('twitter:title', $twitterTitle);
}

$twitterDescription = $record->getRecordData()['twitter_description'];
if (!empty($twitterDescription)) {
$manager = $this->managerRegistry->getManagerForProperty('twitter:description');
$manager->removeProperty('twitter:description');
$manager->addProperty('twitter:description', $twitterDescription);
}

if ($record->getRecordData()['twitter_image']) {
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
$fileCollector->addFilesFromRelation($record->getTableName(), 'twitter_image', $record->getRecordData());
$manager = $this->managerRegistry->getManagerForProperty('twitter:image');

$twitterImages = $this->generateSocialImages($fileCollector->getFiles());
if (count($twitterImages) > 0) {
$manager->removeProperty('twitter:image');
}
foreach ($twitterImages as $twitterImage) {
$subProperties = [];

if (!empty($twitterImage['alternative'])) {
$subProperties['alt'] = $twitterImage['alternative'];
}

$manager->addProperty(
'twitter:image',
$twitterImage['url'],
$subProperties
);
}
}
}
}
Loading

0 comments on commit 08d39d2

Please sign in to comment.