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 (#727)
by Alexander
02:43
created

OaiPmhController::verbGetRecord()   C

Complexity

Conditions 12
Paths 22

Size

Total Lines 73
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 49
c 2
b 0
f 0
dl 0
loc 73
rs 6.9666
cc 12
nc 22
nop 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
4
 *
5
 * This file is part of the Kitodo and TYPO3 projects.
6
 *
7
 * @license GNU General Public License version 3 or later.
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace Kitodo\Dlf\Controller;
13
14
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
15
use TYPO3\CMS\Core\Utility\GeneralUtility;
16
use Kitodo\Dlf\Common\DocumentList;
17
use Kitodo\Dlf\Common\Helper;
18
use Kitodo\Dlf\Common\Solr;
19
use TYPO3\CMS\Core\Database\Connection;
20
use TYPO3\CMS\Core\Database\ConnectionPool;
21
22
/**
23
 * Controller for the plugin 'OAI-PMH Interface' for the 'dlf' extension
24
 *
25
 * @author Sebastian Meyer <[email protected]>
26
 * @package TYPO3
27
 * @subpackage dlf
28
 * @access public
29
 */
30
class OaiPmhController extends AbstractController
31
{
32
    /**
33
     * Initializes the current action
34
     *
35
     * @return void
36
     */
37
    public function initializeAction()
38
    {
39
        $this->request->setFormat('xml');
40
    }
41
42
    /**
43
     * Did an error occur?
44
     *
45
     * @var string
46
     * @access protected
47
     */
48
    protected $error;
49
50
    /**
51
     * This holds the configuration for all supported metadata prefixes
52
     *
53
     * @var array
54
     * @access protected
55
     */
56
    protected $formats = [
57
        'oai_dc' => [
58
            'schema' => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
59
            'namespace' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
60
            'requiredFields' => ['record_id'],
61
        ],
62
        'epicur' => [
63
            'schema' => 'http://www.persistent-identifier.de/xepicur/version1.0/xepicur.xsd',
64
            'namespace' => 'urn:nbn:de:1111-2004033116',
65
            'requiredFields' => ['purl', 'urn'],
66
        ],
67
        'mets' => [
68
            'schema' => 'http://www.loc.gov/standards/mets/version17/mets.v1-7.xsd',
69
            'namespace' => 'http://www.loc.gov/METS/',
70
            'requiredFields' => ['location'],
71
        ]
72
    ];
73
74
    /**
75
     * @var ExtensionConfiguration
76
     */
77
    protected $extensionConfiguration;
78
79
    /**
80
     * @var array
81
     */
82
    protected $parameters = [];
83
84
    /**
85
     * Delete expired resumption tokens
86
     *
87
     * @access protected
88
     *
89
     * @return void
90
     */
91
    protected function deleteExpiredTokens()
92
    {
93
        // Delete expired resumption tokens.
94
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_dlf_tokens');
95
96
        $result = $queryBuilder
97
            ->delete('tx_dlf_tokens')
98
            ->where(
99
                $queryBuilder->expr()->eq('tx_dlf_tokens.ident', $queryBuilder->createNamedParameter('oai')),
100
                $queryBuilder->expr()->lt('tx_dlf_tokens.tstamp',
101
                    $queryBuilder->createNamedParameter((int) ($GLOBALS['EXEC_TIME'] - $this->settings['expired'])))
102
            )
103
            ->execute();
104
105
        if ($result === -1) {
106
            // Deletion failed.
107
            $this->logger->warning('Could not delete expired resumption tokens');
1 ignored issue
show
Bug introduced by
The method warning() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

107
            $this->logger->/** @scrutinizer ignore-call */ 
108
                           warning('Could not delete expired resumption tokens');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
108
        }
109
    }
110
111
    /**
112
     * Load URL parameters
113
     *
114
     * @access protected
115
     *
116
     * @return void
117
     */
118
    protected function getUrlParams()
119
    {
120
        $allowedParams = [
121
            'verb',
122
            'identifier',
123
            'metadataPrefix',
124
            'from',
125
            'until',
126
            'set',
127
            'resumptionToken'
128
        ];
129
        // Clear plugin variables.
130
        $this->parameters = [];
131
        // Set only allowed parameters.
132
        foreach ($allowedParams as $param) {
133
            if (GeneralUtility::_GP($param)) {
134
                $this->parameters[$param] = GeneralUtility::_GP($param);
135
            }
136
        }
137
    }
138
139
    /**
140
     * Get unqualified Dublin Core data.
141
     * @see http://www.openarchives.org/OAI/openarchivesprotocol.html#dublincore
142
     *
143
     * @access protected
144
     *
145
     * @param array $record : The full record array
146
     *
147
     * @return array $metadata: The mapped metadata array
148
     */
149
    protected function getDcData(array $record)
150
    {
151
        $metadata = [];
152
153
        $metadata[] = ['dc:identifier' => $record['record_id']];
154
155
        if (!empty($record['purl'])) {
156
            $metadata[] = ['dc:identifier' => $record['purl']];
157
        }
158
        if (!empty($record['prod_id'])) {
159
            $metadata[] = ['dc:identifier' => $record['prod_id']];
160
        }
161
        if (!empty($record['urn'])) {
162
            $metadata[] = ['dc:identifier' => $record['urn']];
163
        }
164
        if (!empty($record['title'])) {
165
            $metadata[] = ['dc:title' => $record['title']];
166
        }
167
        if (!empty($record['author'])) {
168
            $metadata[] = ['dc:creator' => $record['author']];
169
        }
170
        if (!empty($record['year'])) {
171
            $metadata[] = ['dc:date' => $record['year']];
172
        }
173
        if (!empty($record['place'])) {
174
            $metadata[] = ['dc:coverage' => $record['place']];
175
        }
176
        $record[] = ['dc:format' => $record['application/mets+xml']];
177
        $record[] = ['dc:type' => $record['Text']];
178
        if (!empty($record['partof'])) {
179
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
180
                ->getQueryBuilderForTable('tx_dlf_documents');
181
182
            $result = $queryBuilder
183
                ->select('tx_dlf_documents.record_id')
184
                ->from('tx_dlf_documents')
185
                ->where(
186
                    $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($metadata['partof'])),
187
                    Helper::whereExpression('tx_dlf_documents')
188
                )
189
                ->setMaxResults(1)
190
                ->execute();
191
192
            if ($partof = $result->fetch()) {
193
                $metadata[] = ['dc:relation' => $partof['record_id']];
194
            }
195
        }
196
        if (!empty($record['license'])) {
197
            $metadata[] = ['dc:rights' => $record['license']];
198
        }
199
        if (!empty($record['terms'])) {
200
            $metadata[] = ['dc:rights' => $record['terms']];
201
        }
202
        if (!empty($record['restrictions'])) {
203
            $metadata[] = ['dc:rights' => $record['restrictions']];
204
        }
205
        if (!empty($record['out_of_print'])) {
206
            $metadata[] = ['dc:rights' => $record['out_of_print']];
207
        }
208
        if (!empty($record['rights_info'])) {
209
            $metadata[] = ['dc:rights' => $record['rights_info']];
210
        }
211
        return $metadata;
212
    }
213
214
215
    /**
216
     * Get METS data.
217
     * @see http://www.loc.gov/standards/mets/docs/mets.v1-7.html
218
     *
219
     * @access protected
220
     *
221
     * @param array $record : The full record array
222
     *
223
     * @return string: The fetched METS XML
224
     */
225
    protected function getMetsData(array $record)
226
    {
227
        $mets = null;
228
        // Load METS file.
229
        $xml = new \DOMDocument();
230
        if ($xml->load($record['location'])) {
231
            // Get root element.
232
            $root = $xml->getElementsByTagNameNS($this->formats['mets']['namespace'], 'mets');
233
            if ($root->item(0) instanceof \DOMNode) {
234
                // Import node into \DOMDocument.
235
                $mets = $xml->saveXML();
236
                // Remove leading line
237
                $mets = substr($mets, strpos($mets, '>'));
238
            } else {
239
                $this->logger->error('No METS part found in document with location "' . $record['location'] . '"');
240
            }
241
        } else {
242
            $this->logger->error('Could not load XML file from "' . $record['location'] . '"');
243
        }
244
        return $mets;
245
    }
246
247
    /**
248
     * The main method of the plugin
249
     *
250
     * @return void
251
     */
252
    public function mainAction()
253
    {
254
        // Get allowed GET and POST variables.
255
        $this->getUrlParams();
256
257
        // Get extension configuration.
258
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
259
260
        // Delete expired resumption tokens.
261
        $this->deleteExpiredTokens();
262
263
        switch ($this->parameters['verb']) {
264
            case 'GetRecord':
265
                $this->verbGetRecord();
266
                break;
267
            case 'Identify':
268
                $this->verbIdentify();
269
                break;
270
            case 'ListIdentifiers':
271
                $this->verbListIdentifiers();
272
                break;
273
            case 'ListMetadataFormats':
274
                $this->verbListMetadataFormats();
275
                break;
276
            case 'ListRecords':
277
                $this->verbListRecords();
278
                break;
279
            case 'ListSets':
280
                $this->verbListSets();
281
                break;
282
        }
283
284
        $this->view->assign('parameters', $this->parameters);
285
        $this->view->assign('error', $this->error);
286
287
        return;
288
    }
289
290
    /**
291
     * Continue with resumption token
292
     *
293
     * @access protected
294
     *
295
     * @return \Kitodo\Dlf\Common\DocumentList|null list of uids
296
     */
297
    protected function resume(): ?DocumentList
298
    {
299
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
300
            ->getQueryBuilderForTable('tx_dlf_tokens');
301
302
        // Get resumption token.
303
        $result = $queryBuilder
304
            ->select('tx_dlf_tokens.options AS options')
305
            ->from('tx_dlf_tokens')
306
            ->where(
307
                $queryBuilder->expr()->eq('tx_dlf_tokens.ident', $queryBuilder->createNamedParameter('oai')),
308
                $queryBuilder->expr()->eq('tx_dlf_tokens.token',
309
                    $queryBuilder->expr()->literal($this->parameters['resumptionToken'])
310
                )
311
            )
312
            ->setMaxResults(1)
313
            ->execute();
314
315
        if ($resArray = $result->fetch()) {
316
            return unserialize($resArray['options']);
317
        } else {
318
            // No resumption token found or resumption token expired.
319
            $this->error = 'badResumptionToken';
320
            return;
321
        }
322
    }
323
324
    /**
325
     * Process verb "GetRecord"
326
     *
327
     * @access protected
328
     *
329
     * @return void
330
     */
331
    protected function verbGetRecord()
332
    {
333
        if (count($this->parameters) !== 3 || empty($this->parameters['metadataPrefix']) || empty($this->parameters['identifier'])) {
334
            $this->error = 'badArgument';
335
            return;
336
        }
337
        if (!array_key_exists($this->parameters['metadataPrefix'], $this->formats)) {
338
            $this->error = 'cannotDisseminateFormat';
339
            return;
340
        }
341
        $where = '';
342
        if (!$this->settings['show_userdefined']) {
343
            $where .= 'AND tx_dlf_collections.fe_cruser_id=0 ';
344
        }
345
346
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_dlf_documents');
347
348
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
349
            'FROM `tx_dlf_documents` ' .
350
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
351
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
352
            'WHERE `tx_dlf_documents`.`record_id` = ? ' .
353
            'AND `tx_dlf_documents`.`pid` = ? ' .
354
            'AND `tx_dlf_collections`.`pid` = ? ' .
355
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
356
            $where .
357
            'AND ' . Helper::whereExpression('tx_dlf_collections');
358
359
        $values = [
360
            $this->parameters['identifier'],
361
            $this->settings['pages'],
362
            $this->settings['pages']
363
        ];
364
        $types = [
365
            Connection::PARAM_STR,
366
            Connection::PARAM_INT,
367
            Connection::PARAM_INT
368
        ];
369
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
370
        $statement = $connection->executeQuery($sql, $values, $types);
371
372
        $resArray = $statement->fetch();
373
374
        if (!$resArray['uid']) {
375
            $this->error = 'idDoesNotExist';
376
            return;
377
        }
378
379
        // Check for required fields.
380
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
381
            if (empty($resArray[$required])) {
382
                $this->error = 'cannotDisseminateFormat';
383
                return;
384
            }
385
        }
386
387
        // we need the collections as array later
388
        $resArray['collections'] = explode(' ', $resArray['collections']);
389
390
        // Add metadata
391
        switch ($this->parameters['metadataPrefix']) {
392
            case 'oai_dc':
393
                $resArray['metadata'] = $this->getDcData($resArray);
394
                break;
395
            case 'epicur':
396
                $resArray['metadata'] = $resArray;
397
                break;
398
            case 'mets':
399
                $resArray['metadata'] = $this->getMetsData($resArray);
400
                break;
401
        }
402
403
        $this->view->assign('record', $resArray);
404
    }
405
406
    /**
407
     * Process verb "Identify"
408
     *
409
     * @access protected
410
     *
411
     * @return void
412
     */
413
    protected function verbIdentify()
414
    {
415
        // Get repository name and administrative contact.
416
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
417
            ->getQueryBuilderForTable('tx_dlf_libraries');
418
419
        $result = $queryBuilder
420
            ->select(
421
                'tx_dlf_libraries.oai_label AS oai_label',
422
                'tx_dlf_libraries.contact AS contact'
423
            )
424
            ->from('tx_dlf_libraries')
425
            ->where(
426
                $queryBuilder->expr()->eq('tx_dlf_libraries.pid', intval($this->settings['pages'])),
427
                $queryBuilder->expr()->eq('tx_dlf_libraries.uid', intval($this->settings['library']))
428
            )
429
            ->setMaxResults(1)
430
            ->execute();
431
432
        $oaiIdentifyInfo = $result->fetch();
433
        if (!$oaiIdentifyInfo) {
434
            $this->logger->notice('Incomplete plugin configuration');
435
        }
436
437
        // Use default values for an installation with incomplete plugin configuration.
438
        if (empty($oaiIdentifyInfo['oai_label'])) {
439
            $oaiIdentifyInfo['oai_label'] = 'Kitodo.Presentation OAI-PMH Interface (default configuration)';
440
            $this->logger->notice('Incomplete plugin configuration (oai_label is missing)');
441
        }
442
443
        if (empty($oaiIdentifyInfo['contact'])) {
444
            $oaiIdentifyInfo['contact'] = '[email protected]';
445
            $this->logger->notice('Incomplete plugin configuration (contact is missing)');
446
        }
447
448
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
449
            ->getQueryBuilderForTable('tx_dlf_documents');
450
451
        $result = $queryBuilder
452
            ->select('tx_dlf_documents.tstamp AS tstamp')
453
            ->from('tx_dlf_documents')
454
            ->where(
455
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($this->settings['pages']))
456
            )
457
            ->orderBy('tx_dlf_documents.tstamp')
458
            ->setMaxResults(1)
459
            ->execute();
460
461
        if ($resArray = $result->fetch()) {
462
            $oaiIdentifyInfo['earliestDatestamp'] = gmdate('Y-m-d\TH:i:s\Z', $resArray['tstamp']);
463
        } else {
464
            $this->logger->notice('No records found with PID ' . $this->settings['pages']);
465
        }
466
        $this->view->assign('oaiIdentifyInfo', $oaiIdentifyInfo);
467
    }
468
469
    /**
470
     * Process verb "ListIdentifiers"
471
     *
472
     * @access protected
473
     *
474
     * @return void
475
     */
476
    protected function verbListIdentifiers()
477
    {
478
        // If we have a resumption token we can continue our work
479
        if (!empty($this->parameters['resumptionToken'])) {
480
            // "resumptionToken" is an exclusive argument.
481
            if (count($this->parameters) > 2) {
482
                $this->error = 'badArgument';
483
                return;
484
            } else {
485
                // return next chunk of documents
486
                $resultSet = $this->resume();
487
                if ($resultSet instanceof DocumentList) {
488
                    $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
489
                    $this->view->assign('listIdentifiers', $listIdentifiers);
490
                }
491
                return;
492
            }
493
        }
494
        // "metadataPrefix" is required and "identifier" is not allowed.
495
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
496
            $this->error = 'badArgument';
497
            return;
498
        }
499
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
500
            $this->error = 'cannotDisseminateFormat';
501
            return;
502
        }
503
        try {
504
            $documentSet = $this->fetchDocumentUIDs();
505
        } catch (\Exception $exception) {
506
            $this->error = 'idDoesNotExist';
507
            return;
508
        }
509
        // create new and empty documentlist
510
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
511
        $resultSet->reset();
512
        if (is_array($documentSet)) {
513
            $resultSet->add($documentSet);
514
            $resultSet->metadata = [
515
                'completeListSize' => count($documentSet),
516
                'metadataPrefix' => $this->parameters['metadataPrefix'],
517
            ];
518
        }
519
520
        $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
521
        $this->view->assign('listIdentifiers', $listIdentifiers);
522
    }
523
524
    /**
525
     * Process verb "ListMetadataFormats"
526
     *
527
     * @access protected
528
     *
529
     * @return void
530
     */
531
    protected function verbListMetadataFormats()
532
    {
533
        $resArray = [];
534
        // check for the optional "identifier" parameter
535
        if (isset($this->parameters['identifier'])) {
536
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
537
                ->getQueryBuilderForTable('tx_dlf_documents');
538
539
            // Check given identifier.
540
            $result = $queryBuilder
541
                ->select('tx_dlf_documents.*')
542
                ->from('tx_dlf_documents')
543
                ->where(
544
                    $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($this->settings['pages'])),
545
                    $queryBuilder->expr()->eq('tx_dlf_documents.record_id',
546
                    $queryBuilder->expr()->literal($this->parameters['identifier']))
547
                )
548
                ->orderBy('tx_dlf_documents.tstamp')
549
                ->setMaxResults(1)
550
                ->execute();
551
552
            $resArray = $result->fetch();
553
        }
554
555
        $resultSet = [];
556
        foreach ($this->formats as $prefix => $details) {
557
            if (!empty($resArray)) {
558
                // check, if all required fields are available for a given identifier
559
                foreach ($details['requiredFields'] as $required) {
560
                    if (empty($resArray[$required])) {
561
                        // Skip metadata formats whose requirements are not met.
562
                        continue 2;
563
                    }
564
                }
565
            }
566
            $details['prefix'] = $prefix;
567
            $resultSet[] = $details;
568
        }
569
        $this->view->assign('metadataFormats', $resultSet);
570
    }
571
572
    /**
573
     * Process verb "ListRecords"
574
     *
575
     * @access protected
576
     *
577
     * @return void
578
     */
579
    protected function verbListRecords()
580
    {
581
        // Check for invalid arguments.
582
        if (!empty($this->parameters['resumptionToken'])) {
583
            // "resumptionToken" is an exclusive argument.
584
            if (count($this->parameters) > 2) {
585
                $this->error = 'badArgument';
586
                return;
587
            } else {
588
                // return next chunk of documents
589
                $resultSet = $this->resume();
590
                if ($resultSet instanceof DocumentList) {
591
                    $listRecords = $this->generateOutputForDocumentList($resultSet);
592
                    $this->parameters['metadataPrefix'] = $resultSet->metadata['metadataPrefix'];
593
                    $this->view->assign('listRecords', $listRecords);
594
                }
595
                return;
596
            }
597
        }
598
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
599
            // "metadataPrefix" is required and "identifier" is not allowed.
600
            $this->error = 'badArgument';
601
            return;
602
        }
603
        // Check "metadataPrefix" for valid value.
604
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
605
            $this->error = 'cannotDisseminateFormat';
606
            return;
607
        }
608
        try {
609
            $documentSet = $this->fetchDocumentUIDs();
610
        } catch (\Exception $exception) {
611
            $this->error = 'idDoesNotExist';
612
            return;
613
        }
614
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
615
        $resultSet->reset();
616
        if (is_array($documentSet)) {
617
            $resultSet->add($documentSet);
618
            $resultSet->metadata = [
619
                'completeListSize' => count($documentSet),
620
                'metadataPrefix' => $this->parameters['metadataPrefix'],
621
            ];
622
        }
623
624
        $resultSet = $this->generateOutputForDocumentList($resultSet);
625
        $this->view->assign('listRecords', $resultSet);
626
    }
627
628
    /**
629
     * Process verb "ListSets"
630
     *
631
     * @access protected
632
     *
633
     * @return void
634
     */
635
    protected function verbListSets()
636
    {
637
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
638
            ->getQueryBuilderForTable('tx_dlf_collections');
639
640
        // Check for invalid arguments.
641
        if (count($this->parameters) > 1) {
642
            if (!empty($this->parameters['resumptionToken'])) {
643
                $this->error = 'badResumptionToken';
644
                return;
645
            } else {
646
                $this->error = 'badArgument';
647
                return;
648
            }
649
        }
650
        $where = '';
651
        if (!$this->settings['show_userdefined']) {
652
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
653
        }
654
655
        $result = $queryBuilder
656
            ->select(
657
                'tx_dlf_collections.oai_name AS oai_name',
658
                'tx_dlf_collections.label AS label'
659
            )
660
            ->from('tx_dlf_collections')
661
            ->where(
662
                $queryBuilder->expr()->in('tx_dlf_collections.sys_language_uid', [-1, 0]),
663
                $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
664
                $queryBuilder->expr()->neq('tx_dlf_collections.oai_name', $queryBuilder->createNamedParameter('')),
665
                $where,
666
                Helper::whereExpression('tx_dlf_collections')
667
            )
668
            ->orderBy('tx_dlf_collections.oai_name')
669
            ->execute();
670
671
        $allResults = $result->fetchAll();
672
673
        $this->view->assign('oaiSets', $allResults);
674
    }
675
676
    /**
677
     * Fetch records
678
     *
679
     * @access protected
680
     *
681
     * @return array|null Array of matching records
682
     */
683
    protected function fetchDocumentUIDs()
684
    {
685
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
686
            ->getQueryBuilderForTable('tx_dlf_collections');
687
688
        $solr_query = '';
689
        $where = '';
690
        if (!$this->settings['show_userdefined']) {
691
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
692
        }
693
        // Check "set" for valid value.
694
        if (!empty($this->parameters['set'])) {
695
            // For SOLR we need the index_name of the collection,
696
            // For DB Query we need the UID of the collection
697
            $result = $queryBuilder
698
                ->select(
699
                    'tx_dlf_collections.index_name AS index_name',
700
                    'tx_dlf_collections.uid AS uid',
701
                    'tx_dlf_collections.index_search as index_query'
702
                )
703
                ->from('tx_dlf_collections')
704
                ->where(
705
                    $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
706
                    $queryBuilder->expr()->eq('tx_dlf_collections.oai_name',
707
                        $queryBuilder->expr()->literal($this->parameters['set'])),
708
                    $where,
709
                    Helper::whereExpression('tx_dlf_collections')
710
                )
711
                ->setMaxResults(1)
712
                ->execute();
713
714
            $allResults = $result->fetchAll();
715
716
            if (count($allResults) < 1) {
717
                $this->error = 'noSetHierarchy';
718
                return;
719
            }
720
            $resArray = $allResults[0];
721
            if ($resArray['index_query'] != "") {
722
                $solr_query .= '(' . $resArray['index_query'] . ')';
723
            } else {
724
                $solr_query .= 'collection:' . '"' . $resArray['index_name'] . '"';
725
            }
726
        } else {
727
            // If no set is specified we have to query for all collections
728
            $solr_query .= 'collection:* NOT collection:""';
729
        }
730
        // Check for required fields.
731
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
732
            $solr_query .= ' NOT ' . $required . ':""';
733
        }
734
        // toplevel="true" is always required
735
        $solr_query .= ' AND toplevel:true';
736
        $from = "*";
737
        // Check "from" for valid value.
738
        if (!empty($this->parameters['from'])) {
739
            // Is valid format?
740
            if (
741
                is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%dT%H:%M:%SZ'))
742
                || is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%d'))
743
            ) {
744
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
745
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
746
                $from = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.000Z';
747
            } else {
748
                $this->error = 'badArgument';
749
                return;
750
            }
751
        }
752
        $until = "*";
753
        // Check "until" for valid value.
754
        if (!empty($this->parameters['until'])) {
755
            // Is valid format?
756
            if (
757
                is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%dT%H:%M:%SZ'))
758
                || is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%d'))
759
            ) {
760
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
761
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
762
                $until = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.999Z';
763
                if ($from != "*" && $from > $until) {
764
                    $this->error = 'badArgument';
765
                }
766
            } else {
767
                $this->error = 'badArgument';
768
            }
769
        }
770
        // Check "from" and "until" for same granularity.
771
        if (
772
            !empty($this->parameters['from'])
773
            && !empty($this->parameters['until'])
774
        ) {
775
            if (strlen($this->parameters['from']) != strlen($this->parameters['until'])) {
776
                $this->error = 'badArgument';
777
            }
778
        }
779
        $solr_query .= ' AND timestamp:[' . $from . ' TO ' . $until . ']';
780
        $documentSet = [];
781
        $solr = Solr::getInstance($this->settings['solrcore']);
782
        if (!$solr->ready) {
783
            $this->logger->error('Apache Solr not available');
784
            return $documentSet;
785
        }
786
        if (intval($this->settings['solr_limit']) > 0) {
787
            $solr->limit = intval($this->settings['solr_limit']);
788
        }
789
        // We only care about the UID in the results and want them sorted
790
        $parameters = [
791
            "fields" => "uid",
792
            "sort" => [
793
                "uid" => "asc"
794
            ]
795
        ];
796
        $result = $solr->search_raw($solr_query, $parameters);
797
        if (empty($result)) {
798
            $this->error = 'noRecordsMatch';
799
            return;
800
        }
801
        foreach ($result as $doc) {
802
            $documentSet[] = $doc->uid;
803
        }
804
        return $documentSet;
805
    }
806
807
    /**
808
     * Fetch more information for document list
809
     * @access protected
810
     *
811
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
812
     *
813
     * @return array of enriched records
814
     */
815
    protected function generateOutputForDocumentList(DocumentList $documentListSet)
816
    {
817
        $documentsToProcess = $documentListSet->removeRange(0, (int) $this->settings['limit']);
818
        if ($documentsToProcess === null) {
819
            $this->error = 'noRecordsMatch';
820
            return [];
821
        }
822
        $verb = $this->parameters['verb'];
823
824
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
825
            ->getConnectionForTable('tx_dlf_documents');
826
827
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
828
            'FROM `tx_dlf_documents` ' .
829
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
830
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
831
            'WHERE `tx_dlf_documents`.`uid` IN ( ? ) ' .
832
            'AND `tx_dlf_documents`.`pid` = ? ' .
833
            'AND `tx_dlf_collections`.`pid` = ? ' .
834
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
835
            'AND ' . Helper::whereExpression('tx_dlf_collections') . ' ' .
836
            'GROUP BY `tx_dlf_documents`.`uid` ' .
837
            'LIMIT ?';
838
839
        $values = [
840
            $documentsToProcess,
841
            $this->settings['pages'],
842
            $this->settings['pages'],
843
            $this->settings['limit']
844
        ];
845
        $types = [
846
            Connection::PARAM_INT_ARRAY,
847
            Connection::PARAM_INT,
848
            Connection::PARAM_INT,
849
            Connection::PARAM_INT
850
        ];
851
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
852
        $documents = $connection->executeQuery($sql, $values, $types);
853
854
        $records = [];
855
        while ($resArray = $documents->fetch()) {
856
            // we need the collections as array later
857
            $resArray['collections'] = explode(' ', $resArray['collections']);
858
859
            if ($verb === 'ListRecords') {
860
                // Add metadata node.
861
                $metadataPrefix = $this->parameters['metadataPrefix'];
862
                if (!$metadataPrefix) {
863
                    // If we resume an action the metadataPrefix is stored with the documentSet
864
                    $metadataPrefix = $documentListSet->metadata['metadataPrefix'];
865
                }
866
                switch ($metadataPrefix) {
867
                    case 'oai_dc':
868
                        $resArray['metadata'] = $this->getDcData($resArray);
869
                        break;
870
                    case 'epicur':
871
                        $resArray['metadata'] = $resArray;
872
                        break;
873
                    case 'mets':
874
                        $resArray['metadata'] = $this->getMetsData($resArray);
875
                        break;
876
                }
877
            }
878
879
            $records[] = $resArray;
880
        }
881
882
        $this->generateResumptionTokenForDocumentListSet($documentListSet);
883
884
        return $records;
885
    }
886
887
    /**
888
     * Generate resumption token
889
     *
890
     * @access protected
891
     *
892
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
893
     *
894
     * @return void
895
     */
896
    protected function generateResumptionTokenForDocumentListSet(DocumentList $documentListSet)
897
    {
898
        if ($documentListSet->count() !== 0) {
899
            $token = uniqid('', false);
900
901
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_dlf_tokens');
902
            $affectedRows = $queryBuilder
903
                ->insert('tx_dlf_tokens')
904
                ->values([
905
                    'tstamp' => $GLOBALS['EXEC_TIME'],
906
                    'token' => $token,
907
                    'options' => serialize($documentListSet),
908
                    'ident' => 'oai',
909
                ])
910
                ->execute();
911
912
            if ($affectedRows === 1) {
913
                $resumptionToken = $token;
914
            } else {
915
                $this->logger->error('Could not create resumption token');
916
                $this->error = 'badResumptionToken';
917
                return;
918
            }
919
        } else {
920
            // Result set complete. We don't need a token.
921
            $resumptionToken = '';
922
        }
923
924
        $resumptionTokenInfo = [];
925
        $resumptionTokenInfo['token'] = $resumptionToken;
926
        $resumptionTokenInfo['cursor'] = $documentListSet->metadata['completeListSize'] - count($documentListSet);
927
        $resumptionTokenInfo['completeListSize'] = $documentListSet->metadata['completeListSize'];
928
        $expireDateTime = new \DateTime();
929
        $expireDateTime->add(new \DateInterval('PT' . $this->settings['expired'] . 'S'));
930
        $resumptionTokenInfo['expired'] = $expireDateTime;
931
932
        $this->view->assign('resumptionToken', $resumptionTokenInfo);
933
    }
934
}
935