1
|
|
|
<?php |
2
|
|
|
namespace Fab\Media\Index; |
3
|
|
|
|
4
|
|
|
/* |
5
|
|
|
* This file is part of the Fab/Media project under GPLv2 or later. |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please read the |
8
|
|
|
* LICENSE.md file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
use Fab\Media\Resource\FileReferenceService; |
12
|
|
|
use TYPO3\CMS\Core\Database\ConnectionPool; |
13
|
|
|
use TYPO3\CMS\Core\Database\Query\QueryBuilder; |
14
|
|
|
use TYPO3\CMS\Core\Resource\ResourceFactory; |
15
|
|
|
use TYPO3\CMS\Core\Resource\ResourceStorage; |
16
|
|
|
use TYPO3\CMS\Core\SingletonInterface; |
17
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* A class providing indexing service for Media/ |
21
|
|
|
*/ |
22
|
|
|
class IndexAnalyser implements SingletonInterface |
23
|
|
|
{ |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Return missing file for a given storage. |
27
|
|
|
* |
28
|
|
|
* @param ResourceStorage $storage |
29
|
|
|
* @return array |
30
|
|
|
*/ |
31
|
|
|
public function searchForMissingFiles(ResourceStorage $storage) |
32
|
|
|
{ |
33
|
|
|
|
34
|
|
|
$query = $this->getDatabaseConnection()->SELECTquery('*', 'sys_file', 'storage = ' . $storage->getUid()); |
35
|
|
|
$resource = $this->getDatabaseConnection()->sql_query($query); |
36
|
|
|
|
37
|
|
|
$missingFiles = []; |
38
|
|
|
while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($resource)) { |
39
|
|
|
|
40
|
|
|
// This task is very memory consuming on large data set e.g > 20'000 records. |
41
|
|
|
// We must think of having a pagination if there is the need for such thing. |
42
|
|
|
$file = ResourceFactory::getInstance()->getFileObject($row['uid'], $row); |
43
|
|
|
if (!$file->exists()) { |
44
|
|
|
$missingFiles[] = $file; |
45
|
|
|
} |
46
|
|
|
} |
47
|
|
|
return $missingFiles; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Deletes all missing files for a given storage. |
52
|
|
|
* |
53
|
|
|
* @param ResourceStorage $storage |
54
|
|
|
* @return array |
55
|
|
|
* @throws \InvalidArgumentException |
56
|
|
|
*/ |
57
|
|
|
public function deleteMissingFiles(ResourceStorage $storage) |
58
|
|
|
{ |
59
|
|
|
/** @var FileReferenceService $fileReferenceService */ |
60
|
|
|
$fileReferenceService = GeneralUtility::makeInstance(FileReferenceService::class); |
61
|
|
|
$missingFiles = $this->searchForMissingFiles($storage); |
62
|
|
|
$deletedFiles = []; |
63
|
|
|
|
64
|
|
|
/** @var \TYPO3\CMS\Core\Resource\File $missingFile */ |
65
|
|
|
foreach ($missingFiles as $missingFile) { |
66
|
|
|
try { |
67
|
|
|
// if missingFile has no file references |
68
|
|
|
if ($missingFile && count($fileReferenceService->findFileReferences($missingFile)) === 0) { |
69
|
|
|
// The case is special as we have a missing file in the file system |
70
|
|
|
// As a result, we can't use $fileObject->delete(); which will |
71
|
|
|
// raise exception "Error while fetching permissions" |
72
|
|
|
$this->getDatabaseConnection()->exec_DELETEquery('sys_file', 'uid = ' . $missingFile->getUid()); |
73
|
|
|
$deletedFiles[$missingFile->getUid()] = $missingFile->getIdentifier(); |
74
|
|
|
} |
75
|
|
|
} catch (\Exception $e) { |
76
|
|
|
continue; |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
return $deletedFiles; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/* |
83
|
|
|
* Return duplicates file records |
84
|
|
|
* |
85
|
|
|
* @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage |
86
|
|
|
* @return array |
87
|
|
|
*/ |
88
|
|
View Code Duplication |
public function searchForDuplicateIdentifiers(ResourceStorage $storage) |
|
|
|
|
89
|
|
|
{ |
90
|
|
|
|
91
|
|
|
// Detect duplicate records. |
92
|
|
|
$query = "SELECT identifier FROM sys_file WHERE storage = {$storage->getUid()} GROUP BY identifier, storage Having COUNT(*) > 1"; |
93
|
|
|
$resource = $this->getDatabaseConnection()->sql_query($query); |
94
|
|
|
$duplicates = []; |
95
|
|
|
while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($resource)) { |
96
|
|
|
$clause = sprintf('identifier = "%s" AND storage = %s', $row['identifier'], $storage->getUid()); |
97
|
|
|
$records = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'sys_file', $clause); |
98
|
|
|
$duplicates[$row['identifier']] = $records; |
99
|
|
|
} |
100
|
|
|
return $duplicates; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Return duplicates file records |
105
|
|
|
* |
106
|
|
|
* @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage |
107
|
|
|
* @return array |
108
|
|
|
*/ |
109
|
|
View Code Duplication |
public function searchForDuplicateSha1(ResourceStorage $storage) |
|
|
|
|
110
|
|
|
{ |
111
|
|
|
|
112
|
|
|
// Detect duplicate records. |
113
|
|
|
$query = "SELECT sha1 FROM sys_file WHERE storage = {$storage->getUid()} GROUP BY sha1, storage Having COUNT(*) > 1"; |
114
|
|
|
$resource = $this->getDatabaseConnection()->sql_query($query); |
115
|
|
|
$duplicates = []; |
116
|
|
|
while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($resource)) { |
117
|
|
|
$clause = sprintf('sha1 = "%s" AND storage = %s', $row['sha1'], $storage->getUid()); |
118
|
|
|
$records = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'sys_file', $clause); |
119
|
|
|
$duplicates[$row['sha1']] = $records; |
120
|
|
|
} |
121
|
|
|
return $duplicates; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @param string $tableName |
126
|
|
|
* @return object|QueryBuilder |
127
|
|
|
*/ |
128
|
|
|
protected function getQueryBuilder($tableName): QueryBuilder |
129
|
|
|
{ |
130
|
|
|
/** @var ConnectionPool $connectionPool */ |
131
|
|
|
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); |
132
|
|
|
return $connectionPool->getQueryBuilderForTable($tableName); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.