Passed
Pull Request — master (#81)
by
unknown
02:33
created

Helper::renderFlashMessages()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
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\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 the "label" for an UID
398
     *
399
     * @access public
400
     *
401
     * @param int $uid: The UID of the record
402
     * @param string $table: Get the "label" from this table
403
     * @param int $pid: Get the "label" from this page
404
     *
405
     * @return string "label" for the given UID
406
     */
407
    public static function getLabelFromUid($uid, $table, $pid = -1)
408
    {
409
        // Sanitize input.
410
        $uid = max(intval($uid), 0);
411
        if (
412
            !$uid
413
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])
414
        ) {
415
            self::log('Invalid UID "' . $uid . '" or table "' . $table . '"', LOG_SEVERITY_ERROR);
416
            return '';
417
        }
418
419
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
420
            ->getQueryBuilderForTable($table);
421
422
        $where = '';
423
        // Should we check for a specific PID, too?
424
        if ($pid !== -1) {
425
            $pid = max(intval($pid), 0);
426
            $where = $queryBuilder->expr()->eq($table . '.pid', $pid);
427
        }
428
429
        // Get label from database.
430
        $result = $queryBuilder
431
            ->select($table . '.label AS label')
432
            ->from($table)
433
            ->where(
434
                $queryBuilder->expr()->eq($table . '.uid', $uid),
435
                $where,
436
                self::whereExpression($table)
437
            )
438
            ->setMaxResults(1)
439
            ->execute();
440
441
        if ($resArray = $result->fetch()) {
442
            return $resArray['label'];
443
        } else {
444
            self::log('No "label" with UID ' . $uid . ' and PID ' . $pid . ' found in table "' . $table . '"', LOG_SEVERITY_WARNING);
445
            return '';
446
        }
447
    }
448
449
    /**
450
     * Get language name from ISO code
451
     *
452
     * @access public
453
     *
454
     * @param string $code: ISO 639-1 or ISO 639-2/B language code
455
     *
456
     * @return string Localized full name of language or unchanged input
457
     */
458
    public static function getLanguageName($code)
459
    {
460
        // Analyze code and set appropriate ISO table.
461
        $isoCode = strtolower(trim($code));
462
        if (preg_match('/^[a-z]{3}$/', $isoCode)) {
463
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey) . 'Resources/Private/Data/iso-639-2b.xml';
464
        } elseif (preg_match('/^[a-z]{2}$/', $isoCode)) {
465
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey) . 'Resources/Private/Data/iso-639-1.xml';
466
        } else {
467
            // No ISO code, return unchanged.
468
            return $code;
469
        }
470
        // Load ISO table and get localized full name of language.
471
        if (\TYPO3_MODE === 'FE') {
472
            $iso639 = $GLOBALS['TSFE']->readLLfile($file);
473
            if (!empty($iso639['default'][$isoCode])) {
474
                $lang = $GLOBALS['TSFE']->getLLL($isoCode, $iso639);
475
            }
476
        } elseif (\TYPO3_MODE === 'BE') {
477
            $iso639 = $GLOBALS['LANG']->includeLLFile($file, false, true);
478
            if (!empty($iso639['default'][$isoCode])) {
479
                $lang = $GLOBALS['LANG']->getLLL($isoCode, $iso639);
480
            }
481
        } else {
482
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
483
            return $code;
484
        }
485
        if (!empty($lang)) {
486
            return $lang;
487
        } else {
488
            self::log('Language code "' . $code . '" not found in ISO-639 table', LOG_SEVERITY_NOTICE);
489
            return $code;
490
        }
491
    }
492
493
    /**
494
     * Wrapper function for getting localized messages in frontend and backend
495
     *
496
     * @access public
497
     *
498
     * @param string $key: The locallang key to translate
499
     * @param bool $hsc: Should the result be htmlspecialchar()'ed?
500
     * @param string $default: Default return value if no translation is available
501
     *
502
     * @return string The translated string or the given key on failure
503
     */
504
    public static function getMessage($key, $hsc = false, $default = '')
505
    {
506
        // Set initial output to default value.
507
        $translated = (string) $default;
508
        // Load common messages file.
509
        if (empty(self::$messages)) {
510
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey, 'Resources/Private/Language/FlashMessages.xml');
511
            if (\TYPO3_MODE === 'FE') {
512
                self::$messages = $GLOBALS['TSFE']->readLLfile($file);
513
            } elseif (\TYPO3_MODE === 'BE') {
514
                self::$messages = $GLOBALS['LANG']->includeLLFile($file, false, true);
515
            } else {
516
                self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
517
            }
518
        }
519
        // Get translation.
520
        if (!empty(self::$messages['default'][$key])) {
521
            if (\TYPO3_MODE === 'FE') {
522
                $translated = $GLOBALS['TSFE']->getLLL($key, self::$messages);
523
            } elseif (\TYPO3_MODE === 'BE') {
524
                $translated = $GLOBALS['LANG']->getLLL($key, self::$messages);
525
            } else {
526
                self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
527
            }
528
        }
529
        // Escape HTML characters if applicable.
530
        if ($hsc) {
531
            $translated = htmlspecialchars($translated);
532
        }
533
        return $translated;
534
    }
535
536
    /**
537
     * Get the UID for a given "index_name"
538
     *
539
     * @access public
540
     *
541
     * @param int $index_name: The index_name of the record
542
     * @param string $table: Get the "index_name" from this table
543
     * @param int $pid: Get the "index_name" from this page
544
     *
545
     * @return string "uid" for the given index_name
546
     */
547
    public static function getUidFromIndexName($index_name, $table, $pid = -1)
548
    {
549
        if (
550
            !$index_name
551
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])
552
        ) {
553
            self::log('Invalid UID ' . $index_name . ' or table "' . $table . '"', LOG_SEVERITY_ERROR);
554
            return '';
555
        }
556
557
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
558
            ->getQueryBuilderForTable($table);
559
560
        $where = '';
561
        // Should we check for a specific PID, too?
562
        if ($pid !== -1) {
563
            $pid = max(intval($pid), 0);
564
            $where = $queryBuilder->expr()->eq($table . '.pid', $pid);
565
        }
566
        // Get index_name from database.
567
        $result = $queryBuilder
568
            ->select($table . '.uid AS uid')
569
            ->from($table)
570
            ->where(
571
                $queryBuilder->expr()->eq($table . '.index_name', $queryBuilder->expr()->literal($index_name)),
572
                $where,
573
                self::whereExpression($table)
574
            )
575
            ->setMaxResults(1)
576
            ->execute();
577
578
        $allResults = $result->fetchAll();
579
580
        if (count($allResults) == 1) {
581
            return $allResults[0]['uid'];
582
        } else {
583
            self::log('No UID for given index_name "' . $index_name . '" and PID ' . $pid . ' found in table "' . $table . '"', LOG_SEVERITY_WARNING);
584
            return '';
585
        }
586
    }
587
588
    /**
589
     * Get the URN of an object
590
     * @see http://www.persistent-identifier.de/?link=316
591
     *
592
     * @access public
593
     *
594
     * @param string $base: The namespace and base URN
595
     * @param string $id: The object's identifier
596
     *
597
     * @return string Uniform Resource Name as string
598
     */
599
    public static function getURN($base, $id)
600
    {
601
        $concordance = [
602
            '0' => 1,
603
            '1' => 2,
604
            '2' => 3,
605
            '3' => 4,
606
            '4' => 5,
607
            '5' => 6,
608
            '6' => 7,
609
            '7' => 8,
610
            '8' => 9,
611
            '9' => 41,
612
            'a' => 18,
613
            'b' => 14,
614
            'c' => 19,
615
            'd' => 15,
616
            'e' => 16,
617
            'f' => 21,
618
            'g' => 22,
619
            'h' => 23,
620
            'i' => 24,
621
            'j' => 25,
622
            'k' => 42,
623
            'l' => 26,
624
            'm' => 27,
625
            'n' => 13,
626
            'o' => 28,
627
            'p' => 29,
628
            'q' => 31,
629
            'r' => 12,
630
            's' => 32,
631
            't' => 33,
632
            'u' => 11,
633
            'v' => 34,
634
            'w' => 35,
635
            'x' => 36,
636
            'y' => 37,
637
            'z' => 38,
638
            '-' => 39,
639
            ':' => 17,
640
        ];
641
        $urn = strtolower($base . $id);
642
        if (preg_match('/[^a-z0-9:-]/', $urn)) {
643
            self::log('Invalid chars in given parameters', LOG_SEVERITY_WARNING);
644
            return '';
645
        }
646
        $digits = '';
647
        for ($i = 0, $j = strlen($urn); $i < $j; $i++) {
648
            $digits .= $concordance[substr($urn, $i, 1)];
649
        }
650
        $checksum = 0;
651
        for ($i = 0, $j = strlen($digits); $i < $j; $i++) {
652
            $checksum += ($i + 1) * intval(substr($digits, $i, 1));
653
        }
654
        $checksum = substr(intval($checksum / intval(substr($digits, -1, 1))), -1, 1);
655
        return $base . $id . $checksum;
656
    }
657
658
    /**
659
     * Check if given ID is a valid Pica Production Number (PPN)
660
     *
661
     * @access public
662
     *
663
     * @param string $id: The identifier to check
664
     *
665
     * @return bool Is $id a valid PPN?
666
     */
667
    public static function isPPN($id)
668
    {
669
        return self::checkIdentifier($id, 'PPN');
670
    }
671
672
    /**
673
     * Load value from user's session.
674
     *
675
     * @access public
676
     *
677
     * @param string $key: Session data key for retrieval
678
     *
679
     * @return mixed Session value for given key or null on failure
680
     */
681
    public static function loadFromSession($key)
682
    {
683
        // Cast to string for security reasons.
684
        $key = (string) $key;
685
        if (!$key) {
686
            self::log('Invalid key "' . $key . '" for session data retrieval', LOG_SEVERITY_WARNING);
687
            return;
688
        }
689
        // Get the session data.
690
        if (\TYPO3_MODE === 'FE') {
691
            return $GLOBALS['TSFE']->fe_user->getKey('ses', $key);
692
        } elseif (\TYPO3_MODE === 'BE') {
693
            return $GLOBALS['BE_USER']->getSessionData($key);
694
        } else {
695
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
696
            return;
697
        }
698
    }
699
700
    /**
701
     * Merges two arrays recursively and actually returns the modified array.
702
     * @see \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule()
703
     *
704
     * @access public
705
     *
706
     * @param array $original: Original array
707
     * @param array $overrule: Overrule array, overruling the original array
708
     * @param bool $addKeys: If set to false, keys that are not found in $original will not be set
709
     * @param bool $includeEmptyValues: If set, values from $overrule will overrule if they are empty
710
     * @param bool $enableUnsetFeature: If set, special value "__UNSET" can be used in the overrule array to unset keys in the original array
711
     *
712
     * @return array Merged array
713
     */
714
    public static function mergeRecursiveWithOverrule(array $original, array $overrule, $addKeys = true, $includeEmptyValues = true, $enableUnsetFeature = true)
715
    {
716
        \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($original, $overrule, $addKeys, $includeEmptyValues, $enableUnsetFeature);
717
        return $original;
718
    }
719
720
    /**
721
     * Process a data and/or command map with TYPO3 core engine as admin.
722
     *
723
     * @access public
724
     *
725
     * @param array $data: Data map
726
     * @param array $cmd: Command map
727
     * @param bool $reverseOrder: Should the data map be reversed?
728
     * @param bool $cmdFirst: Should the command map be processed first?
729
     *
730
     * @return array Array of substituted "NEW..." identifiers and their actual UIDs.
731
     */
732
    public static function processDBasAdmin(array $data = [], array $cmd = [], $reverseOrder = false, $cmdFirst = false)
733
    {
734
        if (
735
            \TYPO3_MODE === 'BE'
736
            && $GLOBALS['BE_USER']->isAdmin()
737
        ) {
738
            // Instantiate TYPO3 core engine.
739
            $dataHandler = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
740
            // We do not use workspaces and have to bypass restrictions in DataHandler.
741
            $dataHandler->bypassWorkspaceRestrictions = true;
742
            // Load data and command arrays.
743
            $dataHandler->start($data, $cmd);
744
            // Process command map first if default order is reversed.
745
            if (
746
                !empty($cmd)
747
                && $cmdFirst
748
            ) {
749
                $dataHandler->process_cmdmap();
750
            }
751
            // Process data map.
752
            if (!empty($data)) {
753
                $dataHandler->reverseOrder = $reverseOrder;
754
                $dataHandler->process_datamap();
755
            }
756
            // Process command map if processing order is not reversed.
757
            if (
758
                !empty($cmd)
759
                && !$cmdFirst
760
            ) {
761
                $dataHandler->process_cmdmap();
762
            }
763
            return $dataHandler->substNEWwithIDs;
764
        } else {
765
            self::log('Current backend user has no admin privileges', LOG_SEVERITY_ERROR);
766
            return [];
767
        }
768
    }
769
770
    /**
771
     * Fetches and renders all available flash messages from the queue.
772
     *
773
     * @access public
774
     *
775
     * @param string $queue: The queue's unique identifier
776
     *
777
     * @return string All flash messages in the queue rendered as HTML.
778
     */
779
    public static function renderFlashMessages($queue = 'kitodo.default.flashMessages')
780
    {
781
        $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
782
        $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier($queue);
783
        $flashMessages = $flashMessageQueue->getAllMessagesAndFlush();
784
        $content = GeneralUtility::makeInstance(\Kitodo\Dlf\Common\KitodoFlashMessageRenderer::class)
785
            ->render($flashMessages);
786
        return $content;
787
    }
788
789
    /**
790
     * Save given value to user's session.
791
     *
792
     * @access public
793
     *
794
     * @param mixed $value: Value to save
795
     * @param string $key: Session data key for saving
796
     *
797
     * @return bool true on success, false on failure
798
     */
799
    public static function saveToSession($value, $key)
800
    {
801
        // Cast to string for security reasons.
802
        $key = (string) $key;
803
        if (!$key) {
804
            self::log('Invalid key "' . $key . '" for session data saving', LOG_SEVERITY_WARNING);
805
            return false;
806
        }
807
        // Save value in session data.
808
        if (\TYPO3_MODE === 'FE') {
809
            $GLOBALS['TSFE']->fe_user->setKey('ses', $key, $value);
810
            $GLOBALS['TSFE']->fe_user->storeSessionData();
811
            return true;
812
        } elseif (\TYPO3_MODE === 'BE') {
813
            $GLOBALS['BE_USER']->setAndSaveSessionData($key, $value);
814
            return true;
815
        } else {
816
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
817
            return false;
818
        }
819
    }
820
821
    /**
822
     * This translates an internal "index_name"
823
     *
824
     * @access public
825
     *
826
     * @param string $index_name: The internal "index_name" to translate
827
     * @param string $table: Get the translation from this table
828
     * @param string $pid: Get the translation from this page
829
     *
830
     * @return string Localized label for $index_name
831
     */
832
    public static function translate($index_name, $table, $pid)
833
    {
834
        // Load labels into static variable for future use.
835
        static $labels = [];
836
        // Sanitize input.
837
        $pid = max(intval($pid), 0);
838
        if (!$pid) {
839
            self::log('Invalid PID ' . $pid . ' for translation', LOG_SEVERITY_WARNING);
840
            return $index_name;
841
        }
842
        // Check if "index_name" is an UID.
843
        if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($index_name)) {
844
            $index_name = self::getIndexNameFromUid($index_name, $table, $pid);
845
        }
846
        /* $labels already contains the translated content element, but with the index_name of the translated content element itself
847
         * and not with the $index_name of the original that we receive here. So we have to determine the index_name of the
848
         * associated translated content element. E.g. $labels['title0'] != $index_name = title. */
849
850
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
851
            ->getQueryBuilderForTable($table);
852
853
        // First fetch the uid of the received index_name
854
        $result = $queryBuilder
855
            ->select(
856
                $table . '.uid AS uid',
857
                $table . '.l18n_parent AS l18n_parent'
858
            )
859
            ->from($table)
860
            ->where(
861
                $queryBuilder->expr()->eq($table . '.pid', $pid),
862
                $queryBuilder->expr()->eq($table . '.index_name', $queryBuilder->expr()->literal($index_name)),
863
                self::whereExpression($table, true)
864
            )
865
            ->setMaxResults(1)
866
            ->execute();
867
868
        $allResults = $result->fetchAll();
869
870
        if (count($allResults) == 1) {
871
            // Now we use the uid of the l18_parent to fetch the index_name of the translated content element.
872
            $resArray = $allResults[0];
873
874
            $result = $queryBuilder
875
                ->select($table . '.index_name AS index_name')
876
                ->from($table)
877
                ->where(
878
                    $queryBuilder->expr()->eq($table . '.pid', $pid),
879
                    $queryBuilder->expr()->eq($table . '.uid', $resArray['l18n_parent']),
880
                    $queryBuilder->expr()->eq($table . '.sys_language_uid', intval($GLOBALS['TSFE']->sys_language_content)),
881
                    self::whereExpression($table, true)
882
                )
883
                ->setMaxResults(1)
884
                ->execute();
885
886
            $allResults = $result->fetchAll();
887
888
            if (count($allResults) == 1) {
889
                // If there is an translated content element, overwrite the received $index_name.
890
                $index_name = $allResults[0]['index_name'];
891
            }
892
        }
893
894
        // Check if we already got a translation.
895
        if (empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
896
            // Check if this table is allowed for translation.
897
            if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures'])) {
898
                $additionalWhere = $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]);
899
                if ($GLOBALS['TSFE']->sys_language_content > 0) {
900
                    $additionalWhere = $queryBuilder->expr()->andX(
901
                        $queryBuilder->expr()->orX(
902
                            $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]),
903
                            $queryBuilder->expr()->eq($table . '.sys_language_uid', intval($GLOBALS['TSFE']->sys_language_content))
904
                        ),
905
                        $queryBuilder->expr()->eq($table . '.l18n_parent', 0)
906
                    );
907
                }
908
909
                // Get labels from database.
910
                $result = $queryBuilder
911
                    ->select('*')
912
                    ->from($table)
913
                    ->where(
914
                        $queryBuilder->expr()->eq($table . '.pid', $pid),
915
                        $additionalWhere,
916
                        self::whereExpression($table, true)
917
                    )
918
                    ->setMaxResults(10000)
919
                    ->execute();
920
921
                if ($result->rowCount() > 0) {
922
                    while ($resArray = $result->fetch()) {
923
                        // Overlay localized labels if available.
924
                        if ($GLOBALS['TSFE']->sys_language_content > 0) {
925
                            $resArray = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $resArray, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
926
                        }
927
                        if ($resArray) {
928
                            $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$resArray['index_name']] = $resArray['label'];
929
                        }
930
                    }
931
                } else {
932
                    self::log('No translation with PID ' . $pid . ' available in table "' . $table . '" or translation not accessible', LOG_SEVERITY_NOTICE);
933
                }
934
            } else {
935
                self::log('No translations available for table "' . $table . '"', LOG_SEVERITY_WARNING);
936
            }
937
        }
938
939
        if (!empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
940
            return $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name];
941
        } else {
942
            return $index_name;
943
        }
944
    }
945
946
    /**
947
     * This returns the additional WHERE expression of a table based on its TCA configuration
948
     *
949
     * @access public
950
     *
951
     * @param string $table: Table name as defined in TCA
952
     * @param bool $showHidden: Ignore the hidden flag?
953
     *
954
     * @return string Additional WHERE expression
955
     */
956
    public static function whereExpression($table, $showHidden = false)
957
    {
958
        if (\TYPO3_MODE === 'FE') {
959
            // Should we ignore the record's hidden flag?
960
            $ignoreHide = 0;
961
            if ($showHidden) {
962
                $ignoreHide = 1;
963
            }
964
            $expression = $GLOBALS['TSFE']->sys_page->enableFields($table, $ignoreHide);
965
            if (!empty($expression)) {
966
                return substr($expression, 5);
967
            } else {
968
                return '';
969
            }
970
        } elseif (\TYPO3_MODE === 'BE') {
971
            return GeneralUtility::makeInstance(ConnectionPool::class)
972
                ->getQueryBuilderForTable($table)
973
                ->expr()
974
                ->eq($table . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'], 0);
975
        } else {
976
            self::log('Unexpected TYPO3_MODE "' . \TYPO3_MODE . '"', LOG_SEVERITY_ERROR);
977
            return '1=-1';
978
        }
979
    }
980
981
    /**
982
     * Prevent instantiation by hiding the constructor
983
     *
984
     * @access private
985
     */
986
    private function __construct()
987
    {
988
        // This is a static class, thus no instances should be created.
989
    }
990
}
991