Skip to content

Commit 6a6dae2

Browse files
author
Mathis Koblin
committed
Added interactive mode and moved metadata handling in its own class
1 parent bae4463 commit 6a6dae2

3 files changed

Lines changed: 384 additions & 240 deletions

File tree

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ElementareTeilchen\Unduplicator\Command;
6+
7+
use Doctrine\DBAL\Exception;
8+
use Symfony\Component\Console\Input\InputInterface;
9+
use Symfony\Component\Console\Style\SymfonyStyle;
10+
use TYPO3\CMS\Core\Database\ConnectionPool;
11+
12+
class MetadataUpdateHandler
13+
{
14+
15+
/**
16+
* @var String|bool
17+
*/
18+
private $force = false;
19+
20+
public function __construct(
21+
private bool $dryRun,
22+
private InputInterface $input,
23+
private SymfonyStyle $output,
24+
private array $fieldsToCheck,
25+
private ConnectionPool $connectionPool
26+
) {
27+
$this->force = $input->getOption("force"); // force will be null if no value is passed -> overwrite
28+
if ($this->force === null) {
29+
$this->force = "overwrite";
30+
}
31+
}
32+
33+
34+
/**
35+
* @param int $masterFileUid
36+
* @param int $oldFileUid
37+
* @return bool
38+
* @throws Exception
39+
*/
40+
public function updateMetadata(int $masterFileUid, array $masterMetadataRecords, int $oldFileUid, array $oldMetadataRecords): bool
41+
{
42+
43+
$deleteRecords = true;
44+
// iterate over all languages
45+
foreach ($oldMetadataRecords as $oldMetadata) {
46+
47+
$masterMetadata = [];
48+
foreach ($masterMetadataRecords as $metadata) {
49+
if ($metadata['sys_language_uid'] === $oldMetadata['sys_language_uid']) {
50+
$masterMetadata = $metadata;
51+
break;
52+
}
53+
}
54+
55+
$metadataObject = new MetadataUpdateObject(
56+
$masterFileUid,
57+
$masterMetadata,
58+
$oldMetadata,
59+
$this->fieldsToCheck
60+
);
61+
62+
$deleteRecords = $this->updateMetadataRecord($metadataObject) && $deleteRecords;
63+
}
64+
65+
return $deleteRecords;
66+
}
67+
68+
69+
/**
70+
* @param MetadataUpdateObject $metadata
71+
* @return bool
72+
*/
73+
public function updateMetadataRecord(MetadataUpdateObject $metadata): bool
74+
{
75+
if ($metadata->isOldEmtpyt() || $metadata->isOldSameAsMaster()) { // check if record is empty or if the values are the same as in master
76+
77+
if ($this->output->isVerbose()) {
78+
$this->output->writeln("\t<info>Old metadata " . $oldUid . " is empty or same as in master for sys_language_uid " . $languageUid . "</info>");
79+
}
80+
81+
$this->metadataHandleNoUpdate($metadata->getLanguageUid(), $metadata->getOldUid());
82+
83+
} elseif (!$metadata->isOldEmtpyt() && ($metadata->isMasterEmpty() || $this->force !== false)) { // check if master record has metadata, if not, copy the old ones
84+
85+
if ($this->output->isVerbose()) {
86+
if ($metadata->isMasterEmpty()) {
87+
$this->output->writeln("\t<info>Master metadata " . $metadata->getMasterUid() . " is empty for sys_language_uid " . $metadata->getLanguageUid() . "</info>");
88+
} else if ($this->force !== false) {
89+
if ($this->force !== "keep") {
90+
$this->output->writeln("\t<info>Force overwriting metadata in master.</info>");
91+
} else {
92+
$this->output->writeln("\t<info>Force keeping metadata in master.</info>");
93+
}
94+
}
95+
}
96+
97+
$this->metadataHandleUpdate($metadata);
98+
99+
} else {
100+
101+
return $this->metadataHandleConflict($metadata);
102+
}
103+
return true;
104+
}
105+
106+
/**
107+
* @param $languageUid
108+
* @param mixed $oldUid
109+
* @return void
110+
*/
111+
public function metadataHandleNoUpdate($languageUid, mixed $oldUid): void
112+
{
113+
if ($this->output->isVerbose()) {
114+
$this->output->writeln("\t -> <info>Deleting old metadata record</info>");
115+
}
116+
117+
if (!$this->dryRun) {
118+
$this->deleteMetadataRecord($oldUid);
119+
$this->deleteMetadataReference($oldUid);
120+
}
121+
}
122+
123+
/**
124+
* @param MetadataUpdateObject $metadata
125+
* @return void
126+
*/
127+
public function metadataHandleUpdate(MetadataUpdateObject $metadata): void
128+
{
129+
130+
if ($this->force === false ||
131+
$this->force === "overwrite" ||
132+
$metadata->isMasterEmpty() && $this->force === "keep-nonempty") {
133+
134+
if ($this->output->isVerbose()) {
135+
$this->output->writeln("\t -> <info>" . ($metadata->getMasterUid() ? "Updating" : "Creating") . " master metadata record</info>");
136+
}
137+
138+
if (!$this->dryRun) {
139+
if ($metadata->hasMaster()) {
140+
$this->updateMasterMetadata($metadata->getMasterUid(), $metadata->getOldMetadata());
141+
} else {
142+
$this->createMasterMetadata($metadata->getMasterFileUid(), $metadata->getOldMetadata());
143+
}
144+
}
145+
}
146+
147+
if ($this->output->isVerbose()) {
148+
$this->output->writeln("\t -> <info>Deleting old metadata record " . $metadata->getOldUid() . "</info>");
149+
}
150+
151+
if (!$this->dryRun) {
152+
$this->deleteMetadataRecord($metadata->getOldUid());
153+
$this->deleteMetadataReference($metadata->getOldUid());
154+
}
155+
}
156+
157+
/**
158+
* @param MetadataUpdateObject $metadata
159+
* @return false
160+
*/
161+
public function metadataHandleConflict(MetadataUpdateObject $metadata): bool
162+
{
163+
$interactive = $this->input->getOption('interactive');
164+
165+
$this->output->writeln("\t<error>Old metadata " . $metadata->getOldUid() . " with sys_language_uid " . $metadata->getLanguageUid() . " is not empty and conflicts with the master data. " . ($interactive ? "Please choose what to do." : "Not deleting this record") . ".</error>");
166+
if ($this->output->isVerbose()) {
167+
$this->output->writeln("\t -> Old metadata : <comment>" . json_encode($metadata->getOldClean()) . "</comment>");
168+
$this->output->writeln("\t -> Master metadata: <comment>" . json_encode($metadata->getMasterClean()) . "</comment>");
169+
}
170+
171+
if ($interactive) {
172+
while (true) {
173+
switch ($this->output->ask('<info>Keep OLD or MASTER metadata or SKIP record [o,m,s,?]?</info> ', '?')) {
174+
case 'o':
175+
$this->output->writeln("\t<info>Keeping OLD metadata</info>");
176+
$this->metadataHandleUpdate($metadata);
177+
return true;
178+
case 'm':
179+
$this->output->writeln("\t<info>Keeping MASTER metadata</info>");
180+
$this->metadataHandleNoUpdate($metadata->getLanguageUid(), $metadata->getOldUid());
181+
return true;
182+
case 's':
183+
$this->output->writeln("\t<info>Skipping record. Not deleting any duplicate records related to file " . $metadata->getMasterFileUid() . "</info>");
184+
return false;
185+
case 'h':
186+
default:
187+
$this->output->text([
188+
' o - keep OLD metadata record',
189+
' m - keep MASTER metadata record',
190+
' s - SKIP handling of record for now',
191+
' ? - HELP',
192+
]);
193+
break;
194+
}
195+
}
196+
}
197+
198+
return false;
199+
}
200+
201+
202+
private function updateMasterMetadata(int $masterMetadataUid, array $metadata)
203+
{
204+
$metadataUpdateQueryBuilder = $this->connectionPool->getQueryBuilderForTable("sys_file_metadata");
205+
$metadataUpdateQueryBuilder->update("sys_file_metadata");
206+
foreach ($this->fieldsToCheck as $field) {
207+
if (!isset($metadata[$field])) {
208+
$this->output->writeln("\t\t<warning>Field \'" . $field . "\' does not exist</warning>");
209+
continue;
210+
}
211+
$metadataUpdateQueryBuilder->set($field, $metadata[$field]);
212+
}
213+
$metadataUpdateQueryBuilder->where(
214+
$metadataUpdateQueryBuilder->expr()->eq(
215+
'uid',
216+
$metadataUpdateQueryBuilder->createNamedParameter($masterMetadataUid, \PDO::PARAM_INT)
217+
)
218+
)
219+
->executeStatement();
220+
}
221+
222+
private function createMasterMetadata(int $masterFileUid, array $metadata)
223+
{
224+
unset($metadata['uid']);
225+
$metadata['file'] = $masterFileUid;
226+
$metadataUpdateQueryBuilder = $this->connectionPool->getQueryBuilderForTable("sys_file_metadata");
227+
$metadataUpdateQueryBuilder->insert("sys_file_metadata")
228+
->values($metadata)
229+
->executeStatement();
230+
}
231+
232+
/**
233+
* @param int $uid
234+
* @return mixed
235+
*/
236+
public function deleteMetadataRecord(int $uid)
237+
{
238+
$queryBuilder = $this->connectionPool->getQueryBuilderForTable("sys_file_metadata");
239+
return $queryBuilder
240+
->delete("sys_file_metadata")
241+
->where(
242+
$queryBuilder->expr()->eq(
243+
"uid",
244+
$queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
245+
)
246+
)
247+
->executeStatement();
248+
}
249+
250+
private function deleteMetadataReference(int $uid)
251+
{
252+
$referenceDeleteQueryBuilder = $this->connectionPool->getQueryBuilderForTable("sys_refindex");
253+
$referenceDeleteExpr = $referenceDeleteQueryBuilder->expr();
254+
$referenceDeleteQueryBuilder
255+
->delete("sys_refindex")
256+
->where(
257+
$referenceDeleteExpr->eq(
258+
"tablename",
259+
$referenceDeleteQueryBuilder->createNamedParameter("sys_file_metadata", \PDO::PARAM_STR)
260+
),
261+
$referenceDeleteExpr->eq(
262+
"recuid",
263+
$referenceDeleteQueryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
264+
)
265+
)->executeStatement();
266+
}
267+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ElementareTeilchen\Unduplicator\Command;
6+
7+
class MetadataUpdateObject
8+
{
9+
10+
public function __construct(
11+
public readonly int $masterFileUid,
12+
public readonly array $masterMetadata,
13+
public readonly array $oldMetadata,
14+
public readonly array $fieldsToCheck
15+
) {
16+
}
17+
18+
public function getOldMetadata(): array
19+
{
20+
return $this->oldMetadata;
21+
}
22+
23+
public function getMasterFileUid(): int
24+
{
25+
return $this->masterFileUid;
26+
}
27+
28+
public function getMasterUid(): int|null
29+
{
30+
return $this->masterMetadata['uid'] ?? null;
31+
}
32+
33+
public function getOldUid(): int
34+
{
35+
return $this->oldMetadata['uid'];
36+
}
37+
38+
public function getLanguageUid(): int
39+
{
40+
return $this->oldMetadata['sys_language_uid'];
41+
}
42+
43+
public function isOldEmtpyt(): bool
44+
{
45+
return empty($this->getOldClean());
46+
}
47+
48+
public function isMasterEmpty(): bool
49+
{
50+
return empty($this->getMasterClean());
51+
}
52+
53+
public function hasMaster(): bool
54+
{
55+
return $this->getMasterUid() !== null;
56+
}
57+
58+
public function isOldSameAsMaster(): bool
59+
{
60+
return $this->getOldClean() === $this->getMasterClean();
61+
}
62+
63+
public function getMasterClean(): array
64+
{
65+
return $this->clearMetadataRecord($this->masterMetadata);
66+
}
67+
68+
public function getOldClean(): array
69+
{
70+
return $this->clearMetadataRecord($this->oldMetadata);
71+
}
72+
73+
private function clearMetadataRecord(array $metadata): array
74+
{
75+
// unset all fields that are not in $this->fieldsToCheck
76+
foreach ($metadata as $key => $value) {
77+
if (!in_array($key, $this->fieldsToCheck) || empty($value)) {
78+
unset($metadata[$key]);
79+
}
80+
}
81+
return $metadata;
82+
}
83+
}

0 commit comments

Comments
 (0)