Skip to content

Commit

Permalink
[TASK] provide another sorting fix
Browse files Browse the repository at this point in the history
This command maybe helpful when migrating to EXT:container
from any other grid extension and have inconsistent sorting
on page, e.g. container is sorted before previous
container child

fixes: #422
  • Loading branch information
achimfritz-b13 authored and achimfritz committed Mar 28, 2024
1 parent 4a1654c commit 92b6ed0
Show file tree
Hide file tree
Showing 11 changed files with 393 additions and 2 deletions.
68 changes: 68 additions & 0 deletions Classes/Command/SortingInPageCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace B13\Container\Command;

/*
* This file is part of TYPO3 CMS-based extension "container" by b13.
*
* 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.
*/

use B13\Container\Integrity\SortingInPage;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Core\Bootstrap;

class SortingInPageCommand extends Command
{
/**
* @var SortingInPage
*/
protected $sorting;

protected function configure()
{
$this->addArgument('pid', InputArgument::OPTIONAL, 'limit to this pid', 0);
$this->addOption('apply', null, InputOption::VALUE_NONE, 'apply migration');
$this->addOption(
'enable-logging',
null,
InputOption::VALUE_NONE,
'enables datahandler logging, should only use for debug issues, not in production'
);
}

public function __construct(SortingInPage $sorting, string $name = null)
{
parent::__construct($name);
$this->sorting = $sorting;
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$dryrun = $input->getOption('apply') !== true;
$pid = (int)$input->getArgument('pid');

Bootstrap::initializeBackendAuthentication();
Bootstrap::initializeLanguageObject();
$errors = $this->sorting->run(
$dryrun,
$input->getOption('enable-logging'),
$pid
);
foreach ($errors as $error) {
$output->writeln($error);
}
if (empty($errors)) {
$output->writeln('migration finished');
}
return 0;
}
}
48 changes: 48 additions & 0 deletions Classes/Integrity/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,54 @@ public function getChildrenByContainerAndColPos(int $containerId, int $colPos, i
return (array)$stm->fetchAllAssociative();
}

public function getNonContainerChildrenPerColPos(array $containerUsedColPosArray, ?int $pid = null): array
{
$queryBuilder = $this->getQueryBuilder();
$stm = $queryBuilder
->select(...$this->fields)
->from('tt_content')
->where(
$queryBuilder->expr()->notIn(
'colPos',
$queryBuilder->createNamedParameter($containerUsedColPosArray, Connection::PARAM_INT_ARRAY)
),
$queryBuilder->expr()->eq(
'sys_language_uid',
$queryBuilder->createNamedParameter(0, Connection::PARAM_INT)
)
);
if (!empty($pid)) {
$stm->andWhere(
$queryBuilder->expr()->eq(
'pid',
$queryBuilder->createNamedParameter($pid, Connection::PARAM_INT)
)
);
}
$stm->orderBy('pid');
$stm->addOrderBy('colPos');
$stm->addOrderBy('sorting');
if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() >= 12) {
$stm = $stm->executeQuery();
} else {
$stm = $stm->execute();
}
if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() === 10) {
$results = $stm->fetchAll();
} else {
$results = $stm->fetchAllAssociative();
}
$rows = [];
foreach ($results as $result) {
$key = $result['pid'] . '-' . $result['colPos'];
if (!isset($rows[$key])) {
$rows[$key] = [];
}
$rows[$key][$result['uid']] = $result;
}
return $rows;
}

public function getContainerRecords(array $cTypes, ?int $pid = null): array
{
$queryBuilder = $this->getQueryBuilder();
Expand Down
135 changes: 135 additions & 0 deletions Classes/Integrity/SortingInPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

declare(strict_types=1);

namespace B13\Container\Integrity;

/*
* This file is part of TYPO3 CMS-based extension "container" by b13.
*
* 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.
*/

use B13\Container\Domain\Factory\ContainerFactory;
use B13\Container\Domain\Model\Container;
use B13\Container\Domain\Service\ContainerService;
use B13\Container\Tca\Registry;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class SortingInPage implements SingletonInterface
{
/**
* @var Database
*/
protected $database;

/**
* @var Registry
*/
protected $tcaRegistry;

/**
* @var ContainerFactory
*/
protected $containerFactory;

/**
* @var ContainerService
*/
protected $containerService;

protected $errors = [];

public function __construct(Database $database, Registry $tcaRegistry, ContainerFactory $containerFactory, ContainerService $containerService)
{
$this->database = $database;
$this->tcaRegistry = $tcaRegistry;
$this->containerFactory = $containerFactory;
$this->containerService = $containerService;
}

public function run(bool $dryRun = true, bool $enableLogging = false, ?int $pid = null): array
{
$this->unsetContentDefenderConfiguration();
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->enableLogging = $enableLogging;
$cTypes = $this->tcaRegistry->getRegisteredCTypes();
$containerUsedColPosArray = [];
foreach ($cTypes as $cType) {
$columns = $this->tcaRegistry->getAvailableColumns($cType);
foreach ($columns as $column) {
$containerUsedColPosArray[] = $column['colPos'];
}
}
$rows = $this->database->getNonContainerChildrenPerColPos($containerUsedColPosArray, $pid);
foreach ($rows as $recordsPerPageAndColPos) {
$prevSorting = 0;
$prevContainer = null;
$prevChild = null;
foreach ($recordsPerPageAndColPos as $record) {
if (in_array($record['CType'], $cTypes, true)) {
$container = $this->containerFactory->buildContainer($record['uid']);
$children = $container->getChildRecords();
if (empty($children)) {
$sorting = $record['sorting'];
} else {
$lastChild = array_pop($children);
$sorting = $lastChild['sorting'];

if ($prevChild === null || $prevContainer === null) {
$prevChild = $lastChild;
$prevContainer = $container;
$prevSorting = $sorting;
continue;
}
$containerSorting = $container->getContainerRecord()['sorting'];
if ($containerSorting < $prevSorting) {
$this->errors[] = 'record ' . $record['uid'] . ' (' . $record['sorting'] . ')' .
' on page ' . $record['pid'] .
' should be sorted after last child ' . $prevChild['uid'] . ' (' . $prevChild['sorting'] . ')' .
' of container ' . $prevContainer->getUid() . ' (' . $containerSorting . ')';
$this->moveRecordAfter((int)$record['uid'], $prevContainer->getUid(), $dryRun, $dataHandler);
}
$prevContainer = $container;
$prevChild = $lastChild;
}
} else {
$sorting = $record['sorting'];
}
$prevSorting = $sorting;
}
}
return $this->errors;
}

protected function moveRecordAfter(int $recordUid, int $moveUid, bool $dryRun, DataHandler $dataHandler): void
{
if ($dryRun === false) {
$cmdmap = [
'tt_content' => [
$recordUid => [
'move' => -1 * $moveUid,
],
],
];
$dataHandler->start([], $cmdmap);
$dataHandler->process_datamap();
$dataHandler->process_cmdmap();
}
}

protected function unsetContentDefenderConfiguration(): void
{
// content_defender uses FormDataCompiler which expects a ServerRequest
if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['content_defender'])) {
unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['content_defender']);
}
if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['content_defender'])) {
unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['content_defender']);
}
}
}
6 changes: 6 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,9 @@ services:
command: 'container:sorting'
schedulable: false
description: Resort Content Elements
B13\Container\Command\SortingInPageCommand:
tags:
- name: 'console.command'
command: 'container:sorting-in-page'
schedulable: false
description: Resort Content Elements
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ vendor/bin/typo3 container:sorting
# Fix the sorting of container children on page 123
vendor/bin/typo3 container:sorting --apply 123

# Check the sorting of records in page colPos
vendor/bin/typo3 container:sorting-in-page

# ??
bin/typo3 container:fixLanguageMode
bin/typo3 container:fixContainerParentForConnectedMode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"pages"
,"uid","pid"
,1,0
"tt_content"
,"uid","pid","colPos","CType","sorting","tx_container_parent"
,1,1,0,"b13-2cols-with-header-container",1,
,2,1,0,"b13-2cols-with-header-container",2,
,3,1,202,,4,1
,4,1,202,,3,2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"pages"
,"uid","pid"
,1,0
"tt_content"
,"uid","pid","colPos","CType","sorting","tx_container_parent"
,1,1,0,"b13-2cols-with-header-container",1,
,2,1,0,"b13-2cols-with-header-container",2,
,3,1,202,,3,1
,4,1,202,,4,2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"pages"
,"uid","pid"
,1,0
"tt_content"
,"uid","pid","colPos","CType","sorting","tx_container_parent"
,1,1,0,"b13-2cols-with-header-container",1,
,2,1,0,"b13-2cols-with-header-container",3,
,3,1,202,,2,1
,4,1,202,,4,2
Loading

0 comments on commit 92b6ed0

Please sign in to comment.