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 — dev-extbase-fluid (#784)
by Alexander
03:07
created

Helper::getUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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