Completed
Push — master ( 72a27b...32a824 )
by
unknown
16s queued 12s
created

Helper::getURN()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 57
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 51
c 0
b 0
f 0
dl 0
loc 57
rs 9.069
cc 4
nc 5
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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