Completed
Push — master ( ce3674...ce3674 )
by
unknown
15s queued 12s
created

OaiPmhController::getDcData()   F

Complexity

Conditions 15
Paths 12288

Size

Total Lines 53
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 33
nc 12288
nop 1
dl 0
loc 53
rs 1.7499
c 1
b 0
f 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\Utility\GeneralUtility;
15
use Kitodo\Dlf\Common\Solr;
16
use Kitodo\Dlf\Domain\Model\Token;
17
use Kitodo\Dlf\Domain\Repository\CollectionRepository;
18
use Kitodo\Dlf\Domain\Repository\LibraryRepository;
19
use Kitodo\Dlf\Domain\Repository\TokenRepository;
20
21
/**
22
 * Controller class for the plugin 'OAI-PMH Interface'.
23
 *
24
 * @author Sebastian Meyer <[email protected]>
25
 * @author Alexander Bigga <[email protected]>
26
 * @package TYPO3
27
 * @subpackage dlf
28
 * @access public
29
 */
30
class OaiPmhController extends AbstractController
31
{
32
    /**
33
     * @var TokenRepository
34
     */
35
    protected $tokenRepository;
36
37
    /**
38
     * @param TokenRepository $tokenRepository
39
     */
40
    public function injectTokenRepository(TokenRepository $tokenRepository)
41
    {
42
        $this->tokenRepository = $tokenRepository;
43
    }
44
45
    /**
46
     * @var CollectionRepository
47
     */
48
    protected $collectionRepository;
49
50
    /**
51
     * @param CollectionRepository $collectionRepository
52
     */
53
    public function injectCollectionRepository(CollectionRepository $collectionRepository)
54
    {
55
        $this->collectionRepository = $collectionRepository;
56
    }
57
58
    /**
59
     * @var LibraryRepository
60
     */
61
    protected $libraryRepository;
62
63
    /**
64
     * @param LibraryRepository $libraryRepository
65
     */
66
    public function injectLibraryRepository(LibraryRepository $libraryRepository)
67
    {
68
        $this->libraryRepository = $libraryRepository;
69
    }
70
71
    /**
72
     * Initializes the current action
73
     *
74
     * @return void
75
     */
76
    public function initializeAction()
77
    {
78
        $this->request->setFormat('xml');
79
    }
80
81
    /**
82
     * Did an error occur?
83
     *
84
     * @var string
85
     * @access protected
86
     */
87
    protected $error;
88
89
    /**
90
     * This holds the configuration for all supported metadata prefixes
91
     *
92
     * @var array
93
     * @access protected
94
     */
95
    protected $formats = [
96
        'oai_dc' => [
97
            'schema' => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
98
            'namespace' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
99
            'requiredFields' => ['record_id'],
100
        ],
101
        'epicur' => [
102
            'schema' => 'http://www.persistent-identifier.de/xepicur/version1.0/xepicur.xsd',
103
            'namespace' => 'urn:nbn:de:1111-2004033116',
104
            'requiredFields' => ['purl', 'urn'],
105
        ],
106
        'mets' => [
107
            'schema' => 'http://www.loc.gov/standards/mets/version17/mets.v1-7.xsd',
108
            'namespace' => 'http://www.loc.gov/METS/',
109
            'requiredFields' => ['location'],
110
        ]
111
    ];
112
113
    /**
114
     * @var array
115
     */
116
    protected $parameters = [];
117
118
    /**
119
     * Delete expired resumption tokens
120
     *
121
     * @access protected
122
     *
123
     * @return void
124
     */
125
    protected function deleteExpiredTokens()
126
    {
127
        // Delete expired resumption tokens.
128
        $this->tokenRepository->deleteExpiredTokens($this->settings['expired']);
129
    }
130
131
    /**
132
     * Load URL parameters
133
     *
134
     * @access protected
135
     *
136
     * @return void
137
     */
138
    protected function getUrlParams()
139
    {
140
        $allowedParams = [
141
            'verb',
142
            'identifier',
143
            'metadataPrefix',
144
            'from',
145
            'until',
146
            'set',
147
            'resumptionToken'
148
        ];
149
        // Clear plugin variables.
150
        $this->parameters = [];
151
        // Set only allowed parameters.
152
        foreach ($allowedParams as $param) {
153
            if (GeneralUtility::_GP($param)) {
154
                $this->parameters[$param] = GeneralUtility::_GP($param);
155
            }
156
        }
157
    }
158
159
    /**
160
     * Get unqualified Dublin Core data.
161
     * @see http://www.openarchives.org/OAI/openarchivesprotocol.html#dublincore
162
     *
163
     * @access protected
164
     *
165
     * @param array $record : The full record array
166
     *
167
     * @return array $metadata: The mapped metadata array
168
     */
169
    protected function getDcData(array $record)
170
    {
171
        $metadata = [];
172
173
        $metadata[] = ['dc:identifier' => $record['record_id']];
174
175
        if (!empty($record['purl'])) {
176
            $metadata[] = ['dc:identifier' => $record['purl']];
177
        }
178
        if (!empty($record['prod_id'])) {
179
            $metadata[] = ['dc:identifier' => $record['prod_id']];
180
        }
181
        if (!empty($record['urn'])) {
182
            $metadata[] = ['dc:identifier' => $record['urn']];
183
        }
184
        if (!empty($record['title'])) {
185
            $metadata[] = ['dc:title' => $record['title']];
186
        }
187
        if (!empty($record['author'])) {
188
            $metadata[] = ['dc:creator' => $record['author']];
189
        }
190
        if (!empty($record['year'])) {
191
            $metadata[] = ['dc:date' => $record['year']];
192
        }
193
        if (!empty($record['place'])) {
194
            $metadata[] = ['dc:coverage' => $record['place']];
195
        }
196
        $record[] = ['dc:format' => $record['application/mets+xml']];
197
        $record[] = ['dc:type' => $record['Text']];
198
        if (!empty($record['partof'])) {
199
200
            $document = $this->documentRepository->findOneByPartof($metadata['partof']);
0 ignored issues
show
Bug introduced by
The method findOneByPartof() does not exist on Kitodo\Dlf\Domain\Repository\DocumentRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

200
            /** @scrutinizer ignore-call */ 
201
            $document = $this->documentRepository->findOneByPartof($metadata['partof']);
Loading history...
201
202
            if ($document) {
203
                $metadata[] = ['dc:relation' => $document->getRecordId()];
0 ignored issues
show
Bug introduced by
The method getRecordId() does not exist on TYPO3\CMS\Extbase\Persistence\QueryResultInterface. ( Ignorable by Annotation )

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

203
                $metadata[] = ['dc:relation' => $document->/** @scrutinizer ignore-call */ getRecordId()];

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...
204
            }
205
        }
206
        if (!empty($record['license'])) {
207
            $metadata[] = ['dc:rights' => $record['license']];
208
        }
209
        if (!empty($record['terms'])) {
210
            $metadata[] = ['dc:rights' => $record['terms']];
211
        }
212
        if (!empty($record['restrictions'])) {
213
            $metadata[] = ['dc:rights' => $record['restrictions']];
214
        }
215
        if (!empty($record['out_of_print'])) {
216
            $metadata[] = ['dc:rights' => $record['out_of_print']];
217
        }
218
        if (!empty($record['rights_info'])) {
219
            $metadata[] = ['dc:rights' => $record['rights_info']];
220
        }
221
        return $metadata;
222
    }
223
224
225
    /**
226
     * Get METS data.
227
     * @see http://www.loc.gov/standards/mets/docs/mets.v1-7.html
228
     *
229
     * @access protected
230
     *
231
     * @param array $record : The full record array
232
     *
233
     * @return string: The fetched METS XML
234
     */
235
    protected function getMetsData(array $record)
236
    {
237
        $mets = null;
238
        // Load METS file.
239
        $xml = new \DOMDocument();
240
        if ($xml->load($record['location'])) {
241
            // Get root element.
242
            $root = $xml->getElementsByTagNameNS($this->formats['mets']['namespace'], 'mets');
243
            if ($root->item(0) instanceof \DOMNode) {
244
                $mets = $xml->saveXML($root->item(0));
245
            } else {
246
                $this->logger->error('No METS part found in document with location "' . $record['location'] . '"');
0 ignored issues
show
Bug introduced by
The method error() 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

246
                $this->logger->/** @scrutinizer ignore-call */ 
247
                               error('No METS part found in document with location "' . $record['location'] . '"');

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...
247
            }
248
        } else {
249
            $this->logger->error('Could not load XML file from "' . $record['location'] . '"');
250
        }
251
        return $mets;
252
    }
253
254
    /**
255
     * The main method of the plugin
256
     *
257
     * @return void
258
     */
259
    public function mainAction()
260
    {
261
        // Get allowed GET and POST variables.
262
        $this->getUrlParams();
263
264
        // Delete expired resumption tokens.
265
        $this->deleteExpiredTokens();
266
267
        switch ($this->parameters['verb']) {
268
            case 'GetRecord':
269
                $this->verbGetRecord();
270
                break;
271
            case 'Identify':
272
                $this->verbIdentify();
273
                break;
274
            case 'ListIdentifiers':
275
                $this->verbListIdentifiers();
276
                break;
277
            case 'ListMetadataFormats':
278
                $this->verbListMetadataFormats();
279
                break;
280
            case 'ListRecords':
281
                $this->verbListRecords();
282
                break;
283
            case 'ListSets':
284
                $this->verbListSets();
285
                break;
286
            default:
287
                $this->error = 'badVerb';
288
                break;
289
        }
290
291
        $this->view->assign('parameters', $this->parameters);
292
        $this->view->assign('error', $this->error);
293
294
        return;
295
    }
296
297
    /**
298
     * Continue with resumption token
299
     *
300
     * @access protected
301
     *
302
     * @return array|null list of uids
303
     */
304
    protected function resume(): ?array
305
    {
306
        $token = $this->tokenRepository->findOneByToken($this->parameters['resumptionToken']);
0 ignored issues
show
Bug introduced by
The method findOneByToken() does not exist on Kitodo\Dlf\Domain\Repository\TokenRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

306
        /** @scrutinizer ignore-call */ 
307
        $token = $this->tokenRepository->findOneByToken($this->parameters['resumptionToken']);
Loading history...
307
308
        if ($token) {
309
            $options = $token->getOptions();
0 ignored issues
show
Bug introduced by
The method getOptions() does not exist on TYPO3\CMS\Extbase\Persistence\QueryResultInterface. ( Ignorable by Annotation )

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

309
            /** @scrutinizer ignore-call */ 
310
            $options = $token->getOptions();

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...
310
        }
311
        if (is_array($options)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options does not seem to be defined for all execution paths leading up to this point.
Loading history...
312
            return $options;
313
        } else {
314
            // No resumption token found or resumption token expired.
315
            $this->error = 'badResumptionToken';
316
            return null;
317
        }
318
    }
319
320
    /**
321
     * Process verb "GetRecord"
322
     *
323
     * @access protected
324
     *
325
     * @return void
326
     */
327
    protected function verbGetRecord()
328
    {
329
        if (count($this->parameters) !== 3 || empty($this->parameters['metadataPrefix']) || empty($this->parameters['identifier'])) {
330
            $this->error = 'badArgument';
331
            return;
332
        }
333
334
        if (!array_key_exists($this->parameters['metadataPrefix'], $this->formats)) {
335
            $this->error = 'cannotDisseminateFormat';
336
            return;
337
        }
338
339
        $document = $this->documentRepository->getOaiRecord($this->settings, $this->parameters);
340
341
        if (!$document['uid']) {
342
            $this->error = 'idDoesNotExist';
343
            return;
344
        }
345
346
        // Check for required fields.
347
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
348
            if (empty($document[$required])) {
349
                $this->error = 'cannotDisseminateFormat';
350
                return;
351
            }
352
        }
353
354
        // we need the collections as array later
355
        $document['collections'] = explode(' ', $document['collections']);
356
357
        // Add metadata
358
        switch ($this->parameters['metadataPrefix']) {
359
            case 'oai_dc':
360
                $document['metadata'] = $this->getDcData($document);
361
                break;
362
            case 'epicur':
363
                $document['metadata'] = $document;
364
                break;
365
            case 'mets':
366
                $document['metadata'] = $this->getMetsData($document);
367
                break;
368
        }
369
370
        $this->view->assign('record', $document);
371
    }
372
373
    /**
374
     * Process verb "Identify"
375
     *
376
     * @access protected
377
     *
378
     * @return void
379
     */
380
    protected function verbIdentify()
381
    {
382
        $library = $this->libraryRepository->findByUid($this->settings['library']);
383
384
        $oaiIdentifyInfo = [];
385
386
        if (!$oaiIdentifyInfo) {
0 ignored issues
show
introduced by
$oaiIdentifyInfo is an empty array, thus ! $oaiIdentifyInfo is always true.
Loading history...
Bug Best Practice introduced by
The expression $oaiIdentifyInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
387
            $this->logger->notice('Incomplete plugin configuration');
388
        }
389
390
        $oaiIdentifyInfo['oai_label'] = $library->getOaiLabel();
391
        // Use default values for an installation with incomplete plugin configuration.
392
        if (empty($oaiIdentifyInfo['oai_label'])) {
393
            $oaiIdentifyInfo['oai_label'] = 'Kitodo.Presentation OAI-PMH Interface (default configuration)';
394
            $this->logger->notice('Incomplete plugin configuration (oai_label is missing)');
395
        }
396
397
        $oaiIdentifyInfo['contact'] = $library->getContact();
398
        if (empty($oaiIdentifyInfo['contact'])) {
399
            $oaiIdentifyInfo['contact'] = '[email protected]';
400
            $this->logger->notice('Incomplete plugin configuration (contact is missing)');
401
        }
402
403
        $document = $this->documentRepository->findOldestDocument();
404
405
        if ($document) {
406
            $oaiIdentifyInfo['earliestDatestamp'] = gmdate('Y-m-d\TH:i:s\Z', $document->getTstamp()->getTimestamp());
407
        } else {
408
            // Provide a fallback timestamp if no document is found
409
            $oaiIdentifyInfo['earliestDatestamp'] = '0000-00-00T00:00:00Z';
410
411
            // access storagePid from TypoScript
412
            $pageSettings = $this->configurationManager->getConfiguration($this->configurationManager::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
413
            $storagePid = $pageSettings["plugin."]["tx_dlf."]["persistence."]["storagePid"];
414
            if ($storagePid > 0) {
415
                $this->logger->notice('No records found with PID ' . $storagePid);
416
            } else {
417
                $this->logger->notice('No records found');
418
            }
419
        }
420
        $this->view->assign('oaiIdentifyInfo', $oaiIdentifyInfo);
421
    }
422
423
    /**
424
     * Process verb "ListIdentifiers"
425
     *
426
     * @access protected
427
     *
428
     * @return void
429
     */
430
    protected function verbListIdentifiers()
431
    {
432
        // If we have a resumption token we can continue our work
433
        if (!empty($this->parameters['resumptionToken'])) {
434
            // "resumptionToken" is an exclusive argument.
435
            if (count($this->parameters) > 2) {
436
                $this->error = 'badArgument';
437
                return;
438
            } else {
439
                // return next chunk of documents
440
                $resultSet = $this->resume();
441
                if (is_array($resultSet)) {
442
                    $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
443
                    $this->view->assign('listIdentifiers', $listIdentifiers);
444
                }
445
                return;
446
            }
447
        }
448
        // "metadataPrefix" is required and "identifier" is not allowed.
449
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
450
            $this->error = 'badArgument';
451
            return;
452
        }
453
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
454
            $this->error = 'cannotDisseminateFormat';
455
            return;
456
        }
457
        try {
458
            $documentSet = $this->fetchDocumentUIDs();
459
        } catch (\Exception $exception) {
460
            $this->error = 'idDoesNotExist';
461
            return;
462
        }
463
        // create new and empty documentlist
464
        $resultSet = [];
465
        if (is_array($documentSet)) {
466
            $resultSet['elements'] = $documentSet;
467
            $resultSet['metadata'] = [
468
                'cursor' => 0,
469
                'completeListSize' => count($documentSet),
470
                'metadataPrefix' => $this->parameters['metadataPrefix'],
471
            ];
472
        }
473
474
        $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
475
        $this->view->assign('listIdentifiers', $listIdentifiers);
476
    }
477
478
    /**
479
     * Process verb "ListMetadataFormats"
480
     *
481
     * @access protected
482
     *
483
     * @return void
484
     */
485
    protected function verbListMetadataFormats()
486
    {
487
        $resArray = [];
488
        // check for the optional "identifier" parameter
489
        if (isset($this->parameters['identifier'])) {
490
            $resArray = $this->documentRepository->findOneByRecordId($this->parameters['identifier']);
0 ignored issues
show
Bug introduced by
The method findOneByRecordId() does not exist on Kitodo\Dlf\Domain\Repository\DocumentRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

490
            /** @scrutinizer ignore-call */ 
491
            $resArray = $this->documentRepository->findOneByRecordId($this->parameters['identifier']);
Loading history...
491
        }
492
493
        $resultSet = [];
494
        foreach ($this->formats as $prefix => $details) {
495
            if (!empty($resArray)) {
496
                // check, if all required fields are available for a given identifier
497
                foreach ($details['requiredFields'] as $required) {
498
                    $methodName = 'get' . GeneralUtility::underscoredToUpperCamelCase($required);
499
                    if (empty($resArray->$methodName())) {
500
                        // Skip metadata formats whose requirements are not met.
501
                        continue 2;
502
                    }
503
                }
504
            }
505
            $details['prefix'] = $prefix;
506
            $resultSet[] = $details;
507
        }
508
        $this->view->assign('metadataFormats', $resultSet);
509
    }
510
511
    /**
512
     * Process verb "ListRecords"
513
     *
514
     * @access protected
515
     *
516
     * @return void
517
     */
518
    protected function verbListRecords()
519
    {
520
        // Check for invalid arguments.
521
        if (!empty($this->parameters['resumptionToken'])) {
522
            // "resumptionToken" is an exclusive argument.
523
            if (count($this->parameters) > 2) {
524
                $this->error = 'badArgument';
525
                return;
526
            } else {
527
                // return next chunk of documents
528
                $resultSet = $this->resume();
529
                if (is_array($resultSet)) {
530
                    $listRecords = $this->generateOutputForDocumentList($resultSet);
531
                    $this->parameters['metadataPrefix'] = $resultSet['metadata']['metadataPrefix'];
532
                    $this->view->assign('listRecords', $listRecords);
533
                }
534
                return;
535
            }
536
        }
537
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
538
            // "metadataPrefix" is required and "identifier" is not allowed.
539
            $this->error = 'badArgument';
540
            return;
541
        }
542
        // Check "metadataPrefix" for valid value.
543
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
544
            $this->error = 'cannotDisseminateFormat';
545
            return;
546
        }
547
        try {
548
            $documentSet = $this->fetchDocumentUIDs();
549
        } catch (\Exception $exception) {
550
            $this->error = 'idDoesNotExist';
551
            return;
552
        }
553
        $resultSet = [];
554
        if (is_array($documentSet)) {
555
            $resultSet['elements'] = $documentSet;
556
            $resultSet['metadata'] = [
557
                'cursor' => 0,
558
                'completeListSize' => count($documentSet),
559
                'metadataPrefix' => $this->parameters['metadataPrefix'],
560
            ];
561
        }
562
563
        $resultSet = $this->generateOutputForDocumentList($resultSet);
564
        $this->view->assign('listRecords', $resultSet);
565
    }
566
567
    /**
568
     * Process verb "ListSets"
569
     *
570
     * @access protected
571
     *
572
     * @return void
573
     */
574
    protected function verbListSets()
575
    {
576
        // It is required to set a oai_name inside the collection record to be shown in oai-pmh plugin.
577
        $this->settings['hideEmptyOaiNames'] = true;
578
579
        $oaiSets = $this->collectionRepository->findCollectionsBySettings($this->settings);
580
581
        $this->view->assign('oaiSets', $oaiSets);
582
    }
583
584
    /**
585
     * Fetch records
586
     *
587
     * @access protected
588
     *
589
     * @return array|null Array of matching records
590
     */
591
    protected function fetchDocumentUIDs()
592
    {
593
        $solr_query = '';
594
        // Check "set" for valid value.
595
        if (!empty($this->parameters['set'])) {
596
            // For SOLR we need the index_name of the collection,
597
            // For DB Query we need the UID of the collection
598
599
            $result = $this->collectionRepository->getIndexNameForSolr($this->settings, $this->parameters['set']);
600
601
            if ($resArray = $result->fetch()) {
602
                if ($resArray['index_query'] != "") {
603
                    $solr_query .= '(' . $resArray['index_query'] . ')';
604
                } else {
605
                    $solr_query .= 'collection:' . '"' . $resArray['index_name'] . '"';
606
                }
607
            } else {
608
                $this->error = 'noSetHierarchy';
609
                return null;
610
            }
611
        } else {
612
            // If no set is specified we have to query for all collections
613
            $solr_query .= 'collection:* NOT collection:""';
614
        }
615
        // Check for required fields.
616
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
617
            $solr_query .= ' NOT ' . $required . ':""';
618
        }
619
        // toplevel="true" is always required
620
        $solr_query .= ' AND toplevel:true';
621
        $from = "*";
622
        // Check "from" for valid value.
623
        if (!empty($this->parameters['from'])) {
624
            // Is valid format?
625
            if (
626
                is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%dT%H:%M:%SZ'))
0 ignored issues
show
introduced by
The condition is_array($date_array = s... '%Y-%m-%dT%H:%M:%SZ')) is always true.
Loading history...
627
                || is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%d'))
628
            ) {
629
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
630
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
631
                $from = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.000Z';
632
            } else {
633
                $this->error = 'badArgument';
634
                return;
635
            }
636
        }
637
        $until = "*";
638
        // Check "until" for valid value.
639
        if (!empty($this->parameters['until'])) {
640
            // Is valid format?
641
            if (
642
                is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%dT%H:%M:%SZ'))
0 ignored issues
show
introduced by
The condition is_array($date_array = s... '%Y-%m-%dT%H:%M:%SZ')) is always true.
Loading history...
643
                || is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%d'))
644
            ) {
645
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
646
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
647
                $until = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.999Z';
648
                if ($from != "*" && $from > $until) {
649
                    $this->error = 'badArgument';
650
                }
651
            } else {
652
                $this->error = 'badArgument';
653
            }
654
        }
655
        // Check "from" and "until" for same granularity.
656
        if (
657
            !empty($this->parameters['from'])
658
            && !empty($this->parameters['until'])
659
        ) {
660
            if (strlen($this->parameters['from']) != strlen($this->parameters['until'])) {
661
                $this->error = 'badArgument';
662
            }
663
        }
664
        $solr_query .= ' AND timestamp:[' . $from . ' TO ' . $until . ']';
665
        $documentSet = [];
666
        $solr = Solr::getInstance($this->settings['solrcore']);
667
        if (!$solr->ready) {
668
            $this->logger->error('Apache Solr not available');
669
            return $documentSet;
670
        }
671
        if (intval($this->settings['solr_limit']) > 0) {
672
            $solr->limit = intval($this->settings['solr_limit']);
673
        }
674
        // We only care about the UID in the results and want them sorted
675
        $parameters = [
676
            "fields" => "uid",
677
            "sort" => [
678
                "uid" => "asc"
679
            ]
680
        ];
681
        $parameters['query'] = $solr_query;
682
        $result = $solr->search_raw($parameters);
683
        if (empty($result)) {
684
            $this->error = 'noRecordsMatch';
685
            return;
686
        }
687
        foreach ($result as $doc) {
688
            $documentSet[] = $doc->uid;
689
        }
690
        return $documentSet;
691
    }
692
693
    /**
694
     * Fetch more information for document list
695
     * @access protected
696
     *
697
     * @param array $documentListSet
698
     *
699
     * @return array of enriched records
700
     */
701
    protected function generateOutputForDocumentList(array $documentListSet)
702
    {
703
        $documentsToProcess = array_splice($documentListSet['elements'], 0, (int) $this->settings['limit']);
704
        if (empty($documentsToProcess)) {
705
            $this->error = 'noRecordsMatch';
706
            return [];
707
        }
708
        $verb = $this->parameters['verb'];
709
710
        $documents = $this->documentRepository->getOaiDocumentList($this->settings, $documentsToProcess);
711
712
        $records = [];
713
        while ($resArray = $documents->fetch()) {
714
            // we need the collections as array later
715
            $resArray['collections'] = explode(' ', $resArray['collections']);
716
717
            if ($verb === 'ListRecords') {
718
                // Add metadata node.
719
                $metadataPrefix = $this->parameters['metadataPrefix'];
720
                if (!$metadataPrefix) {
721
                    // If we resume an action the metadataPrefix is stored with the documentSet
722
                    $metadataPrefix = $documentListSet['metadata']['metadataPrefix'];
723
                }
724
                switch ($metadataPrefix) {
725
                    case 'oai_dc':
726
                        $resArray['metadata'] = $this->getDcData($resArray);
727
                        break;
728
                    case 'epicur':
729
                        $resArray['metadata'] = $resArray;
730
                        break;
731
                    case 'mets':
732
                        $resArray['metadata'] = $this->getMetsData($resArray);
733
                        break;
734
                }
735
            }
736
737
            $records[] = $resArray;
738
        }
739
740
        $this->generateResumptionTokenForDocumentListSet($documentListSet, count($documentsToProcess));
741
742
        return $records;
743
    }
744
745
    /**
746
     * Generate resumption token
747
     *
748
     * @access protected
749
     *
750
     * @param array $documentListSet
751
     * @param int $numShownDocuments
752
     *
753
     * @return void
754
     */
755
    protected function generateResumptionTokenForDocumentListSet(array $documentListSet, int $numShownDocuments)
756
    {
757
        // The cursor specifies how many elements have already been returned in previous requests
758
        // See http://www.openarchives.org/OAI/openarchivesprotocol.html#FlowControl
759
        $currentCursor = $documentListSet['metadata']['cursor'];
760
761
        if (count($documentListSet['elements']) !== 0) {
762
            $resumptionToken = uniqid('', false);
763
764
            $documentListSet['metadata']['cursor'] += $numShownDocuments;
765
766
            // create new token
767
            $newToken = GeneralUtility::makeInstance(Token::class);
768
            $newToken->setToken($resumptionToken);
769
            $newToken->setOptions($documentListSet);
770
771
            // add to tokenRepository
772
            $this->tokenRepository->add($newToken);
773
        } else {
774
            // Result set complete. We don't need a token.
775
            $resumptionToken = '';
776
        }
777
778
        $resumptionTokenInfo = [];
779
        $resumptionTokenInfo['token'] = $resumptionToken;
780
        $resumptionTokenInfo['cursor'] = $currentCursor;
781
        $resumptionTokenInfo['completeListSize'] = $documentListSet['metadata']['completeListSize'];
782
        $expireDateTime = new \DateTime();
783
        $expireDateTime->add(new \DateInterval('PT' . $this->settings['expired'] . 'S'));
784
        $resumptionTokenInfo['expired'] = $expireDateTime;
785
786
        $omitResumptionToken = $currentCursor === 0 && $numShownDocuments >= $documentListSet['metadata']['completeListSize'];
787
        if (!$omitResumptionToken) {
788
            $this->view->assign('resumptionToken', $resumptionTokenInfo);
789
        }
790
    }
791
}
792