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
Push — master ( 767599...997df0 )
by Sebastian
03:33
created

Helper::getUnqualifiedClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
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
/**
15
 * Helper class for the 'dlf' extension
16
 *
17
 * @author Sebastian Meyer <[email protected]>
18
 * @author Henrik Lochmann <[email protected]>
19
 * @package TYPO3
20
 * @subpackage dlf
21
 * @access public
22
 */
23
class Helper {
24
    /**
25
     * The extension key
26
     *
27
     * @var string
28
     * @access public
29
     */
30
    public static $extKey = 'dlf';
31
32
    /**
33
     * The locallang array for flash messages
34
     *
35
     * @var array
36
     * @access protected
37
     */
38
    protected static $messages = [];
39
40
    /**
41
     * Generates a flash message and adds it to a message queue.
42
     *
43
     * @access public
44
     *
45
     * @param string $message: The body of the message
46
     * @param string $title: The title of the message
47
     * @param integer $severity: The message's severity
48
     * @param boolean $session: Should the message be saved in the user's session?
49
     * @param string $queue: The queue's unique identifier
50
     *
51
     * @return \TYPO3\CMS\Core\Messaging\FlashMessageQueue The queue the message was added to
52
     */
53
    public static function addMessage($message, $title, $severity, $session = FALSE, $queue = 'kitodo.default.flashMessages') {
54
        $flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
55
        $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier($queue);
56
        $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
57
            \TYPO3\CMS\Core\Messaging\FlashMessage::class,
58
            $message,
59
            $title,
60
            $severity,
61
            $session
62
        );
63
        $flashMessageQueue->enqueue($flashMessage);
64
        return $flashMessageQueue;
65
    }
66
67
    /**
68
     * Check if given identifier is a valid identifier of the German National Library
69
     *
70
     * @access public
71
     *
72
     * @param string $id: The identifier to check
73
     * @param string $type: What type is the identifier supposed to be?
74
     *                      Possible values: PPN, IDN, PND, ZDB, SWD, GKD
75
     *
76
     * @return boolean Is $id a valid GNL identifier of the given $type?
77
     */
78
    public static function checkIdentifier($id, $type) {
79
        $digits = substr($id, 0, 8);
80
        $checksum = 0;
81
        for ($i = 0, $j = strlen($digits); $i < $j; $i++) {
82
            $checksum += (9 - $i) * intval(substr($digits, $i, 1));
83
        }
84
        $checksum = (11 - ($checksum % 11)) % 11;
85
        switch (strtoupper($type)) {
86
            case 'PPN':
87
            case 'IDN':
88
            case 'PND':
89
                if ($checksum == 10) {
90
                    $checksum = 'X';
91
                }
92
                if (!preg_match('/[0-9]{8}[0-9X]{1}/i', $id)) {
93
                    return FALSE;
94
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
95
                    return FALSE;
96
                }
97
                break;
98
            case 'ZDB':
99
                if ($checksum == 10) {
100
                    $checksum = 'X';
101
                }
102
                if (!preg_match('/[0-9]{8}-[0-9X]{1}/i', $id)) {
103
                    return FALSE;
104
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
105
                    return FALSE;
106
                }
107
                break;
108
            case 'SWD':
109
                $checksum = 11 - $checksum;
110
                if (!preg_match('/[0-9]{8}-[0-9]{1}/i', $id)) {
111
                    return FALSE;
112
                } elseif ($checksum == 10) {
113
                    return self::checkIdentifier(($digits + 1).substr($id, -2, 2), 'SWD');
114
                } elseif (substr($id, -1, 1) != $checksum) {
115
                    return FALSE;
116
                }
117
                break;
118
            case 'GKD':
119
                $checksum = 11 - $checksum;
120
                if ($checksum == 10) {
121
                    $checksum = 'X';
122
                }
123
                if (!preg_match('/[0-9]{8}-[0-9X]{1}/i', $id)) {
124
                    return FALSE;
125
                } elseif (strtoupper(substr($id, -1, 1)) != $checksum) {
126
                    return FALSE;
127
                }
128
                break;
129
        }
130
        return TRUE;
131
    }
132
133
    /**
134
     * Decrypt encrypted value with given control hash
135
     *
136
     * @access public
137
     *
138
     * @param string $encrypted: The encrypted value to decrypt
139
     * @param string $hash: The control hash for decrypting
140
     *
141
     * @return mixed The decrypted value or NULL on error
142
     */
143
    public static function decrypt($encrypted, $hash) {
144
        $decrypted = NULL;
0 ignored issues
show
Unused Code introduced by
The assignment to $decrypted is dead and can be removed.
Loading history...
145
        if (empty($encrypted)
146
            || empty($hash)) {
147
            self::devLog('Invalid parameters given for decryption', DEVLOG_SEVERITY_ERROR);
148
            return;
149
        }
150
        if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
151
            self::devLog('No encryption key set in TYPO3 configuration', DEVLOG_SEVERITY_ERROR);
152
            return;
153
        }
154
        $iv = substr(md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, openssl_cipher_iv_length('BF-CFB'));
155
        $decrypted = openssl_decrypt($encrypted, 'BF-CFB', substr($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 0, 56), 0, $iv);
156
        $salt = substr($hash, 0, 10);
157
        $hashed = $salt.substr(sha1($salt.$decrypted), -10);
158
        if ($hashed !== $hash) {
159
            self::devLog('Invalid hash "'.$hash.'" given for decryption', DEVLOG_SEVERITY_WARNING);
160
            return;
161
        }
162
        return $decrypted;
163
    }
164
165
    /**
166
     * Add a message to the TYPO3 developer log
167
     *
168
     * @access public
169
     *
170
     * @param string $message: The message to log
171
     * @param integer $severity: The severity of the message
172
     *                           0 is info, 1 is notice, 2 is warning, 3 is fatal error, -1 is "OK" message
173
     *
174
     * @return void
175
     */
176
    public static function devLog($message, $severity = 0) {
177
        if (TYPO3_DLOG) {
178
            $stacktrace = debug_backtrace(0, 2);
179
            // Set some defaults.
180
            $caller = 'Kitodo\Dlf\Default\UnknownClass::unknownMethod';
181
            $args = [];
182
            $data = [];
183
            if (!empty($stacktrace[1])) {
184
                $caller = $stacktrace[1]['class'].$stacktrace[1]['type'].$stacktrace[1]['function'];
185
                foreach ($stacktrace[1]['args'] as $arg) {
186
                    if (is_bool($arg)) {
187
                        $args[] = ($arg ? 'TRUE' : 'FALSE');
188
                    } elseif (is_scalar($arg)) {
189
                        $args[] = (string) $arg;
190
                    } elseif (is_null($arg)) {
191
                        $args[] = 'NULL';
192
                    } elseif (is_array($arg)) {
193
                        $args[] = '[data]';
194
                        $data[] = $arg;
195
                    } elseif (is_object($arg)) {
196
                        $args[] = '['.get_class($arg).']';
197
                        $data[] = $arg;
198
                    }
199
                }
200
            }
201
            $arguments = '('.implode(', ', $args).')';
202
            $additionalData = (empty($data) ? FALSE : $data);
203
            \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('['.$caller.$arguments.'] '.$message, self::$extKey, $severity, $additionalData);
204
        }
205
    }
206
207
    /**
208
     * Encrypt the given string
209
     *
210
     * @access public
211
     *
212
     * @param string $string: The string to encrypt
213
     *
214
     * @return array Array with encrypted string and control hash
215
     */
216
    public static function encrypt($string) {
217
        if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
218
            self::devLog('No encryption key set in TYPO3 configuration', DEVLOG_SEVERITY_ERROR);
219
            return;
220
        }
221
        $iv = substr(md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, openssl_cipher_iv_length('BF-CFB'));
222
        $encrypted = openssl_encrypt($string, 'BF-CFB', substr($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 0, 56), 0, $iv);
223
        $salt = substr(md5(uniqid(rand(), TRUE)), 0, 10);
224
        $hash = $salt.substr(sha1($salt.$string), -10);
225
        return ['encrypted' => $encrypted, 'hash' => $hash];
226
    }
227
228
    /**
229
     * Get the unqualified name of a class
230
     *
231
     * @access public
232
     *
233
     * @param string $qualifiedClassname: The qualified class name from get_class()
234
     *
235
     * @return string The unqualified class name
236
     */
237
    public static function getUnqualifiedClassName($qualifiedClassname) {
238
        $nameParts = explode('\\', $qualifiedClassname);
239
        return end($nameParts);
240
    }
241
242
    /**
243
     * Get a backend user object (even in frontend mode)
244
     *
245
     * @access public
246
     *
247
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication Instance of \TYPO3\CMS\Core\Authentication\BackendUserAuthentication or NULL on failure
248
     */
249
    public static function getBeUser() {
250
        if (TYPO3_MODE === 'FE'
251
            || TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
252
            // Initialize backend session with CLI user's rights.
253
            $userObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class);
254
            $userObj->dontSetCookie = TRUE;
255
            $userObj->start();
256
            $userObj->setBeUserByName('_cli_dlf');
257
            $userObj->backendCheckLogin();
258
            return $userObj;
259
        } else {
260
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
261
            return;
262
        }
263
    }
264
265
    /**
266
     * Clean up a string to use in an URL.
267
     *
268
     * @access public
269
     *
270
     * @param string $string: The string to clean up
271
     *
272
     * @return string The cleaned up string
273
     */
274
    public static function getCleanString($string) {
275
        // Convert to lowercase.
276
        $string = strtolower($string);
277
        // Remove non-alphanumeric characters.
278
        $string = preg_replace('/[^a-z0-9_\s-]/', '', $string);
279
        // Remove multiple dashes or whitespaces.
280
        $string = preg_replace('/[\s-]+/', ' ', $string);
281
        // Convert whitespaces and underscore to dash.
282
        $string = preg_replace('/[\s_]/', '-', $string);
283
        return $string;
284
    }
285
286
    /**
287
     * Get the current frontend user object
288
     *
289
     * @access public
290
     *
291
     * @return \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication Instance of \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication or NULL on failure
292
     */
293
    public static function getFeUser() {
294
        if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
295
            // Check if a user is currently logged in.
296
            if (!empty($GLOBALS['TSFE']->loginUser)) {
297
                return $GLOBALS['TSFE']->fe_user;
298
            } elseif (\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('eID') !== NULL) {
299
                return \TYPO3\CMS\Frontend\Utility\EidUtility::initFeUser();
300
            }
301
        } else {
302
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
303
        }
304
        return;
305
    }
306
307
    /**
308
     * Get the registered hook objects for a class
309
     *
310
     * @access public
311
     *
312
     * @param string $scriptRelPath: The path to the class file
313
     *
314
     * @return array Array of hook objects for the class
315
     */
316
    public static function getHookObjects($scriptRelPath) {
317
        $hookObjects = [];
318
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey.'/'.$scriptRelPath]['hookClass'])) {
319
            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey.'/'.$scriptRelPath]['hookClass'] as $classRef) {
320
                $hookObjects[] = &\TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($classRef);
321
            }
322
        }
323
        return $hookObjects;
324
    }
325
326
    /**
327
     * Get the "index_name" for an UID
328
     *
329
     * @access public
330
     *
331
     * @param integer $uid: The UID of the record
332
     * @param string $table: Get the "index_name" from this table
333
     * @param integer $pid: Get the "index_name" from this page
334
     *
335
     * @return string "index_name" for the given UID
336
     */
337
    public static function getIndexNameFromUid($uid, $table, $pid = -1) {
338
        // Sanitize input.
339
        $uid = max(intval($uid), 0);
340
        if (!$uid
341
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])) {
342
            self::devLog('Invalid UID "'.$uid.'" or table "'.$table.'"', DEVLOG_SEVERITY_ERROR);
343
            return '';
344
        }
345
        $where = '';
346
        // Should we check for a specific PID, too?
347
        if ($pid !== -1) {
348
            $pid = max(intval($pid), 0);
349
            $where = ' AND '.$table.'.pid='.$pid;
350
        }
351
        // Get index_name from database.
352
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
353
            $table.'.index_name AS index_name',
354
            $table,
355
            $table.'.uid='.$uid
356
                .$where
357
                .self::whereClause($table),
358
            '',
359
            '',
360
            '1'
361
        );
362
        if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
363
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
364
            return $resArray['index_name'];
365
        } else {
366
            self::devLog('No "index_name" with UID '.$uid.' and PID '.$pid.' found in table "'.$table.'"', DEVLOG_SEVERITY_WARNING);
367
            return '';
368
        }
369
    }
370
371
    /**
372
     * Get language name from ISO code
373
     *
374
     * @access public
375
     *
376
     * @param string $code: ISO 639-1 or ISO 639-2/B language code
377
     *
378
     * @return string Localized full name of language or unchanged input
379
     */
380
    public static function getLanguageName($code) {
381
        // Analyze code and set appropriate ISO table.
382
        $isoCode = strtolower(trim($code));
383
        if (preg_match('/^[a-z]{3}$/', $isoCode)) {
384
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey).'Resources/Private/ISO-639/iso-639-2b.xml';
385
        } elseif (preg_match('/^[a-z]{2}$/', $isoCode)) {
386
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey).'Resources/Private/ISO-639/iso-639-1.xml';
387
        } else {
388
            // No ISO code, return unchanged.
389
            return $code;
390
        }
391
        // Load ISO table and get localized full name of language.
392
        if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
393
            $iso639 = $GLOBALS['TSFE']->readLLfile($file);
394
            if (!empty($iso639['default'][$isoCode])) {
395
                $lang = $GLOBALS['TSFE']->getLLL($isoCode, $iso639);
396
            }
397
        } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
398
            $iso639 = $GLOBALS['LANG']->includeLLFile($file, FALSE, TRUE);
399
            if (!empty($iso639['default'][$isoCode])) {
400
                $lang = $GLOBALS['LANG']->getLLL($isoCode, $iso639, FALSE);
401
            }
402
        } else {
403
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
404
            return $code;
405
        }
406
        if (!empty($lang)) {
407
            return $lang;
408
        } else {
409
            self::devLog('Language code "'.$code.'" not found in ISO-639 table', DEVLOG_SEVERITY_NOTICE);
410
            return $code;
411
        }
412
    }
413
414
    /**
415
     * Wrapper function for getting localized messages in frontend and backend
416
     *
417
     * @access public
418
     *
419
     * @param string $key: The locallang key to translate
420
     * @param boolean $hsc: Should the result be htmlspecialchar()'ed?
421
     * @param string $default: Default return value if no translation is available
422
     *
423
     * @return string The translated string or the given key on failure
424
     */
425
    public static function getMessage($key, $hsc = FALSE, $default = '') {
426
        // Set initial output to default value.
427
        $translated = (string) $default;
428
        // Load common messages file.
429
        if (empty(self::$messages)) {
430
            $file = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath(self::$extKey, 'Resources/Private/Language/FlashMessages.xml');
431
            if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
432
                self::$messages = $GLOBALS['TSFE']->readLLfile($file);
433
            } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
434
                self::$messages = $GLOBALS['LANG']->includeLLFile($file, FALSE, TRUE);
435
            } else {
436
                self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
437
            }
438
        }
439
        // Get translation.
440
        if (!empty(self::$messages['default'][$key])) {
441
            if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
442
                $translated = $GLOBALS['TSFE']->getLLL($key, self::$messages);
443
            } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
444
                $translated = $GLOBALS['LANG']->getLLL($key, self::$messages, FALSE);
445
            } else {
446
                self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
447
            }
448
        }
449
        // Escape HTML characters if applicable.
450
        if ($hsc) {
451
            $translated = htmlspecialchars($translated);
452
        }
453
        return $translated;
454
    }
455
456
    /**
457
     * Get the UID for a given "index_name"
458
     *
459
     * @access public
460
     *
461
     * @param integer $index_name: The index_name of the record
462
     * @param string $table: Get the "index_name" from this table
463
     * @param integer $pid: Get the "index_name" from this page
464
     *
465
     * @return string "uid" for the given index_name
466
     */
467
    public static function getUidFromIndexName($index_name, $table, $pid = -1) {
468
        if (!$index_name
469
            || !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])) {
470
            self::devLog('Invalid UID '.$index_name.' or table "'.$table.'"', DEVLOG_SEVERITY_ERROR);
471
            return '';
472
        }
473
        $where = '';
474
        // Should we check for a specific PID, too?
475
        if ($pid !== -1) {
476
            $pid = max(intval($pid), 0);
477
            $where = ' AND '.$table.'.pid='.$pid;
478
        }
479
        // Get index_name from database.
480
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
481
            $table.'.uid AS uid',
482
            $table,
483
            $table.'.index_name="'.$index_name.'"'
484
                .$where
485
                .self::whereClause($table),
486
            '',
487
            '',
488
            '1'
489
        );
490
        if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
491
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
492
            return $resArray['uid'];
493
        } else {
494
            self::devLog('No UID for given index_name "'.$index_name.'" and PID '.$pid.' found in table "'.$table.'"', DEVLOG_SEVERITY_WARNING);
495
            return '';
496
        }
497
    }
498
499
    /**
500
     * Get the URN of an object
501
     * @see http://www.persistent-identifier.de/?link=316
502
     *
503
     * @access public
504
     *
505
     * @param string $base: The namespace and base URN
506
     * @param string $id: The object's identifier
507
     *
508
     * @return string Uniform Resource Name as string
509
     */
510
    public static function getURN($base, $id) {
511
        $concordance = [
512
            '0' => 1,
513
            '1' => 2,
514
            '2' => 3,
515
            '3' => 4,
516
            '4' => 5,
517
            '5' => 6,
518
            '6' => 7,
519
            '7' => 8,
520
            '8' => 9,
521
            '9' => 41,
522
            'a' => 18,
523
            'b' => 14,
524
            'c' => 19,
525
            'd' => 15,
526
            'e' => 16,
527
            'f' => 21,
528
            'g' => 22,
529
            'h' => 23,
530
            'i' => 24,
531
            'j' => 25,
532
            'k' => 42,
533
            'l' => 26,
534
            'm' => 27,
535
            'n' => 13,
536
            'o' => 28,
537
            'p' => 29,
538
            'q' => 31,
539
            'r' => 12,
540
            's' => 32,
541
            't' => 33,
542
            'u' => 11,
543
            'v' => 34,
544
            'w' => 35,
545
            'x' => 36,
546
            'y' => 37,
547
            'z' => 38,
548
            '-' => 39,
549
            ':' => 17,
550
        ];
551
        $urn = strtolower($base.$id);
552
        if (preg_match('/[^a-z0-9:-]/', $urn)) {
553
            self::devLog('Invalid chars in given parameters', DEVLOG_SEVERITY_WARNING);
554
            return '';
555
        }
556
        $digits = '';
557
        for ($i = 0, $j = strlen($urn); $i < $j; $i++) {
558
            $digits .= $concordance[substr($urn, $i, 1)];
559
        }
560
        $checksum = 0;
561
        for ($i = 0, $j = strlen($digits); $i < $j; $i++) {
562
            $checksum += ($i + 1) * intval(substr($digits, $i, 1));
563
        }
564
        $checksum = substr(intval($checksum / intval(substr($digits, -1, 1))), -1, 1);
565
        return $base.$id.$checksum;
566
    }
567
568
    /**
569
     * Check if given ID is a valid Pica Production Number (PPN)
570
     *
571
     * @access public
572
     *
573
     * @param string $id: The identifier to check
574
     *
575
     * @return boolean Is $id a valid PPN?
576
     */
577
    public static function isPPN($id) {
578
        return self::checkIdentifier($id, 'PPN');
579
    }
580
581
    /**
582
     * Load value from user's session.
583
     *
584
     * @access public
585
     *
586
     * @param string $key: Session data key for retrieval
587
     *
588
     * @return mixed Session value for given key or NULL on failure
589
     */
590
    public static function loadFromSession($key) {
591
        // Cast to string for security reasons.
592
        $key = (string) $key;
593
        if (!$key) {
594
            self::devLog('Invalid key "'.$key.'" for session data retrieval', DEVLOG_SEVERITY_WARNING);
595
            return;
596
        }
597
        // Get the session data.
598
        if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
599
            return $GLOBALS['TSFE']->fe_user->getKey('ses', $key);
600
        } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
601
            return $GLOBALS['BE_USER']->getSessionData($key);
602
        } else {
603
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
604
            return;
605
        }
606
    }
607
608
    /**
609
     * Process a data and/or command map with TYPO3 core engine.
610
     *
611
     * @access public
612
     *
613
     * @param array $data: Data map
614
     * @param array $cmd: Command map
615
     * @param boolean $reverseOrder: Should the command map be processed first?
616
     * @param boolean $be_user: Use current backend user's rights for processing?
617
     *
618
     * @return array Array of substituted "NEW..." identifiers and their actual UIDs.
619
     */
620
    public static function processDB(array $data = [], array $cmd = [], $reverseOrder = FALSE, $be_user = FALSE) {
621
        // Instantiate TYPO3 core engine.
622
        $tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
623
        // Set some configuration variables.
624
        $tce->stripslashes_values = FALSE;
625
        // Get backend user for processing.
626
        if ($be_user
627
            && isset($GLOBALS['BE_USER'])) {
628
            $user = $GLOBALS['BE_USER'];
629
        } else {
630
            $user = self::getBeUser();
631
        }
632
        // Load data and command arrays.
633
        $tce->start($data, $cmd, $user);
634
        // Process command map first if default order is reversed.
635
        if ($cmd
0 ignored issues
show
Bug Best Practice introduced by
The expression $cmd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
636
            && $reverseOrder) {
637
            $tce->process_cmdmap();
638
        }
639
        // Process data map.
640
        if ($data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
641
            $tce->process_datamap();
642
        }
643
        // Process command map if processing order is not reversed.
644
        if ($cmd
0 ignored issues
show
Bug Best Practice introduced by
The expression $cmd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
645
            && !$reverseOrder) {
646
            $tce->process_cmdmap();
647
        }
648
        return $tce->substNEWwithIDs;
649
    }
650
651
    /**
652
     * Process a data and/or command map with TYPO3 core engine as admin.
653
     *
654
     * @access public
655
     *
656
     * @param array $data: Data map
657
     * @param array $cmd: Command map
658
     * @param boolean $reverseOrder: Should the command map be processed first?
659
     *
660
     * @return array Array of substituted "NEW..." identifiers and their actual UIDs.
661
     */
662
    public static function processDBasAdmin(array $data = [], array $cmd = [], $reverseOrder = FALSE) {
663
        if (TYPO3_MODE === 'BE'
664
            && $GLOBALS['BE_USER']->isAdmin()) {
665
            return self::processDB($data, $cmd, $reverseOrder, TRUE);
666
        } else {
667
            self::devLog('Current backend user has no admin privileges', DEVLOG_SEVERITY_ERROR);
668
            return [];
669
        }
670
    }
671
672
    /**
673
     * Fetches and renders all available flash messages from the queue.
674
     *
675
     * @access public
676
     *
677
     * @param string $queue: The queue's unique identifier
678
     *
679
     * @return string All flash messages in the queue rendered as HTML.
680
     */
681
    public static function renderFlashMessages($queue = 'kitodo.default.flashMessages') {
682
        $flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
683
        $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier($queue);
684
        // \TYPO3\CMS\Core\Messaging\FlashMessage::getMessageAsMarkup() uses htmlspecialchars()
685
        // on all messages, but we have messages with HTML tags. Therefore we copy the official
686
        // implementation and remove the htmlspecialchars() call on the message body.
687
        $content = '';
688
        $flashMessages = $flashMessageQueue->getAllMessagesAndFlush();
689
        if (!empty($flashMessages)) {
690
            $content .= '<div class="typo3-messages">';
691
            foreach ($flashMessages as $flashMessage) {
692
                $messageTitle = $flashMessage->getTitle();
693
                $markup = [];
694
                $markup[] = '<div class="alert '.htmlspecialchars($flashMessage->getClass()).'">';
695
                $markup[] = '    <div class="media">';
696
                $markup[] = '        <div class="media-left">';
697
                $markup[] = '            <span class="fa-stack fa-lg">';
698
                $markup[] = '                <i class="fa fa-circle fa-stack-2x"></i>';
699
                $markup[] = '                <i class="fa fa-'.htmlspecialchars($flashMessage->getIconName()).' fa-stack-1x"></i>';
700
                $markup[] = '            </span>';
701
                $markup[] = '        </div>';
702
                $markup[] = '        <div class="media-body">';
703
                if (!empty($messageTitle)) {
704
                    $markup[] = '            <h4 class="alert-title">'.htmlspecialchars($messageTitle).'</h4>';
705
                }
706
                $markup[] = '            <p class="alert-message">'.$flashMessage->getMessage().'</p>'; // Removed htmlspecialchars() here.
707
                $markup[] = '        </div>';
708
                $markup[] = '    </div>';
709
                $markup[] = '</div>';
710
                $content .= implode('', $markup);
711
            }
712
            $content .= '</div>';
713
        }
714
        return $content;
715
    }
716
717
    /**
718
     * Save given value to user's session.
719
     *
720
     * @access public
721
     *
722
     * @param mixed $value: Value to save
723
     * @param string $key: Session data key for saving
724
     *
725
     * @return boolean TRUE on success, FALSE on failure
726
     */
727
    public static function saveToSession($value, $key) {
728
        // Cast to string for security reasons.
729
        $key = (string) $key;
730
        if (!$key) {
731
            self::devLog('Invalid key "'.$key.'" for session data saving', DEVLOG_SEVERITY_WARNING);
732
            return FALSE;
733
        }
734
        // Save value in session data.
735
        if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
736
            $GLOBALS['TSFE']->fe_user->setKey('ses', $key, $value);
737
            $GLOBALS['TSFE']->fe_user->storeSessionData();
738
            return TRUE;
739
        } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
740
            $GLOBALS['BE_USER']->setAndSaveSessionData($key, $value);
741
            return TRUE;
742
        } else {
743
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
744
            return FALSE;
745
        }
746
    }
747
748
    /**
749
     * This translates an internal "index_name"
750
     *
751
     * @access public
752
     *
753
     * @param string $index_name: The internal "index_name" to translate
754
     * @param string $table: Get the translation from this table
755
     * @param string $pid: Get the translation from this page
756
     *
757
     * @return string Localized label for $index_name
758
     */
759
    public static function translate($index_name, $table, $pid) {
760
        // Load labels into static variable for future use.
761
        static $labels = [];
762
        // Sanitize input.
763
        $pid = max(intval($pid), 0);
764
        if (!$pid) {
765
            self::devLog('Invalid PID '.$pid.' for translation', DEVLOG_SEVERITY_WARNING);
766
            return $index_name;
767
        }
768
        // Check if "index_name" is an UID.
769
        if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($index_name)) {
770
            $index_name = self::getIndexNameFromUid($index_name, $table, $pid);
771
        }
772
        /* $labels already contains the translated content element, but with the index_name of the translated content element itself
773
         * and not with the $index_name of the original that we receive here. So we have to determine the index_name of the
774
         * associated translated content element. E.g. $labels['title0'] != $index_name = title. */
775
        // First fetch the uid of the received index_name
776
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
777
            'uid, l18n_parent',
778
            $table,
779
            'pid='.$pid
780
                .' AND index_name="'.$index_name.'"'
781
                .self::whereClause($table, TRUE),
782
            '',
783
            '',
784
            ''
785
        );
786
        if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
787
            // Now we use the uid of the l18_parent to fetch the index_name of the translated content element.
788
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
789
            $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
790
                'index_name',
791
                $table,
792
                'pid='.$pid
793
                    .' AND uid='.$resArray['l18n_parent']
794
                    .' AND sys_language_uid='.intval($GLOBALS['TSFE']->sys_language_content)
795
                    .self::whereClause($table, TRUE),
796
                '',
797
                '',
798
                ''
799
            );
800
            if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
801
                // If there is an translated content element, overwrite the received $index_name.
802
                $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
803
                $index_name = $resArray['index_name'];
804
            }
805
        }
806
        // Check if we already got a translation.
807
        if (empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
808
            // Check if this table is allowed for translation.
809
            if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures'])) {
810
                $additionalWhere = ' AND sys_language_uid IN (-1,0)';
811
                if ($GLOBALS['TSFE']->sys_language_content > 0) {
812
                    $additionalWhere = ' AND (sys_language_uid IN (-1,0) OR (sys_language_uid='.intval($GLOBALS['TSFE']->sys_language_content).' AND l18n_parent=0))';
813
                }
814
                // Get labels from database.
815
                $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
816
                    '*',
817
                    $table,
818
                    'pid='.$pid
819
                        .$additionalWhere
820
                        .self::whereClause($table, TRUE),
821
                    '',
822
                    '',
823
                    ''
824
                );
825
                if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
826
                    while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
827
                        // Overlay localized labels if available.
828
                        if ($GLOBALS['TSFE']->sys_language_content > 0) {
829
                            $resArray = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $resArray, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
830
                        }
831
                        if ($resArray) {
832
                            $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$resArray['index_name']] = $resArray['label'];
833
                        }
834
                    }
835
                } else {
836
                    self::devLog('No translation with PID '.$pid.' available in table "'.$table.'" or translation not accessible', DEVLOG_SEVERITY_NOTICE);
837
                }
838
            } else {
839
                self::devLog('No translations available for table "'.$table.'"', DEVLOG_SEVERITY_WARNING);
840
            }
841
        }
842
        if (!empty($labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name])) {
843
            return $labels[$table][$pid][$GLOBALS['TSFE']->sys_language_content][$index_name];
844
        } else {
845
            return $index_name;
846
        }
847
    }
848
849
    /**
850
     * This returns the additional WHERE clause of a table based on its TCA configuration
851
     *
852
     * @access public
853
     *
854
     * @param string $table: Table name as defined in TCA
855
     * @param boolean $showHidden: Ignore the hidden flag?
856
     *
857
     * @return string Additional WHERE clause
858
     */
859
    public static function whereClause($table, $showHidden = FALSE) {
860
        if (TYPO3_MODE === 'FE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'FE' is always false.
Loading history...
861
            // Table "tx_dlf_formats" always has PID 0.
862
            if ($table == 'tx_dlf_formats') {
863
                return \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table);
864
            }
865
            // Should we ignore the record's hidden flag?
866
            $ignoreHide = -1;
867
            if ($showHidden) {
868
                $ignoreHide = 1;
869
            }
870
            // $GLOBALS['TSFE']->sys_page is not always available in frontend.
871
            if (is_object($GLOBALS['TSFE']->sys_page)) {
872
                return $GLOBALS['TSFE']->sys_page->enableFields($table, $ignoreHide);
873
            } else {
874
                $pageRepository = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
875
                $GLOBALS['TSFE']->includeTCA();
876
                return $pageRepository->enableFields($table, $ignoreHide);
877
            }
878
        } elseif (TYPO3_MODE === 'BE') {
0 ignored issues
show
introduced by
The condition Kitodo\Dlf\Common\TYPO3_MODE === 'BE' is always true.
Loading history...
879
            return \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table);
880
        } else {
881
            self::devLog('Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', DEVLOG_SEVERITY_ERROR);
882
            return ' AND 1=-1';
883
        }
884
    }
885
886
    /**
887
     * This is a static class, thus no instances should be created
888
     *
889
     * @access private
890
     */
891
    private function __construct() {}
892
}
893