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

206
            /** @scrutinizer ignore-call */ 
207
            $document = $this->documentRepository->findOneByPartof($metadata['partof']);
Loading history...
207
208
            if ($document) {
209
                $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

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

255
                $this->logger->/** @scrutinizer ignore-call */ 
256
                               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...
256
            }
257
        } else {
258
            $this->logger->error('Could not load XML file from "' . $record['location'] . '"');
259
        }
260
        return $mets;
261
    }
262
263
    /**
264
     * The main method of the plugin
265
     *
266
     * @return void
267
     */
268
    public function mainAction()
269
    {
270
        // Get allowed GET and POST variables.
271
        $this->getUrlParams();
272
273
        // Get extension configuration.
274
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
275
276
        // Delete expired resumption tokens.
277
        $this->deleteExpiredTokens();
278
279
        switch ($this->parameters['verb']) {
280
            case 'GetRecord':
281
                $this->verbGetRecord();
282
                break;
283
            case 'Identify':
284
                $this->verbIdentify();
285
                break;
286
            case 'ListIdentifiers':
287
                $this->verbListIdentifiers();
288
                break;
289
            case 'ListMetadataFormats':
290
                $this->verbListMetadataFormats();
291
                break;
292
            case 'ListRecords':
293
                $this->verbListRecords();
294
                break;
295
            case 'ListSets':
296
                $this->verbListSets();
297
                break;
298
        }
299
300
        $this->view->assign('parameters', $this->parameters);
301
        $this->view->assign('error', $this->error);
302
303
        return;
304
    }
305
306
    /**
307
     * Continue with resumption token
308
     *
309
     * @access protected
310
     *
311
     * @return \Kitodo\Dlf\Common\DocumentList|null list of uids
312
     */
313
    protected function resume(): ?DocumentList
314
    {
315
        $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

315
        /** @scrutinizer ignore-call */ 
316
        $token = $this->tokenRepository->findOneByToken($this->parameters['resumptionToken']);
Loading history...
316
317
        if ($token) {
318
            $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

318
            /** @scrutinizer ignore-call */ 
319
            $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...
319
        }
320
        if ($options instanceof DocumentList) {
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...
321
            return $options;
322
        } else {
323
            // No resumption token found or resumption token expired.
324
            $this->error = 'badResumptionToken';
325
            return null;
326
        }
327
    }
328
329
    /**
330
     * Process verb "GetRecord"
331
     *
332
     * @access protected
333
     *
334
     * @return void
335
     */
336
    protected function verbGetRecord()
337
    {
338
        if (count($this->parameters) !== 3 || empty($this->parameters['metadataPrefix']) || empty($this->parameters['identifier'])) {
339
            $this->error = 'badArgument';
340
            return;
341
        }
342
343
        if (!array_key_exists($this->parameters['metadataPrefix'], $this->formats)) {
344
            $this->error = 'cannotDisseminateFormat';
345
            return;
346
        }
347
348
        $document = $this->documentRepository->getOaiRecord($this->settings, $this->parameters);
349
350
        if (!$document['uid']) {
351
            $this->error = 'idDoesNotExist';
352
            return;
353
        }
354
355
        // Check for required fields.
356
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
357
            if (empty($document[$required])) {
358
                $this->error = 'cannotDisseminateFormat';
359
                return;
360
            }
361
        }
362
363
        // we need the collections as array later
364
        $document['collections'] = explode(' ', $document['collections']);
365
366
        // Add metadata
367
        switch ($this->parameters['metadataPrefix']) {
368
            case 'oai_dc':
369
                $document['metadata'] = $this->getDcData($document);
370
                break;
371
            case 'epicur':
372
                $document['metadata'] = $document;
373
                break;
374
            case 'mets':
375
                $document['metadata'] = $this->getMetsData($document);
376
                break;
377
        }
378
379
        $this->view->assign('record', $document);
380
    }
381
382
    /**
383
     * Process verb "Identify"
384
     *
385
     * @access protected
386
     *
387
     * @return void
388
     */
389
    protected function verbIdentify()
390
    {
391
        $library = $this->libraryRepository->findByUid($this->settings['library']);
392
393
        $oaiIdentifyInfo = [];
394
395
        if (!$oaiIdentifyInfo) {
0 ignored issues
show
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...
introduced by
$oaiIdentifyInfo is an empty array, thus ! $oaiIdentifyInfo is always true.
Loading history...
396
            $this->logger->notice('Incomplete plugin configuration');
397
        }
398
399
        $oaiIdentifyInfo['oai_label'] = $library->getOaiLabel();
400
        // Use default values for an installation with incomplete plugin configuration.
401
        if (empty($oaiIdentifyInfo['oai_label'])) {
402
            $oaiIdentifyInfo['oai_label'] = 'Kitodo.Presentation OAI-PMH Interface (default configuration)';
403
            $this->logger->notice('Incomplete plugin configuration (oai_label is missing)');
404
        }
405
406
        $oaiIdentifyInfo['contact'] = $library->getContact();
407
        if (empty($oaiIdentifyInfo['contact'])) {
408
            $oaiIdentifyInfo['contact'] = '[email protected]';
409
            $this->logger->notice('Incomplete plugin configuration (contact is missing)');
410
        }
411
412
        $document = $this->documentRepository->findOldestDocument();
413
414
        if ($document) {
415
            $oaiIdentifyInfo['earliestDatestamp'] = gmdate('Y-m-d\TH:i:s\Z', $document->getTstamp()->getTimestamp());
0 ignored issues
show
Bug introduced by
The method getTstamp() 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

415
            $oaiIdentifyInfo['earliestDatestamp'] = gmdate('Y-m-d\TH:i:s\Z', $document->/** @scrutinizer ignore-call */ getTstamp()->getTimestamp());

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...
416
        } else {
417
            // access storagePid from TypoScript
418
            $pageSettings = $this->configurationManager->getConfiguration($this->configurationManager::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
419
            $storagePid = $pageSettings["plugin."]["tx_dlf."]["persistence."]["storagePid"];
420
            if ($storagePid > 0) {
421
                $this->logger->notice('No records found with PID ' . $storagePid);
422
            } else {
423
                $this->logger->notice('No records found');
424
            }
425
        }
426
        $this->view->assign('oaiIdentifyInfo', $oaiIdentifyInfo);
427
    }
428
429
    /**
430
     * Process verb "ListIdentifiers"
431
     *
432
     * @access protected
433
     *
434
     * @return void
435
     */
436
    protected function verbListIdentifiers()
437
    {
438
        // If we have a resumption token we can continue our work
439
        if (!empty($this->parameters['resumptionToken'])) {
440
            // "resumptionToken" is an exclusive argument.
441
            if (count($this->parameters) > 2) {
442
                $this->error = 'badArgument';
443
                return;
444
            } else {
445
                // return next chunk of documents
446
                $resultSet = $this->resume();
447
                if ($resultSet instanceof DocumentList) {
448
                    $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
449
                    $this->view->assign('listIdentifiers', $listIdentifiers);
450
                }
451
                return;
452
            }
453
        }
454
        // "metadataPrefix" is required and "identifier" is not allowed.
455
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
456
            $this->error = 'badArgument';
457
            return;
458
        }
459
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
460
            $this->error = 'cannotDisseminateFormat';
461
            return;
462
        }
463
        try {
464
            $documentSet = $this->fetchDocumentUIDs();
465
        } catch (\Exception $exception) {
466
            $this->error = 'idDoesNotExist';
467
            return;
468
        }
469
        // create new and empty documentlist
470
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
471
        $resultSet->reset();
472
        if (is_array($documentSet)) {
473
            $resultSet->add($documentSet);
474
            $resultSet->metadata = [
475
                'completeListSize' => count($documentSet),
476
                'metadataPrefix' => $this->parameters['metadataPrefix'],
477
            ];
478
        }
479
480
        $listIdentifiers = $this->generateOutputForDocumentList($resultSet);
481
        $this->view->assign('listIdentifiers', $listIdentifiers);
482
    }
483
484
    /**
485
     * Process verb "ListMetadataFormats"
486
     *
487
     * @access protected
488
     *
489
     * @return void
490
     */
491
    protected function verbListMetadataFormats()
492
    {
493
        $resArray = [];
494
        // check for the optional "identifier" parameter
495
        if (isset($this->parameters['identifier'])) {
496
            $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

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