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.
Completed
Push — master ( c23e92...610884 )
by Sebastian
19s queued 15s
created

Helper::getIndexNameFromUid()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

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