diff --git a/Classes/Command/CleanupCommand.php b/Classes/Command/RemoveAllCommand.php similarity index 76% rename from Classes/Command/CleanupCommand.php rename to Classes/Command/RemoveAllCommand.php index 054fb31..38306ab 100644 --- a/Classes/Command/CleanupCommand.php +++ b/Classes/Command/RemoveAllCommand.php @@ -4,19 +4,17 @@ namespace Wrm\Events\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Core\Bootstrap; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; - use Wrm\Events\Service\CleanupService; -class CleanupCommand extends Command { - +class RemoveAllCommand extends Command +{ public function configure() { - $this->setDescription('Cleanup Events'); - $this->setHelp('Events are cleaned up'); + $this->setDescription('Remove all event data'); + $this->setHelp('All events and associated data will be removed.'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -25,6 +23,6 @@ class CleanupCommand extends Command { return GeneralUtility::makeInstance(ObjectManager::class) ->get(CleanupService::class) - ->doClean(); + ->deleteAllData(); } -} \ No newline at end of file +} diff --git a/Classes/Command/RemovePastCommand.php b/Classes/Command/RemovePastCommand.php new file mode 100644 index 0000000..c661c39 --- /dev/null +++ b/Classes/Command/RemovePastCommand.php @@ -0,0 +1,28 @@ +setDescription('Remove past events'); + $this->setHelp('Past dates are removed, together with events that do not have any left dates.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + Bootstrap::initializeBackendAuthentication(); + + return GeneralUtility::makeInstance(ObjectManager::class) + ->get(CleanupService::class) + ->deletePastData(); + } +} diff --git a/Classes/Service/Cleanup/Database.php b/Classes/Service/Cleanup/Database.php new file mode 100644 index 0000000..93a04bd --- /dev/null +++ b/Classes/Service/Cleanup/Database.php @@ -0,0 +1,131 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Database\Connection; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +class Database +{ + const DATE_TABLE = 'tx_events_domain_model_date'; + const EVENT_TABLE = 'tx_events_domain_model_event'; + const ORGANIZER_TABLE = 'tx_events_domain_model_organizer'; + + public function truncateTables(string ...$tableNames): void + { + foreach ($tableNames as $tableName) { + GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable($tableName) + ->truncate($tableName); + } + } + + public function getDeletionStructureForEvents(): array + { + $dataStructure = [static::EVENT_TABLE => []]; + + foreach ($this->getAllRecords(static::EVENT_TABLE) as $recordToDelete) { + $dataStructure[static::EVENT_TABLE][$recordToDelete] = ['delete' => 1]; + } + + return $dataStructure; + } + + private function getAllRecords(string $tableName): array + { + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable($tableName) + ->createQueryBuilder(); + + $records = $queryBuilder->select('uid') + ->from($tableName) + ->execute() + ->fetchAll(); + + return array_map(function (array $record) { + return $record['uid']; + }, $records); + } + + public function getPastDates(): array + { + $midnightToday = new \DateTimeImmutable('midnight today'); + + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable(static::DATE_TABLE) + ->createQueryBuilder(); + + $queryBuilder->getRestrictions()->removeAll(); + + $records = $queryBuilder->select('uid') + ->from(static::DATE_TABLE) + ->where($queryBuilder->expr()->lte( + 'end', + $queryBuilder->createNamedParameter($midnightToday->format('Y-m-d H:i:s')) + )) + ->execute() + ->fetchAll(); + + return array_map(function (array $record) { + return $record['uid']; + }, $records); + } + + public function deleteDates(int ...$uids) + { + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable(static::DATE_TABLE); + + $queryBuilder->delete(static::DATE_TABLE) + ->where('uid in (:uids)') + ->setParameter(':uids', $uids, Connection::PARAM_INT_ARRAY) + ->execute(); + } + + public function getDeletionStructureForEventsWithoutDates(): array + { + $dataStructure = [static::EVENT_TABLE => []]; + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable(static::EVENT_TABLE) + ->createQueryBuilder(); + + $queryBuilder->getRestrictions()->removeAll(); + + $records = $queryBuilder->select('event.uid') + ->from(static::EVENT_TABLE, 'event') + ->leftJoin('event', static::DATE_TABLE, 'date', $queryBuilder->expr()->eq('date.event', 'event.uid')) + ->where($queryBuilder->expr()->isNull('date.uid')) + ->execute() + ->fetchAll(); + + foreach ($records as $record) { + $dataStructure[static::EVENT_TABLE][$record['uid']] = ['delete' => 1]; + } + return $dataStructure; + } +} diff --git a/Classes/Service/Cleanup/Files.php b/Classes/Service/Cleanup/Files.php new file mode 100644 index 0000000..367e47d --- /dev/null +++ b/Classes/Service/Cleanup/Files.php @@ -0,0 +1,115 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Database\Connection; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +class Files +{ + public function deleteAll() + { + $this->delete($this->getFilesFromDb()); + } + + public function deleteDangling() + { + $this->delete($this->getFilesFromDb(function (QueryBuilder $queryBuilder) { + $queryBuilder->leftJoin( + 'file', + 'sys_file_reference', + 'reference', + $queryBuilder->expr()->eq('file.uid', $queryBuilder->quoteIdentifier('reference.uid_local')) + ); + $queryBuilder->andWhere( + $queryBuilder->expr()->orX( + $queryBuilder->expr()->isNull('reference.uid'), + $queryBuilder->expr()->eq('reference.deleted', 1), + $queryBuilder->expr()->eq('reference.hidden', 1) + ) + ); + })); + } + + private function delete(array $filesToDelete) + { + $uidsToRemove = []; + + foreach ($filesToDelete as $fileToDelete) { + $this->deleteFromFal($fileToDelete['storage'], $fileToDelete['identifier']); + $uidsToRemove[] = $fileToDelete['uid']; + } + + $this->deleteFromDb(... $uidsToRemove); + } + + private function getFilesFromDb(callable $whereGenerator = null): array + { + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file'); + + $queryBuilder->getRestrictions()->removeAll(); + + $queryBuilder->select('file.identifier', 'file.storage', 'file.uid') + ->from('sys_file', 'file') + ->where($queryBuilder->expr()->like( + 'file.identifier', + $queryBuilder->createNamedParameter('/staedte/%/events/%') + )); + + if ($whereGenerator !== null) { + $whereGenerator($queryBuilder); + } + + return $queryBuilder->execute()->fetchAll(); + } + + private function deleteFromFal(int $storageUid, string $filePath) + { + /* @var ResourceStorage $storage */ + $storage = GeneralUtility::makeInstance(StorageRepository::class) + ->findByUid($storageUid); + + if ($storage->hasFile($filePath) === false) { + return; + } + + $storage->deleteFile($storage->getFile($filePath)); + } + + private function deleteFromDb(int ...$uids) + { + /* @var QueryBuilder $queryBuilder */ + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file'); + + $queryBuilder->delete('sys_file') + ->where('uid in (:uids)') + ->setParameter(':uids', $uids, Connection::PARAM_INT_ARRAY) + ->execute(); + } +} diff --git a/Classes/Service/CleanupService.php b/Classes/Service/CleanupService.php index 88afad6..d5dbb41 100644 --- a/Classes/Service/CleanupService.php +++ b/Classes/Service/CleanupService.php @@ -2,39 +2,48 @@ namespace Wrm\Events\Service; +use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Log\LogManager; -use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; -use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; -use TYPO3\CMS\Extbase\Object\ObjectManager; +use Wrm\Events\Service\Cleanup\Database; +use Wrm\Events\Service\Cleanup\Files; -class CleanupService { +class CleanupService +{ + /** + * @var Database + */ + private $database; /** - * Cleanup Service constructor. - * @param ConfigurationManager $configurationManager - * @param ObjectManager $objectManager + * @var Files */ - public function __construct( - ConfigurationManager $configurationManager, - ObjectManager $objectManager - ) { + private $files; - // Get Typoscript Settings - $this->settings = $this->configurationManager->getConfiguration( - ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, - 'Events', - 'Pi1' - ); - - - $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__); - $this->logger->info('Event Cleanup Service'); + public function __construct(Database $database, Files $files) + { + $this->database = $database; + $this->files = $files; } - public function doCleanup() { - - // To be done - // Hmpf + public function deleteAllData() + { + $this->database->truncateTables(... [Database::DATE_TABLE, Database::ORGANIZER_TABLE]); + $this->removeViaDataHandler($this->database->getDeletionStructureForEvents()); + $this->files->deleteAll(); } -} \ No newline at end of file + + public function deletePastData() + { + $this->database->deleteDates(... $this->database->getPastDates()); + $this->removeViaDataHandler($this->database->getDeletionStructureForEventsWithoutDates()); + $this->files->deleteDangling(); + } + + private function removeViaDataHandler(array $structure) + { + /* @var DataHandler $dataHandler */ + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start([], $structure); + $dataHandler->process_cmdmap(); + } +} diff --git a/Configuration/Commands.php b/Configuration/Commands.php index 183f817..91b6d7f 100644 --- a/Configuration/Commands.php +++ b/Configuration/Commands.php @@ -2,5 +2,11 @@ return [ 'events:destinationdataimport‚' => [ 'class' => \Wrm\Events\Command\DestinationDataImportCommand::class - ] -]; \ No newline at end of file + ], + 'events:removeAll' => [ + 'class' => \Wrm\Events\Command\RemoveAllCommand::class + ], + 'events:removePast' => [ + 'class' => \Wrm\Events\Command\RemovePastCommand::class + ], +]; diff --git a/ext_tables.sql b/ext_tables.sql index 73c3c73..dfa2b6e 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -28,6 +28,7 @@ CREATE TABLE tx_events_domain_model_event ( organizer int(11) unsigned DEFAULT '0', region int(11) unsigned DEFAULT '0', + KEY dataHandler (l10n_parent, t3ver_oid, deleted, t3ver_wsid, t3ver_state) ); # @@ -44,6 +45,7 @@ CREATE TABLE tx_events_domain_model_organizer ( web varchar(255) DEFAULT '' NOT NULL, email varchar(255) DEFAULT '' NOT NULL, + KEY dataHandler (l10n_parent, sys_language_uid, deleted) ); # @@ -56,6 +58,8 @@ CREATE TABLE tx_events_domain_model_date ( start datetime DEFAULT NULL, end datetime DEFAULT NULL, + KEY event (event), + KEY dataHandler (event, t3ver_wsid, pid) ); #