Skip to content

Commit

Permalink
[FEATURE] Enhance compatibility with "masi", update slugs when the ex…
Browse files Browse the repository at this point in the history
…clude flag changes
  • Loading branch information
wazum committed Dec 13, 2023
1 parent b2c353e commit ed98912
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
178 changes: 178 additions & 0 deletions Classes/DataHandler/HandleExcludeSlugForSubpages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

declare(strict_types=1);

namespace Wazum\Sluggi\DataHandler;

use Doctrine\DBAL\Exception;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\DataHandling\Model\CorrelationId;
use TYPO3\CMS\Core\DataHandling\SlugHelper;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Wazum\Sluggi\Service\SlugService;

// This class handles compatibility with "masi"'s exclude slug switch
final class HandleExcludeSlugForSubpages implements LoggerAwareInterface
{
use LoggerAwareTrait;

private Context $context;
private PageRepository $pageRepository;
private SlugService $slugService;

public function __construct(
Context $context,
PageRepository $pageRepository,
SlugService $slugService
) {
$this->context = $context;
$this->pageRepository = $pageRepository;
$this->slugService = $slugService;
}

/**
* @param string|int $id
*
* @throws \Exception
* @throws Exception
*/
public function processDatamap_afterDatabaseOperations(
string $status,
string $table,
$id,
array &$fields,
DataHandler $dataHandler
): void {
if (!$this->shouldRun($status, $table, $fields)) {
return;
}

$pageRecord = BackendUtility::getRecordWSOL($table, (int) $id);
if (null === $pageRecord) {
/* @psalm-suppress PossiblyNullReference */
$this->logger->warning(sprintf('Unable to get page record with ID "%s"', $id));

return;
}

// If the flag changed
if (($pageRecord['exclude_slug_for_subpages'] ?? false) !== $fields['exclude_slug_for_subpages']) {
// We update all child pages
$fieldConfig = $GLOBALS['TCA']['pages']['columns']['slug']['config'] ?? [];
$slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $table, 'slug', $fieldConfig);
$pageId = 0 === $pageRecord['sys_language_uid'] ? (int) $pageRecord['uid'] : (int) $pageRecord['l10n_parent'];
$subPageRecords = $this->resolveSubPages($pageId, $pageRecord['sys_language_uid']);
foreach ($subPageRecords as $subPageRecord) {
if ($this->shouldApplySubpageUpdate($subPageRecord)) {
$slug = $slugHelper->generate($subPageRecord, $subPageRecord['pid']);
$this->persistNewSlug((int) $subPageRecord['uid'], $slug, $dataHandler->getCorrelationId());
}
}
}
}

/**
* Method copied from \TYPO3\CMS\Redirects\Service\SlugService.
*
* @throws \Exception
* @throws Exception
*/
private function resolveSubPages(int $id, int $languageUid): array
{
// First resolve all sub-pages in default language
$queryBuilder = $this->getQueryBuilderForPages();
$subPages = $queryBuilder
->select('*')
->from('pages')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, Connection::PARAM_INT)),
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT))
)
->orderBy('uid', 'ASC')
->executeQuery()
->fetchAllAssociative();

// If the language is not the default language, resolve the language related records.
if ($languageUid > 0) {
$queryBuilder = $this->getQueryBuilderForPages();
$subPages = $queryBuilder
->select('*')
->from('pages')
->where(
$queryBuilder->expr()->in('l10n_parent', $queryBuilder->createNamedParameter(array_column($subPages, 'uid'), Connection::PARAM_INT_ARRAY)),
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($languageUid, Connection::PARAM_INT))
)
->orderBy('uid', 'ASC')
->executeQuery()
->fetchAllAssociative();
}
$results = [];
if (!empty($subPages)) {
$subPages = $this->pageRepository->getPagesOverlay($subPages, $languageUid);
foreach ($subPages as $subPage) {
// Only one level deep, rest is done by the Core recursively
$results[] = $subPage;
}
}

return $results;
}

/**
* @param array<string, mixed> $pageRecord
*/
private function shouldApplySubpageUpdate(array $pageRecord): bool
{
return !$pageRecord['slug_locked'] && $pageRecord['tx_sluggi_sync'];
}

private function persistNewSlug(int $uid, string $newSlug, CorrelationId $correlationId): void
{
$this->disableHook();
$data = [];
$data['pages'][$uid]['slug'] = $newSlug;
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, []);
$dataHandler->setCorrelationId($correlationId);
$dataHandler->process_datamap();
$this->enabledHook();
}

private function getQueryBuilderForPages(): QueryBuilder
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('pages');
$queryBuilder
->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class))
->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->context->getPropertyFromAspect('workspace', 'id')));

return $queryBuilder;
}

private function enabledHook(): void
{
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['sluggi-exclude'] = __CLASS__;
}

private function disableHook(): void
{
unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['sluggi-exclude']);
}

private function shouldRun(string $status, string $table, array $fields): bool
{
return 'update' === $status && 'pages' === $table && isset($fields['exclude_slug_for_subpages']);
}
}
3 changes: 3 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ services:

Wazum\Sluggi\DataHandler\HandlePageUpdate:
public: true

Wazum\Sluggi\DataHandler\HandleExcludeSlugForSubpages:
public: true
4 changes: 4 additions & 0 deletions Configuration/TCA/Overrides/pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,9 @@ function array_flatten(array $array): array
break;
}
}

// Makes no sense and only problems
unset($GLOBALS['TCA']['pages']['columns']['exclude_slug_for_subpages']['config']['behaviour']['allowLanguageSynchronization']);
$GLOBALS['TCA']['pages']['columns']['exclude_slug_for_subpages']['l10n_mode'] = 'exclude';
}
})();
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ The code is compatible with _masi_ (>= `2.0`) and there's a configuration option
The default is the list the core uses.
If you want to use _masi_, set the value to `255` (recycler) only.

When you change the "Exclude this page for slug generation of subpages" toggle, _sluggi_ will regenerate the slug for all the subpages of the current page.
If you want to preserve slugs for certain subpages you have to _lock_ them before.

_sluggi_ removes the configuration for `['behaviour']['allowLanguageSynchronization']` and sets `'l10n_mode' = 'exclude'` for the `exclude_slug_for_subpages` field. Makes no sense in my eyes and I don't want to deal with the problems.

### _ig_slug_ (https://github.com/internetgalerie/ig_slug)

The field `tx_sluggi_lock` has been renamed to `slug_locked` in version 12, so both extensions can work together.
Expand Down
5 changes: 5 additions & 0 deletions ext_localconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['slug_lock_upgrade_wizard']
= \Wazum\Sluggi\Upgrade\SlugLockUpgradeWizard::class;

if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('masi')) {
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['sluggi-exclude']
= \Wazum\Sluggi\DataHandler\HandleExcludeSlugForSubpages::class;
}
})();

0 comments on commit ed98912

Please sign in to comment.