Completed
Push — master ( 522461...c89cc2 )
by Fabien
02:24
created

DataHandlerHook::getDataService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Fab\Media\Hook;
4
5
/*
6
 * This file is part of the Fab/Media project under GPLv2 or later.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.md file that was distributed with this source code.
10
 */
11
12
use Fab\Vidi\Service\DataService;
13
use Fab\Vidi\Utility\BackendUtility;
14
use TYPO3\CMS\Core\DataHandling\DataHandler;
15
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
16
use TYPO3\CMS\Core\Resource\ResourceFactory;
17
use TYPO3\CMS\Core\Utility\GeneralUtility;
18
use TYPO3\CMS\Core\Utility\MathUtility;
19
20
/**
21
 * Hook Functions for the 'DataHandler'.
22
 */
23
class DataHandlerHook
24
{
25
26
    /**
27
     * Store indexed file before the Data Handler start "working".
28
     *
29
     * @var array
30
     */
31
    protected $beforeDataHandlerProcessFileIdentifiers = [];
32
33
    /**
34
     * Store indexed file after the Data Handler has done its job.
35
     *
36
     * @var array
37
     */
38
    protected $afterDataHandlerProcessFileIdentifiers = [];
39
40
    /**
41
     * Internal key for the Cache Manager.
42
     *
43
     * @var string
44
     */
45
    protected $registerKey = 'media-hook-elementsToKeepTrack';
46
47
    /**
48
     * First procedures to launch before all operations in DataHandler.
49
     *
50
     * Feed variable $this->beforeDataHandlerProcessFileIdentifiers
51
     *
52
     * @param \TYPO3\CMS\Core\DataHandling\DataHandler $caller TCEMain Object
53
     * @return void
54
     */
55
    public function processDatamap_beforeStart(DataHandler $caller)
56
    {
57
58
        // Use a register to keep track of files.
59
        // It is required according to TCEMain behaviour which register "elements to be deleted".
60
        // Those element must not be forgotten.
61
        $this->initializeFileRegister();
62
        $this->registerFilesToKeepTrack();
63
64 View Code Duplication
        foreach ($caller->datamap as $tableName => $configuration) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
65
66
            $id = key($configuration);
67
            if (!MathUtility::canBeInterpretedAsInteger($id)) {
68
                continue;
69
            }
70
71
            /** @var $refIndexObj \TYPO3\CMS\Core\Database\ReferenceIndex */
72
            $refIndexObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
73
            if (\TYPO3\CMS\Backend\Utility\BackendUtility::isTableWorkspaceEnabled($tableName)) {
74
                $refIndexObj->setWorkspaceId($caller->BE_USER->workspace);
75
            }
76
            $indexes = $refIndexObj->updateRefIndexTable($tableName, $id);
77
78
            // Make sure $index is an array.
79
            if (!is_array($indexes)) {
80
                $indexes = [];
81
            }
82
83
            $fileIdentifiers = $this->lookForFiles($indexes);
84
            $this->addBeforeDataHandlerProcessFileIdentifiers($fileIdentifiers);
85
        }
86
    }
87
88
    /**
89
     * Last procedures to launch after all operations in DataHandler.
90
     *
91
     * Process field "number_of_references" which may require updates.
92
     *
93
     * @param \TYPO3\CMS\Core\DataHandling\DataHandler $caller TCEMain Object
94
     * @return void
95
     */
96
    public function processDatamap_afterAllOperations(DataHandler $caller)
97
    {
98
99
        // First collect files which have been involved.
100 View Code Duplication
        foreach ($caller->datamap as $tableName => $configuration) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
101
102
            $id = key($configuration);
103
104
            /** @var $refIndexObj \TYPO3\CMS\Core\Database\ReferenceIndex */
105
            $refIndexObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
106
            if (\TYPO3\CMS\Backend\Utility\BackendUtility::isTableWorkspaceEnabled($tableName)) {
107
                $refIndexObj->setWorkspaceId($caller->BE_USER->workspace);
108
            }
109
            $indexes = $refIndexObj->updateRefIndexTable($tableName, $id);
110
111
            // Make sure $index is an array.
112
            if (!is_array($indexes)) {
113
                $indexes = [];
114
            }
115
116
            $fileIdentifiers = $this->lookForFiles($indexes);
117
            $this->addAfterDataHandlerProcessFileIdentifiers($fileIdentifiers);
118
        }
119
120
        // After collecting files, update the column "number_of_references".
121
        foreach ($this->getFileToProcess() as $fileIdentifier) {
122
            try {
123
                $file = ResourceFactory::getInstance()->getFileObject($fileIdentifier);
124
                $numberOfReferences = $this->getFileReferenceService()->countTotalReferences($file);
125
126
                $values = array(
127
                    'number_of_references' => $numberOfReferences
128
                );
129
                $this->getDataService()->update('sys_file', $values, ['uid' => $file->getUid()]);
130
            } catch (FileDoesNotExistException $fileDoesNotExistException) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Core\Resource\...leDoesNotExistException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
131
                // Do nothing here. A file that does not exist needs no update.
132
                // See https://github.com/fabarea/media/issues/159 for more information.
133
            }
134
        }
135
    }
136
137
    /**
138
     * @return void
139
     */
140
    protected function initializeFileRegister()
141
    {
142
143
        $items = $this->getMemoryCache()->get($this->registerKey);
144
        if (!is_array($items)) {
145
            $this->getMemoryCache()->set($this->registerKey, []);
146
        }
147
    }
148
149
    /**
150
     * @return void
151
     */
152
    protected function registerFilesToKeepTrack()
153
    {
154
        $fileIdentifiers = [];
155
        $elementsToBeDeleted = $this->getMemoryCache()->get('core-t3lib_TCEmain-elementsToBeDeleted');
156
        if (is_array($elementsToBeDeleted)) {
157
            foreach ($elementsToBeDeleted as $tableName => $element) {
158
159
                if ($tableName === 'sys_file_reference') {
160
161
                    $fileReferenceIdentifier = key($element);
162
                    if ($element[$fileReferenceIdentifier] === true) {
163
                        $fileIdentifier = $this->findFileByFileReference($fileReferenceIdentifier);
164
                        $fileIdentifiers[] = $fileIdentifier;
165
                    }
166
                }
167
            }
168
        }
169
170
        // Put back in the memory cache the value.
171
        $items = $this->getMemoryCache()->get($this->registerKey);
172
        $mergedItems = array_merge($items, $fileIdentifiers);
173
        $this->getMemoryCache()->set($this->registerKey, $mergedItems);
174
    }
175
176
    /**
177
     * @return array
178
     */
179
    protected function getRegisteredFiles()
180
    {
181
        $files = $this->getMemoryCache()->get($this->registerKey);
182
        return $files;
183
    }
184
185
    /**
186
     * Look for file which are within the reference index.
187
     *
188
     * @return array
189
     */
190
    protected function getFileToProcess()
191
    {
192
        $fileIdentifiers = array_merge(
193
            $this->beforeDataHandlerProcessFileIdentifiers,
194
            $this->afterDataHandlerProcessFileIdentifiers,
195
            $this->getRegisteredFiles()
196
        );
197
        return array_unique($fileIdentifiers);
198
    }
199
200
    /**
201
     * @param array $fileIdentifiers
202
     * @return void
203
     */
204
    protected function addBeforeDataHandlerProcessFileIdentifiers(array $fileIdentifiers)
205
    {
206
        $this->beforeDataHandlerProcessFileIdentifiers = array_merge($this->beforeDataHandlerProcessFileIdentifiers, $fileIdentifiers);
207
    }
208
209
    /**
210
     * @param array $fileIdentifiers
211
     * @return void
212
     */
213
    protected function addAfterDataHandlerProcessFileIdentifiers(array $fileIdentifiers)
214
    {
215
        $this->afterDataHandlerProcessFileIdentifiers = array_merge($this->afterDataHandlerProcessFileIdentifiers, $fileIdentifiers);
216
    }
217
218
    /**
219
     * Look for file which are within the reference index.
220
     *
221
     * @param array $indexes
222
     * @return array
223
     */
224
    protected function lookForFiles(array $indexes)
225
    {
226
227
        $fileIdentifiers = [];
228
        if (isset($indexes['relations'])) {
229
230
            foreach ($indexes['relations'] as $index) {
231
                if (is_array($index)) {
232
                    if ($this->isSoftReferenceImage($index)) {
233
                        $fileIdentifiers[] = $index['ref_uid'];
234
                    } elseif ($this->isSoftReferenceLink($index)) {
235
                        $fileIdentifiers[] = $index['ref_uid'];
236
                    } elseif ($this->isFileReference($index)) {
237
                        $fileIdentifiers[] = $this->findFileByFileReference($index['ref_uid']);
238
                    }
239
                }
240
            }
241
        }
242
243
        return $fileIdentifiers;
244
    }
245
246
    /**
247
     * @param array $index
248
     * @return bool
249
     */
250
    public function isFileReference(array $index)
251
    {
252
        return $index['ref_table'] === 'sys_file_reference';
253
    }
254
255
    /**
256
     * @param array $index
257
     * @return bool
258
     */
259
    public function isSoftReferenceLink(array $index)
260
    {
261
        return $index['softref_key'] === 'typolink_tag' && $index['ref_table'] === 'sys_file';
262
    }
263
264
    /**
265
     * @param array $index
266
     * @return bool
267
     */
268
    public function isSoftReferenceImage(array $index)
269
    {
270
        return $index['softref_key'] === 'rtehtmlarea_images' && $index['ref_table'] === 'sys_file';
271
    }
272
273
    /**
274
     * Retrieve the File identifier.
275
     *
276
     * @param $fileReferenceIdentifier
277
     * @return int
278
     * @throws \Exception
279
     */
280
    protected function findFileByFileReference($fileReferenceIdentifier)
281
    {
282
        $record = $this->getDataService()->getRecord(
283
            'sys_file_reference',
284
            [
285
                'uid' => $fileReferenceIdentifier
286
            ]
287
        );
288
289
        if (empty($record)) {
290
            throw new \Exception('There is something broken with the File References. Consider updating the Reference Index.', 1408619796);
291
        }
292
293
        $fileIdentifier = $record['uid_local'];
294
        return $fileIdentifier;
295
    }
296
297
    /**
298
     * @return \Fab\Media\Resource\FileReferenceService|object
299
     */
300
    protected function getFileReferenceService()
301
    {
302
        return GeneralUtility::makeInstance(\Fab\Media\Resource\FileReferenceService::class);
303
    }
304
305
    /**
306
     * Gets an instance of the memory cache.
307
     *
308
     * @return \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
309
     */
310
    protected function getMemoryCache()
311
    {
312
        return $this->getCacheManager()->getCache('cache_runtime');
313
    }
314
315
    /**
316
     * Create and returns an instance of the CacheManager
317
     *
318
     * @return \TYPO3\CMS\Core\Cache\CacheManager|object
319
     */
320
    protected function getCacheManager()
321
    {
322
        return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
323
    }
324
325
    /**
326
     * @return object|DataService
327
     */
328
    protected function getDataService(): DataService
329
    {
330
        return GeneralUtility::makeInstance(DataService::class);
331
    }
332
333
}
334