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 ( 8008ee...08c94e )
by Sebastian
23s queued 13s
created

Helper::digest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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