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.
Passed
Pull Request — master (#376)
by Sebastian
03:43
created

Helper::addMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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