Add update wizard to convert thumbnail field to FAL reference

This commit is contained in:
Alexander Bigga 2021-11-24 15:25:06 +01:00
parent a7ddc313dd
commit 6db98429eb
6 changed files with 336 additions and 7 deletions

View File

@ -0,0 +1,318 @@
<?php
/**
* (c) Kitodo. Key to digital objects e.V. <contact@kitodo.org>
*
* This file is part of the Kitodo and TYPO3 projects.
*
* @license GNU General Public License version 3 or later.
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
namespace Kitodo\Dlf\Updates;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
use TYPO3\CMS\Install\Updates\ChattyInterface;
use Doctrine\DBAL\DBALException;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Resource\StorageRepository;
/**
* Migrate reference of thumbnail image in collections record.
*/
class FileLocationUpdater implements UpgradeWizardInterface, ChattyInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* @var OutputInterface
*/
protected $output;
/**
* @var ResourceStorage
*/
protected $storage;
/**
* @var Logger
*/
protected $logger;
/**
* Array with table and fields to migrate
*
* @var string
*/
protected $fieldsToMigrate = [
'tx_dlf_collections' => 'thumbnail'
];
/**
* @return string Unique identifier of this updater
*/
public function getIdentifier(): string
{
return 'dlfFileLocationUpdater';
}
/**
* Return the speaking name of this wizard
*
* @return string
*/
public function getTitle(): string
{
return 'Migrate file references used in EXT:dlf';
}
/**
* Get description
*
* @return string Longer description of this updater
*/
public function getDescription(): string
{
return 'Convert file reference of thumbnail in in collection records.';
}
/**
* Is an update necessary?
*
* Is used to determine whether a wizard needs to be run.
* Check if data for migration exists.
*
* @return bool
*/
public function updateNecessary(): bool
{
$numRecords = $this->getRecordsFromTable(true);
if ($numRecords > 0) {
return true;
}
return false;
}
/**
* @return string[] All new fields and tables must exist
*/
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}
/**
* @param OutputInterface $output
*/
public function setOutput(OutputInterface $output): void
{
$this->output = $output;
}
/**
* Execute the update
*
* Called when a wizard reports that an update is necessary
*
* @return bool
*/
public function executeUpdate(): bool
{
$result = true;
try {
$numRecords = $this->getRecordsFromTable(true);
if ($numRecords > 0) {
$this->performUpdate();
}
} catch (\Exception $e) {
// If something goes wrong, migrateField() logs an error
$result = false;
}
return $result;
}
/**
* Get records from table where the field to migrate is not empty (NOT NULL and != '')
* and also not numeric (which means that it is migrated)
*
* Work based on BackendLayoutIconUpdateWizard::class
*
* @return array|int
* @throws \RuntimeException
*/
protected function getRecordsFromTable($countOnly = false)
{
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$allResults = [];
$numResults = 0;
foreach(array_keys($this->fieldsToMigrate) as $table) {
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()->removeAll();
try {
$result = $queryBuilder
->select('uid', 'pid', $this->fieldsToMigrate[$table])
->from($table)
->where(
$queryBuilder->expr()->isNotNull($this->fieldsToMigrate[$table]),
$queryBuilder->expr()->neq(
$this->fieldsToMigrate[$table],
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
),
$queryBuilder->expr()->comparison(
'CAST(CAST(' . $queryBuilder->quoteIdentifier($this->fieldsToMigrate[$table]) . ' AS DECIMAL) AS CHAR)',
ExpressionBuilder::NEQ,
'CAST(' . $queryBuilder->quoteIdentifier($this->fieldsToMigrate[$table]) . ' AS CHAR)'
)
)
->orderBy('uid')
->execute()
->fetchAll();
if ($countOnly === true) {
$numResults += count($result);
} else {
$allResults[$table] = $result;
}
} catch (DBALException $e) {
throw new \RuntimeException(
'Database query failed. Error was: ' . $e->getPrevious()->getMessage(),
1511950673
);
}
}
if ($countOnly === true) {
return $numResults;
} else {
return $allResults;
}
}
/**
* Performs the database update.
*
* @return bool TRUE on success, FALSE on error
*/
protected function performUpdate(): bool
{
$result = true;
$title = "Perform Update";
try {
$storages = GeneralUtility::makeInstance(StorageRepository::class)->findAll();
$this->storage = $storages[0];
$records = $this->getRecordsFromTable();
foreach ($records as $table => $recordsInTable) {
foreach ($recordsInTable as $record) {
$this->migrateField($table, $record);
}
}
} catch (\Exception $e) {
$result = false;
}
return $result;
}
/**
* Migrates a single field.
*
* @param string $table
* @param array $row
* @throws \Exception
*/
protected function migrateField($table, $row)
{
$fieldItem = trim($row[$this->fieldsToMigrate[$table]]);
if (empty($fieldItem) || is_numeric($fieldItem)) {
return;
}
$fileadminDirectory = rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/';
$i = 0;
$storageUid = (int)$this->storage->getUid();
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$fileUid = null;
$sourcePath = Environment::getPublicPath() . '/' . $fieldItem;
// maybe the file was already moved, so check if the original file still exists
if (file_exists($sourcePath)) {
$title = 'Migrate field ' . $sourcePath;
// see if the file already exists in the storage
$fileSha1 = sha1_file($sourcePath);
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file');
$existingFileRecord = $queryBuilder->select('uid')->from('sys_file')->where(
$queryBuilder->expr()->eq(
'missing',
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
'sha1',
$queryBuilder->createNamedParameter($fileSha1, \PDO::PARAM_STR)
),
$queryBuilder->expr()->eq(
'storage',
$queryBuilder->createNamedParameter($storageUid, \PDO::PARAM_INT)
)
)->execute()->fetch();
// the file exists
if (is_array($existingFileRecord)) {
$fileUid = $existingFileRecord['uid'];
}
}
if ($fileUid > 0) {
$fields = [
'fieldname' => $this->fieldsToMigrate[$table],
'table_local' => 'sys_file',
'pid' => ($table === 'pages' ? $row['uid'] : $row['pid']),
'uid_foreign' => $row['uid'],
'uid_local' => $fileUid,
'tablenames' => $table,
'crdate' => time(),
'tstamp' => time(),
'sorting_foreign' => $i,
];
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_reference');
$result = $queryBuilder
->insert('sys_file_reference')
->values($fields)
->execute();
++$i;
}
// Update referencing table's original field to now contain the count of references,
// but only if all new references could be set
if ($i === 1) {
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
$queryBuilder->update($table)->where(
$queryBuilder->expr()->eq(
'uid',
$queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT)
)
)->set($this->fieldsToMigrate[$table], $i)->execute();
}
}
}

View File

@ -125,7 +125,7 @@
</numIndex>
</foreign_types>
<foreign_match_fields>
<fieldname>stylesheet</fieldname> <!-- CAUTION!! Replace "fal" with the variable name of this field! -->
<fieldname>stylesheet</fieldname>
</foreign_match_fields>
<appearance type="array">
<newRecordLinkAddTitle>1</newRecordLinkAddTitle>

View File

@ -61,7 +61,7 @@
</numIndex>
</foreign_types>
<foreign_match_fields>
<fieldname>image</fieldname> <!-- CAUTION!! Replace "fal" with the variable name of this field! -->
<fieldname>image</fieldname>
</foreign_match_fields>
<appearance type="array">
<newRecordLinkAddTitle>1</newRecordLinkAddTitle>

View File

@ -173,11 +173,20 @@ return [
'exclude' => 1,
'l10n_mode' => 'exclude',
'label' => 'LLL:EXT:dlf/Resources/Private/Language/Labels.xml:tx_dlf_collections.thumbnail',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig('image', [
'appearance' => [
'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference'
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
'image',
[
'appearance' => [
'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference'
],
'foreign_match_fields' => [
'fieldname' => 'thumbnail',
'tablenames' => 'tx_dlf_collections',
'table_local' => 'sys_file',
],
],
], $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'])
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
)
],
'priority' => [
'exclude' => 1,

View File

@ -106,6 +106,8 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][] = [
// Add migration wizards
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\Kitodo\Dlf\Updates\MigrateSettings::class]
= \Kitodo\Dlf\Updates\MigrateSettings::class;
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\Kitodo\Dlf\Updates\FileLocationUpdater::class]
= \Kitodo\Dlf\Updates\FileLocationUpdater::class;
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Kitodo.Dlf',

View File

@ -201,7 +201,7 @@ CREATE TABLE tx_dlf_collections (
index_search text NOT NULL,
oai_name varchar(255) DEFAULT '' NOT NULL,
description text NOT NULL,
thumbnail int(11) NOT NULL,
thumbnail text NOT NULL,
priority smallint(6) DEFAULT '3' NOT NULL,
documents int(11) DEFAULT '0' NOT NULL,
owner int(11) DEFAULT '0' NOT NULL,