Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 72a27b...32a824 )
by Alexander
16s queued 12s
created

Helper   F

Complexity

Total Complexity 112

Size/Duplication

Total Lines 907
Duplicated Lines 0 %

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 112
eloc 409
c 6
b 1
f 0
dl 0
loc 907
rs 2

23 Methods

Rating   Name   Duplication   Size   Complexity  
A addMessage() 0 13 1
D checkIdentifier() 0 54 20
A decrypt() 0 28 6
A getUnqualifiedClassName() 0 4 1
A getCleanString() 0 11 1
A getHookObjects() 0 9 3
A saveToSession() 0 19 4
A encrypt() 0 23 5
A whereExpression() 0 22 5
B getMessage() 0 30 8
A loadFromSession() 0 16 4
A isPPN() 0 3 1
A __construct() 0 2 1
A mergeRecursiveWithOverrule() 0 4 1
A getURN() 0 57 4
C translate() 0 111 13
A getIndexNameFromUid() 0 39 5
A digest() 0 9 2
B getLanguageName() 0 32 8
A getUidFromIndexName() 0 38 5
B processDBasAdmin() 0 35 8
A renderFlashMessages() 0 8 1
A log() 0 19 5

How to fix   Complexity   

Complex Class

Complex classes like Helper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Helper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Common;
14
15
use TYPO3\CMS\Core\Database\ConnectionPool;
16
use TYPO3\CMS\Core\Log\LogManager;
17
use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19
/**
20
 * Helper class for the 'dlf' extension
21
 *
22
 * @author Sebastian Meyer <[email protected]>
23
 * @author Henrik Lochmann <[email protected]>
24
 * @package TYPO3
25
 * @subpackage dlf
26
 * @access public
27
 */
28
class Helper
29
{
30
    /**
31
     * The extension key
32
     *
33
     * @var string
34
     * @access public
35
     */
36
    public static $extKey = 'dlf';
37
38
    /**
39
     * This holds the cipher algorithm
40
     * @see openssl_get_cipher_methods() for options
41
     *
42
     * @var string
43
     * @access protected
44
     */
45
    protected static $cipherAlgorithm = 'aes-256-ctr';
46
47
    /**
48
     * This holds the hash algorithm
49
     * @see openssl_get_md_methods() for options
50
     *
51
     * @var string
52
     * @access protected
53
     */
54
    protected static $hashAlgorithm = 'sha256';
55
56
    /**
57
     * The locallang array for flash messages
58
     *
59
     * @var array
60
     * @access protected
61
     */
62
    protected static $messages = [];
63
64
    /**
65
     * Generates a flash message and adds it to a message queue.
66
     *
67
     * @access public
68
     *
69
     * @param string $message: The body of the message
70
     * @param string $title: The title of the message
71
     * @param int $severity: The message's severity
72
     * @param bool $session: Should the message be saved in the user's session?
73
     * @param string $queue: The queue's unique identifier
74
     *
75
     * @return \TYPO3\CMS\Core\Messaging\FlashMessageQueue The queue the message was added to
76
     */
77
    public static function addMessage($message, $title, $severity, $session = false, $queue = 'kitodo.default.flashMessages')
78
    {
79
        $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
80
        $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier($queue);
81
        $flashMessage = GeneralUtility::makeInstance(
82
            \TYPO3\CMS\Core\Messaging\FlashMessage::class,
83
            $message,
84
            $title,
85
            $severity,
86
            $session
87
        );
88
        $flashMessageQueue->enqueue($flashMessage);
89
        return $flashMessageQueue;
90
    }
91
92
    /**
93
     * Check if given identifier is a valid identifier of the German National Library
94
     *
95
     * @access public
96
     *
97
     * @param string $id: The identifier to check
98
     * @param string $type: What type is the identifier supposed to be?
99
     *                      Possible values: PPN, IDN, PND, ZDB, SWD, GKD
100
     *
101
     * @return bool Is $id a valid GNL identifier of the given $type?
102
     */
103
    public static function checkIdentifier($id, $type)
104
    {
105
        $digits = substr($id, 0, 8);
106
        $checksum = 0;
107
        for ($i = 0, $j = strlen($digits); $i < $j; $i++) {
108
            $checksum += (9 - $i) * intval(substr($digits, $i, 1));
109
        }
110
        $checksum = (11 - ($checksum % 11)) % 11;
111
        switch (strtoupper($type)) {
112
            case 'PPN':
113
            case 'IDN':
114
            case 'PND':
115
                if ($checksum == 10) {
116
                    $checksum = 'X';
117
                }
118
                if (!preg_match('/[0-9]{8}[0-9X]{1}/i', $id)) {
119
                    return false;
120
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
121
                    return false;
122
                }
123
                break;
124
            case 'ZDB':
125
                if ($checksum == 10) {
126
                    $checksum = 'X';
127
                }
128
                if (!preg_match('/[0-9]{8}-[0-9X]{1}/i', $id)) {
129
                    return false;
130
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
131
                    return false;
132
                }
133
                break;
134
            case 'SWD':
135
                $checksum = 11 - $checksum;
136
                if (!preg_match('/[0-9]{8}-[0-9]{1}/i', $id)) {
137
                    return false;
138
                } elseif ($checksum == 10) {
139
                    return self::checkIdentifier(($digits + 1) . substr($id, -2, 2), 'SWD');
140
                } elseif (substr($id, -1, 1) != $checksum) {
141
                    return false;
142
                }
143
                break;
144
            case 'GKD':
145
                $checksum = 11 - $checksum;
146
                if ($checksum == 10) {
147
                    $checksum = 'X';
148
                }
149
                if (!preg_match('/[0-9]{8}-[0-9X]{1}/i', $id)) {
150
                    return false;
151
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
152
                    return false;
153
                }
154
                break;
155
        }
156
        return true;
157
    }
158
159
    /**
160
     * Decrypt encrypted value with given control hash
161
     *
162
     * @access public
163
     *
164
     * @param string $encrypted: The encrypted value to decrypt
165
     *
166
     * @return mixed The decrypted value or false on error
167
     */
168
    public static function decrypt($encrypted)
169
    {
170
        if (
171
            !in_array(self::$cipherAlgorithm, openssl_get_cipher_methods(true))
172
            || !in_array(self::$hashAlgorithm, openssl_get_md_methods(true))
173
        ) {
174
            self::log('OpenSSL library doesn\'t support cipher and/or hash algorithm', LOG_SEVERITY_ERROR);
175
            return false;
176
        }
177
        if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
178
            self::log('No encryption key set in TYPO3 configuration', LOG_SEVERITY_ERROR);
179
            return false;
180
        }
181
        if (
182
            empty($encrypted)
183
            || strlen($encrypted) < openssl_cipher_iv_length(self::$cipherAlgorithm)
184
        ) {
185
            self::log('Invalid parameters given for decryption', LOG_SEVERITY_ERROR);
186
            return false;
187
        }
188
        // Split initialisation vector and encrypted data.
189
        $binary = base64_decode($encrypted);
190
        $iv = substr($binary, 0, openssl_cipher_iv_length(self::$cipherAlgorithm));
191
        $data = substr($binary, openssl_cipher_iv_length(self::$cipherAlgorithm));
192
        $key = openssl_digest($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], self::$hashAlgorithm, true);
193
        // Decrypt data.
194
        $decrypted = openssl_decrypt($data, self::$cipherAlgorithm, $key, OPENSSL_RAW_DATA, $iv);
195
        return $decrypted;
196
    }
197
198
    /**
199
     * Add a message to the TYPO3 log
200
     *
201
     * @access public
202
     *
203
     * @param string $message: The message to log
204
     * @param int $severity: The severity of the message
205
     *                       0 is info, 1 is notice, 2 is warning, 3 is fatal error, -1 is "OK" message
206
     *
207
     * @return void
208
     */
209
    //TODO: find better way to handle logger in static class
210
    public static function log($message, $severity = 0)
211
    {
212
        $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
213
214
        switch ($severity) {
215
            case 0:
216
                $logger->info($message);
217
                break;
218
            case 1:
219
                $logger->notice($message);
220
                break;
221
            case 2:
222
                $logger->warning($message);
223
                break;
224
            case 23:
225
                $logger->error($message);
226
                break;
227
            default:
228
                break;
229
        }
230
    }
231
232
    /**
233
     * Digest the given string
234
     *
235
     * @access public
236
     *
237
     * @param string $string: The string to encrypt
238
     *
239
     * @return mixed Hashed string or false on error
240
     */
241
    public static function digest($string)
242
    {
243
        if (!in_array(self::$hashAlgorithm, openssl_get_md_methods(true))) {
244
            self::log('OpenSSL library doesn\'t support hash algorithm', LOG_SEVERITY_ERROR);
245
            return false;
246
        }
247
        // Hash string.
248
        $hashed = openssl_digest($string, self::$hashAlgorithm);
249
        return $hashed;
250
    }
251
252
    /**
253
     * Encrypt the given string
254
     *
255
     * @access public
256
     *
257
     * @param string $string: The string to encrypt
258
     *
259
     * @return mixed Encrypted string or false on error
260
     */
261
    public static function encrypt($string)
262
    {
263
        if (
264
            !in_array(self::$cipherAlgorithm, openssl_get_cipher_methods(true))
265
            || !in_array(self::$hashAlgorithm, openssl_get_md_methods(true))
266
        ) {
267
            self::log('OpenSSL library doesn\'t support cipher and/or hash algorithm', LOG_SEVERITY_ERROR);
268
            return false;
269
        }
270
        if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
271
            self::log('No encryption key set in TYPO3 configuration', LOG_SEVERITY_ERROR);
272
            return false;
273
        }
274
        // Generate random initialisation vector.
275
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(self::$cipherAlgorithm));
276
        $key = openssl_digest($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], self::$hashAlgorithm, true);
277
        // Encrypt data.
278
        $encrypted = openssl_encrypt($string, self::$cipherAlgorithm, $key, OPENSSL_RAW_DATA, $iv);
279
        // Merge initialisation vector and encrypted data.
280
        if ($encrypted !== false) {
281
            $encrypted = base64_encode($iv . $encrypted);
282
        }
283
        return $encrypted;
284
    }
285
286
    /**
287
     * Get the unqualified name of a class
288
     *
289
     * @access public
290
     *
291
     * @param string $qualifiedClassname: The qualified class name from get_class()
292
     *
293
     * @return string The unqualified class name
294
     */
295
    public static function getUnqualifiedClassName($qualifiedClassname)
296
    {
297
        $nameParts = explode('\\', $qualifiedClassname);
298
        return end($nameParts);
299
    }
300
301
    /**
302
     * Clean up a string to use in an URL.
303
     *
304
     * @access public
305
     *
306
     * @param string $string: The string to clean up
307
     *
308
     * @return string The cleaned up string
309
     */
310
    public static function getCleanString($string)
311
    {
312
        // Convert to lowercase.
313
        $string = strtolower($string);
314
        // Remove non-alphanumeric characters.
315
        $string = preg_replace('/[^a-z0-9_\s-]/', '', $string);
316
        // Remove multiple dashes or whitespaces.
317
        $string = preg_replace('/[\s-]+/', ' ', $string);
318
        // Convert whitespaces and underscore to dash.
319
        $string = preg_replace('/[\s_]/', '-', $string);
320
        return $string;
321
    }
322
323
    /**
324
     * Get the registered hook objects for a class
325
     *
326
     * @access public
327
     *
328
     * @param string $scriptRelPath: The path to the class file
329
     *
330
     * @return array Array of hook objects for the class
331
     */
332
    public static function getHookObjects($scriptRelPath)
333
    {
334
        $hookObjects = [];
335
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey . '/' . $scriptRelPath]['hookClass'])) {
336
            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey . '/' . $scriptRelPath]['hookClass'] as $classRef) {
337
                $hookObjects[] = GeneralUtility::makeInstance($classRef);
338
            }
339
        }
340
        return $hookObjects;
341
    }
342
343
    /**
344
     * Get the "index_name" for an UID
345
     *
346
     * @access public
347
     *
348
     * @param int $uid: The UID of the record
349
     * @param string $table: Get the "index_name" from this table
350
     * @param int $pid: Get the "index_name" from this page
351
     *
352
     * @return string "index_name" for the given UID
353
     */
354
    public static function getIndexNameFromUid($uid, $table, $pid = -1)
355
    {
356
        // Sanitize input.
357
        $uid = max(intval($uid), 0);
358
        if (
359
            !$uid
360
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])
361
        ) {
362
            self::log('Invalid UID "' . $uid . '" or table "' . $table . '"', LOG_SEVERITY_ERROR);
363
            return '';
364
        }
365
366
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
367
            ->getQueryBuilderForTable($table);
368
369
        $where = '';
370
        // Should we check for a specific PID, too?
371
        if ($pid !== -1) {
372
            $pid = max(intval($pid), 0);
373
            $where = $queryBuilder->expr()->eq($table . '.pid', $pid);
374
        }
375
376
        // Get index_name from database.
377
        $result = $queryBuilder
378
            ->select($table . '.index_name AS index_name')
379
            ->from($table)
380
            ->where(
381
                $queryBuilder->expr()->eq($table . '.uid', $uid),
382
                $where,
383
                self::whereExpression($table)
384
            )
385
            ->setMaxResults(1)
386
            ->execute();
387
388
        if ($resArray = $result->fetch()) {
389
            return $resArray['index_name'];
390
        } else {
391
            self::log('No "index_name" with UID ' . $uid . ' and PID ' . $pid . ' found in table "' . $table . '"', LOG_SEVERITY_WARNING);
392
            return '';
393
        }
394
    }
395
396
    /**
397
     * Get language name from ISO code
398
     *
399
     * @access public
400
     *
401
     * @param string $code: ISO 639-1 or ISO 639-2/B language code
402
     *
403
     * @return string Localized full name of language or unchanged input
404
     */
405
    public static function getLanguageName($code)
406
    {
407
        // Analyze code and set appropriate ISO table.
408
        $isoCode = strtolower(trim($code));
409
        if (preg_match('/^[a-z]{3}$/', $isoCode)) {
410
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey) . 'Resources/Private/Data/iso-639-2b.xml';
411
        } elseif (preg_match('/^[a-z]{2}$/', $isoCode)) {
412
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey) . 'Resources/Private/Data/iso-639-1.xml';
413
        } else {
414
            // No ISO code, return unchanged.
415
            return $code;
416
        }
417
        // Load ISO table and get localized full name of language.
418
        if (\TYPO3_MODE === 'FE') {
419
            $iso639 = $GLOBALS['TSFE']->readLLfile($file);
420
            if (!empty($iso639['default'][$isoCode])) {
421
                $lang = $GLOBALS['TSFE']->getLLL($isoCode, $iso639);
422
            }
423
        } elseif (\TYPO3_MODE === 'BE') {
424
            $iso639 = $GLOBALS['LANG']->includeLLFile($file, false, true);
425
            if (!empty($iso639['default'][$isoCode])) {
426
                $lang = $GLOBALS['LANG']->getLLL($isoCode, $iso639);
427
            }
428
        } else {
429
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
430
            return $code;
431
        }
432
        if (!empty($lang)) {
433
            return $lang;
434
        } else {
435
            self::log('Language code "' . $code . '" not found in ISO-639 table', LOG_SEVERITY_NOTICE);
436
            return $code;
437
        }
438
    }
439
440
    /**
441
     * Wrapper function for getting localized messages in frontend and backend
442
     *
443
     * @access public
444
     *
445
     * @param string $key: The locallang key to translate
446
     * @param bool $hsc: Should the result be htmlspecialchar()'ed?
447
     * @param string $default: Default return value if no translation is available
448
     *
449
     * @return string The translated string or the given key on failure
450
     */
451
    public static function getMessage($key, $hsc = false, $default = '')
452
    {
453
        // Set initial output to default value.
454
        $translated = (string) $default;
455
        // Load common messages file.
456
        if (empty(self::$messages)) {
457
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey, 'Resources/Private/Language/FlashMessages.xml');
458
            if (\TYPO3_MODE === 'FE') {
459
                self::$messages = $GLOBALS['TSFE']->readLLfile($file);
460
            } elseif (\TYPO3_MODE === 'BE') {
461
                self::$messages = $GLOBALS['LANG']->includeLLFile($file, false, true);
462
            } else {
463
                self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
464
            }
465
        }
466
        // Get translation.
467
        if (!empty(self::$messages['default'][$key])) {
468
            if (\TYPO3_MODE === 'FE') {
469
                $translated = $GLOBALS['TSFE']->getLLL($key, self::$messages);
470
            } elseif (\TYPO3_MODE === 'BE') {
471
                $translated = $GLOBALS['LANG']->getLLL($key, self::$messages);
472
            } else {
473
                self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
474
            }
475
        }
476
        // Escape HTML characters if applicable.
477
        if ($hsc) {
478
            $translated = htmlspecialchars($translated);
479
        }
480
        return $translated;
481
    }
482
483
    /**
484
     * Get the UID for a given "index_name"
485
     *
486
     * @access public
487
     *
488
     * @param int $index_name: The index_name of the record
489
     * @param string $table: Get the "index_name" from this table
490
     * @param int $pid: Get the "index_name" from this page
491
     *
492
     * @return string "uid" for the given index_name
493
     */
494
    public static function getUidFromIndexName($index_name, $table, $pid = -1)
495
    {
496
        if (
497
            !$index_name
498
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])
499
        ) {
500
            self::log('Invalid UID ' . $index_name . ' or table "' . $table . '"', LOG_SEVERITY_ERROR);
501
            return '';
502
        }
503
504
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
505
            ->getQueryBuilderForTable($table);
506
507
        $where = '';
508
        // Should we check for a specific PID, too?
509
        if ($pid !== -1) {
510
            $pid = max(intval($pid), 0);
511
            $where = $queryBuilder->expr()->eq($table . '.pid', $pid);
512
        }
513
        // Get index_name from database.
514
        $result = $queryBuilder
515
            ->select($table . '.uid AS uid')
516
            ->from($table)
517
            ->where(
518
                $queryBuilder->expr()->eq($table . '.index_name', $queryBuilder->expr()->literal($index_name)),
519
                $where,
520
                self::whereExpression($table)
521
            )
522
            ->setMaxResults(1)
523
            ->execute();
524
525
        $allResults = $result->fetchAll();
526
527
        if (count($allResults) == 1) {
528
            return $allResults[0]['uid'];
529
        } else {
530
            self::log('No UID for given index_name "' . $index_name . '" and PID ' . $pid . ' found in table "' . $table . '"', LOG_SEVERITY_WARNING);
531
            return '';
532
        }
533
    }
534
535
    /**
536
     * Get the URN of an object
537
     * @see http://www.persistent-identifier.de/?link=316
538
     *
539
     * @access public
540
     *
541
     * @param string $base: The namespace and base URN
542
     * @param string $id: The object's identifier
543
     *
544
     * @return string Uniform Resource Name as string
545
     */
546
    public static function getURN($base, $id)
547
    {
548
        $concordance = [
549
            '0' => 1,
550
            '1' => 2,
551
            '2' => 3,
552
            '3' => 4,
553
            '4' => 5,
554
            '5' => 6,
555
            '6' => 7,
556
            '7' => 8,
557
            '8' => 9,
558
            '9' => 41,
559
            'a' => 18,
560
            'b' => 14,
561
            'c' => 19,
562
            'd' => 15,
563
            'e' => 16,
564
            'f' => 21,
565
            'g' => 22,
566
            'h' => 23,
567
            'i' => 24,
568
            'j' => 25,
569
            'k' => 42,
570
            'l' => 26,
571
            'm' => 27,
572
            'n' => 13,
573
            'o' => 28,
574
            'p' => 29,
575
            'q' => 31,
576
            'r' => 12,
577
            's' => 32,
578
            't' => 33,
579
            'u' => 11,
580
            'v' => 34,
581
            'w' => 35,
582
            'x' => 36,
583
            'y' => 37,
584
            'z' => 38,
585
            '-' => 39,
586
            ':' => 17,
587
        ];
588
        $urn = strtolower($base . $id);
589
        if (preg_match('/[^a-z0-9:-]/', $urn)) {
590
            self::log('Invalid chars in given parameters', LOG_SEVERITY_WARNING);
591
            return '';
592
        }
593
        $digits = '';
594
        for ($i = 0, $j = strlen($urn); $i < $j; $i++) {
595
            $digits .= $concordance[substr($urn, $i, 1)];
596
        }
597
        $checksum = 0;
598
        for ($i = 0, $j = strlen($digits); $i < $j; $i++) {
599
            $checksum += ($i + 1) * intval(substr($digits, $i, 1));
600
        }
601
        $checksum = substr(intval($checksum / intval(substr($digits, -1, 1))), -1, 1);
602
        return $base . $id . $checksum;
603
    }
604
605
    /**
606
     * Check if given ID is a valid Pica Production Number (PPN)
607
     *
608
     * @access public
609
     *
610
     * @param string $id: The identifier to check
611
     *
612
     * @return bool Is $id a valid PPN?
613
     */
614
    public static function isPPN($id)
615
    {
616
        return self::checkIdentifier($id, 'PPN');
617
    }
618
619
    /**
620
     * Load value from user's session.
621
     *
622
     * @access public
623
     *
624
     * @param string $key: Session data key for retrieval
625
     *
626
     * @return mixed Session value for given key or null on failure
627
     */
628
    public static function loadFromSession($key)
629
    {
630
        // Cast to string for security reasons.
631
        $key = (string) $key;
632
        if (!$key) {
633
            self::log('Invalid key "' . $key . '" for session data retrieval', LOG_SEVERITY_WARNING);
634
            return;
635
        }
636
        // Get the session data.
637
        if (\TYPO3_MODE === 'FE') {
638
            return $GLOBALS['TSFE']->fe_user->getKey('ses', $key);
639
        } elseif (\TYPO3_MODE === 'BE') {
640
            return $GLOBALS['BE_USER']->getSessionData($key);
641
        } else {
642
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
643
            return;
644
        }
645
    }
646
647
    /**
648
     * Merges two arrays recursively and actually returns the modified array.
649
     * @see \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule()
650
     *
651
     * @access public
652
     *
653
     * @param array $original: Original array
654
     * @param array $overrule: Overrule array, overruling the original array
655
     * @param bool $addKeys: If set to false, keys that are not found in $original will not be set
656
     * @param bool $includeEmptyValues: If set, values from $overrule will overrule if they are empty
657
     * @param bool $enableUnsetFeature: If set, special value "__UNSET" can be used in the overrule array to unset keys in the original array
658
     *
659
     * @return array Merged array
660
     */
661
    public static function mergeRecursiveWithOverrule(array $original, array $overrule, $addKeys = true, $includeEmptyValues = true, $enableUnsetFeature = true)
662
    {
663
        \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($original, $overrule, $addKeys, $includeEmptyValues, $enableUnsetFeature);
664
        return $original;
665
    }
666
667
    /**
668
     * Process a data and/or command map with TYPO3 core engine as admin.
669
     *
670
     * @access public
671
     *
672
     * @param array $data: Data map
673
     * @param array $cmd: Command map
674
     * @param bool $reverseOrder: Should the data map be reversed?
675
     * @param bool $cmdFirst: Should the command map be processed first?
676
     *
677
     * @return array Array of substituted "NEW..." identifiers and their actual UIDs.
678
     */
679
    public static function processDBasAdmin(array $data = [], array $cmd = [], $reverseOrder = false, $cmdFirst = false)
680
    {
681
        if (
682
            \TYPO3_MODE === 'BE'
683
            && $GLOBALS['BE_USER']->isAdmin()
684
        ) {
685
            // Instantiate TYPO3 core engine.
686
            $dataHandler = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
687
            // We do not use workspaces and have to bypass restrictions in DataHandler.
688
            $dataHandler->bypassWorkspaceRestrictions = true;
689
            // Load data and command arrays.
690
            $dataHandler->start($data, $cmd);
691
            // Process command map first if default order is reversed.
692
            if (
693
                !empty($cmd)
694
                && $cmdFirst
695
            ) {
696
                $dataHandler->process_cmdmap();
697
            }
698
            // Process data map.
699
            if (!empty($data)) {
700
                $dataHandler->reverseOrder = $reverseOrder;
701
                $dataHandler->process_datamap();
702
            }
703
            // Process command map if processing order is not reversed.
704
            if (
705
                !empty($cmd)
706
                && !$cmdFirst
707
            ) {
708
                $dataHandler->process_cmdmap();
709
            }
710
            return $dataHandler->substNEWwithIDs;
711
        } else {
712
            self::log('Current backend user has no admin privileges', LOG_SEVERITY_ERROR);
713
            return [];
714
        }
715
    }
716
717
    /**
718
     * Fetches and renders all available flash messages from the queue.
719
     *
720
     * @access public
721
     *
722
     * @param string $queue: The queue's unique identifier
723
     *
724
     * @return string All flash messages in the queue rendered as HTML.
725
     */
726
    public static function renderFlashMessages($queue = 'kitodo.default.flashMessages')
727
    {
728
        $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
729
        $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier($queue);
730
        $flashMessages = $flashMessageQueue->getAllMessagesAndFlush();
731
        $content = GeneralUtility::makeInstance(\Kitodo\Dlf\Common\KitodoFlashMessageRenderer::class)
732
            ->render($flashMessages);
733
        return $content;
734
    }
735
736
    /**
737
     * Save given value to user's session.
738
     *
739
     * @access public
740
     *
741
     * @param mixed $value: Value to save
742
     * @param string $key: Session data key for saving
743
     *
744
     * @return bool true on success, false on failure
745
     */
746
    public static function saveToSession($value, $key)
747
    {
748
        // Cast to string for security reasons.
749
        $key = (string) $key;
750
        if (!$key) {
751
            self::log('Invalid key "' . $key . '" for session data saving', LOG_SEVERITY_WARNING);
752
            return false;
753
        }
754
        // Save value in session data.
755
        if (\TYPO3_MODE === 'FE') {
756
            $GLOBALS['TSFE']->fe_user->setKey('ses', $key, $value);
757
            $GLOBALS['TSFE']->fe_user->storeSessionData();
758
            return true;
759
        } elseif (\TYPO3_MODE === 'BE') {
760
            $GLOBALS['BE_USER']->setAndSaveSessionData($key, $value);
761
            return true;
762
        } else {
763
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
764
            return false;
765
        }
766
    }
767
768
    /**
769
     * This translates an internal "index_name"
770
     *
771
     * @access public
772
     *
773
     * @param string $index_name: The internal "index_name" to translate
774
     * @param string $table: Get the translation from this table
775
     * @param string $pid: Get the translation from this page
776
     *
777
     * @return string Localized label for $index_name
778
     */
779
    public static function translate($index_name, $table, $pid)
780
    {
781
        // Load labels into static variable for future use.
782
        static $labels = [];
783
        // Sanitize input.
784
        $pid = max(intval($pid), 0);
785
        if (!$pid) {
786
            self::log('Invalid PID ' . $pid . ' for translation', LOG_SEVERITY_WARNING);
787
            return $index_name;
788
        }
789
        // Check if "index_name" is an UID.
790
        if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($index_name)) {
791
            $index_name = self::getIndexNameFromUid($index_name, $table, $pid);
792
        }
793
        /* $labels already contains the translated content element, but with the index_name of the translated content element itself
794
         * and not with the $index_name of the original that we receive here. So we have to determine the index_name of the
795
         * associated translated content element. E.g. $labels['title0'] != $index_name = title. */
796
797
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
798
            ->getQueryBuilderForTable($table);
799
800
        // First fetch the uid of the received index_name
801
        $result = $queryBuilder
802
            ->select(
803
                $table . '.uid AS uid',
804
                $table . '.l18n_parent AS l18n_parent'
805
            )
806
            ->from($table)
807
            ->where(
808
                $queryBuilder->expr()->eq($table . '.pid', $pid),
809
                $queryBuilder->expr()->eq($table . '.index_name', $queryBuilder->expr()->literal($index_name)),
810
                self::whereExpression($table, true)
811
            )
812
            ->setMaxResults(1)
813
            ->execute();
814
815
        $allResults = $result->fetchAll();
816
817
        if (count($allResults) == 1) {
818
            // Now we use the uid of the l18_parent to fetch the index_name of the translated content element.
819
            $resArray = $allResults[0];
820
821
            $result = $queryBuilder
822
                ->select($table . '.index_name AS index_name')
823
                ->from($table)
824
                ->where(
825
                    $queryBuilder->expr()->eq($table . '.pid', $pid),
826
                    $queryBuilder->expr()->eq($table . '.uid', $resArray['l18n_parent']),
827
                    $queryBuilder->expr()->eq($table . '.sys_language_uid', intval($GLOBALS['TSFE']->sys_language_content)),
828
                    self::whereExpression($table, true)
829
                )
830
                ->setMaxResults(1)
831
                ->execute();
832
833
            $allResults = $result->fetchAll();
834
835
            if (count($allResults) == 1) {
836
                // If there is an translated content element, overwrite the received $index_name.
837
                $index_name = $allResults[0]['index_name'];
838
            }
839
        }
840
841
        // Check if we already got a translation.
842
        if (empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
843
            // Check if this table is allowed for translation.
844
            if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures'])) {
845
                $additionalWhere = $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]);
846
                if ($GLOBALS['TSFE']->sys_language_content > 0) {
847
                    $additionalWhere = $queryBuilder->expr()->andX(
848
                        $queryBuilder->expr()->orX(
849
                            $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]),
850
                            $queryBuilder->expr()->eq($table . '.sys_language_uid', intval($GLOBALS['TSFE']->sys_language_content))
851
                        ),
852
                        $queryBuilder->expr()->eq($table . '.l18n_parent', 0)
853
                    );
854
                }
855
856
                // Get labels from database.
857
                $result = $queryBuilder
858
                    ->select('*')
859
                    ->from($table)
860
                    ->where(
861
                        $queryBuilder->expr()->eq($table . '.pid', $pid),
862
                        $additionalWhere,
863
                        self::whereExpression($table, true)
864
                    )
865
                    ->setMaxResults(10000)
866
                    ->execute();
867
868
                if ($result->rowCount() > 0) {
869
                    while ($resArray = $result->fetch()) {
870
                        // Overlay localized labels if available.
871
                        if ($GLOBALS['TSFE']->sys_language_content > 0) {
872
                            $resArray = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $resArray, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
873
                        }
874
                        if ($resArray) {
875
                            $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$resArray['index_name']] = $resArray['label'];
876
                        }
877
                    }
878
                } else {
879
                    self::log('No translation with PID ' . $pid . ' available in table "' . $table . '" or translation not accessible', LOG_SEVERITY_NOTICE);
880
                }
881
            } else {
882
                self::log('No translations available for table "' . $table . '"', LOG_SEVERITY_WARNING);
883
            }
884
        }
885
886
        if (!empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
887
            return $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name];
888
        } else {
889
            return $index_name;
890
        }
891
    }
892
893
    /**
894
     * This returns the additional WHERE expression of a table based on its TCA configuration
895
     *
896
     * @access public
897
     *
898
     * @param string $table: Table name as defined in TCA
899
     * @param bool $showHidden: Ignore the hidden flag?
900
     *
901
     * @return string Additional WHERE expression
902
     */
903
    public static function whereExpression($table, $showHidden = false)
904
    {
905
        if (\TYPO3_MODE === 'FE') {
906
            // Should we ignore the record's hidden flag?
907
            $ignoreHide = 0;
908
            if ($showHidden) {
909
                $ignoreHide = 1;
910
            }
911
            $expression = $GLOBALS['TSFE']->sys_page->enableFields($table, $ignoreHide);
912
            if (!empty($expression)) {
913
                return substr($expression, 5);
914
            } else {
915
                return '';
916
            }
917
        } elseif (\TYPO3_MODE === 'BE') {
918
            return GeneralUtility::makeInstance(ConnectionPool::class)
919
                ->getQueryBuilderForTable($table)
920
                ->expr()
921
                ->eq($table . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'], 0);
922
        } else {
923
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
924
            return '1=-1';
925
        }
926
    }
927
928
    /**
929
     * Prevent instantiation by hiding the constructor
930
     *
931
     * @access private
932
     */
933
    private function __construct()
934
    {
935
        // This is a static class, thus no instances should be created.
936
    }
937
}
938