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:34
created

OaiPmhController::verbListIdentifiers()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 29
c 1
b 0
f 0
dl 0
loc 44
rs 8.2114
cc 8
nc 7
nop 0
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 null;
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
        $resultSet->add($documentSet);
513
        $resultSet->metadata = [
514
            'completeListSize' => count($documentSet),
515
            'metadataPrefix' => $this->parameters['metadataPrefix'],
516
        ];
517
518
        $listIdentifiers =  $this->generateOutputForDocumentList($resultSet);
519
        $this->view->assign('listIdentifiers', $listIdentifiers);
520
    }
521
522
    /**
523
     * Process verb "ListMetadataFormats"
524
     *
525
     * @access protected
526
     *
527
     * @return void
528
     */
529
    protected function verbListMetadataFormats()
530
    {
531
        $resArray = [];
532
        // check for the optional "identifier" parameter
533
        if (isset($this->parameters['identifier'])) {
534
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
535
                ->getQueryBuilderForTable('tx_dlf_documents');
536
537
            // Check given identifier.
538
            $result = $queryBuilder
539
                ->select('tx_dlf_documents.*')
540
                ->from('tx_dlf_documents')
541
                ->where(
542
                    $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($this->settings['pages'])),
543
                    $queryBuilder->expr()->eq('tx_dlf_documents.record_id',
544
                    $queryBuilder->expr()->literal($this->parameters['identifier']))
545
                )
546
                ->orderBy('tx_dlf_documents.tstamp')
547
                ->setMaxResults(1)
548
                ->execute();
549
550
            $resArray = $result->fetch();
551
        }
552
553
        $resultSet = [];
554
        foreach ($this->formats as $prefix => $details) {
555
            if (!empty($resArray)) {
556
                // check, if all required fields are available for a given identifier
557
                foreach ($details['requiredFields'] as $required) {
558
                    if (empty($resArray[$required])) {
559
                        // Skip metadata formats whose requirements are not met.
560
                        continue 2;
561
                    }
562
                }
563
            }
564
            $details['prefix'] = $prefix;
565
            $resultSet[] = $details;
566
        }
567
        $this->view->assign('metadataFormats', $resultSet);
568
    }
569
570
    /**
571
     * Process verb "ListRecords"
572
     *
573
     * @access protected
574
     *
575
     * @return void
576
     */
577
    protected function verbListRecords()
578
    {
579
        // Check for invalid arguments.
580
        if (!empty($this->parameters['resumptionToken'])) {
581
            // "resumptionToken" is an exclusive argument.
582
            if (count($this->parameters) > 2) {
583
                $this->error = 'badArgument';
584
                return;
585
            } else {
586
                // return next chunk of documents
587
                $resultSet = $this->resume();
588
                if ($resultSet instanceof DocumentList) {
589
                    $listRecords =  $this->generateOutputForDocumentList($resultSet);
590
                    $this->parameters['metadataPrefix'] = $resultSet->metadata['metadataPrefix'];
591
                    $this->view->assign('listRecords', $listRecords);
592
                }
593
                return;
594
            }
595
        }
596
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
597
            // "metadataPrefix" is required and "identifier" is not allowed.
598
            $this->error = 'badArgument';
599
            return;
600
        }
601
        // Check "metadataPrefix" for valid value.
602
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
603
            $this->error = 'cannotDisseminateFormat';
604
            return;
605
        }
606
        try {
607
            $documentSet = $this->fetchDocumentUIDs();
608
        } catch (\Exception $exception) {
609
            $this->error = 'idDoesNotExist';
610
            return;
611
        }
612
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
613
        $resultSet->reset();
614
        $resultSet->add($documentSet);
615
        $resultSet->metadata = [
616
            'completeListSize' => count($documentSet),
617
            'metadataPrefix' => $this->parameters['metadataPrefix'],
618
        ];
619
620
        $resultSet =  $this->generateOutputForDocumentList($resultSet);
621
        $this->view->assign('listRecords', $resultSet);
622
    }
623
624
    /**
625
     * Process verb "ListSets"
626
     *
627
     * @access protected
628
     *
629
     * @return void
630
     */
631
    protected function verbListSets()
632
    {
633
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
634
            ->getQueryBuilderForTable('tx_dlf_collections');
635
636
        // Check for invalid arguments.
637
        if (count($this->parameters) > 1) {
638
            if (!empty($this->parameters['resumptionToken'])) {
639
                $this->error = 'badResumptionToken';
640
                return;
641
            } else {
642
                $this->error = 'badArgument';
643
                return;
644
            }
645
        }
646
        $where = '';
647
        if (!$this->settings['show_userdefined']) {
648
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
649
        }
650
651
        $result = $queryBuilder
652
            ->select(
653
                'tx_dlf_collections.oai_name AS oai_name',
654
                'tx_dlf_collections.label AS label'
655
            )
656
            ->from('tx_dlf_collections')
657
            ->where(
658
                $queryBuilder->expr()->in('tx_dlf_collections.sys_language_uid', [-1, 0]),
659
                $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
660
                $queryBuilder->expr()->neq('tx_dlf_collections.oai_name', $queryBuilder->createNamedParameter('')),
661
                $where,
662
                Helper::whereExpression('tx_dlf_collections')
663
            )
664
            ->orderBy('tx_dlf_collections.oai_name')
665
            ->execute();
666
667
        $allResults = $result->fetchAll();
668
669
        $this->view->assign('oaiSets', $allResults);
670
    }
671
672
    /**
673
     * Fetch records
674
     *
675
     * @access protected
676
     *
677
     * @return array Array of matching records
678
     */
679
    protected function fetchDocumentUIDs()
680
    {
681
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
682
            ->getQueryBuilderForTable('tx_dlf_collections');
683
684
        $solr_query = '';
685
        $where = '';
686
        if (!$this->settings['show_userdefined']) {
687
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
688
        }
689
        // Check "set" for valid value.
690
        if (!empty($this->parameters['set'])) {
691
            // For SOLR we need the index_name of the collection,
692
            // For DB Query we need the UID of the collection
693
            $result = $queryBuilder
694
                ->select(
695
                    'tx_dlf_collections.index_name AS index_name',
696
                    'tx_dlf_collections.uid AS uid',
697
                    'tx_dlf_collections.index_search as index_query'
698
                )
699
                ->from('tx_dlf_collections')
700
                ->where(
701
                    $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
702
                    $queryBuilder->expr()->eq('tx_dlf_collections.oai_name',
703
                        $queryBuilder->expr()->literal($this->parameters['set'])),
704
                    $where,
705
                    Helper::whereExpression('tx_dlf_collections')
706
                )
707
                ->setMaxResults(1)
708
                ->execute();
709
710
            $allResults = $result->fetchAll();
711
712
            if (count($allResults) < 1) {
713
                $this->error = 'noSetHierarchy';
714
                return;
715
            }
716
            $resArray = $allResults[0];
717
            if ($resArray['index_query'] != "") {
718
                $solr_query .= '(' . $resArray['index_query'] . ')';
719
            } else {
720
                $solr_query .= 'collection:' . '"' . $resArray['index_name'] . '"';
721
            }
722
        } else {
723
            // If no set is specified we have to query for all collections
724
            $solr_query .= 'collection:* NOT collection:""';
725
        }
726
        // Check for required fields.
727
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
728
            $solr_query .= ' NOT ' . $required . ':""';
729
        }
730
        // toplevel="true" is always required
731
        $solr_query .= ' AND toplevel:true';
732
        $from = "*";
733
        // Check "from" for valid value.
734
        if (!empty($this->parameters['from'])) {
735
            // Is valid format?
736
            if (
737
                is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%dT%H:%M:%SZ'))
1 ignored issue
show
introduced by
The condition is_array($date_array = s... '%Y-%m-%dT%H:%M:%SZ')) is always true.
Loading history...
738
                || is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%d'))
739
            ) {
740
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
741
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
742
                $from = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.000Z';
743
            } else {
744
                $this->error = 'badArgument';
745
                return;
746
            }
747
        }
748
        $until = "*";
749
        // Check "until" for valid value.
750
        if (!empty($this->parameters['until'])) {
751
            // Is valid format?
752
            if (
753
                is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%dT%H:%M:%SZ'))
1 ignored issue
show
introduced by
The condition is_array($date_array = s... '%Y-%m-%dT%H:%M:%SZ')) is always true.
Loading history...
754
                || is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%d'))
755
            ) {
756
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
757
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
758
                $until = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.999Z';
759
                if ($from != "*" && $from > $until) {
760
                    $this->error = 'badArgument';
761
                }
762
            } else {
763
                $this->error = 'badArgument';
764
            }
765
        }
766
        // Check "from" and "until" for same granularity.
767
        if (
768
            !empty($this->parameters['from'])
769
            && !empty($this->parameters['until'])
770
        ) {
771
            if (strlen($this->parameters['from']) != strlen($this->parameters['until'])) {
772
                $this->error = 'badArgument';
773
            }
774
        }
775
        $solr_query .= ' AND timestamp:[' . $from . ' TO ' . $until . ']';
776
        $documentSet = [];
777
        $solr = Solr::getInstance($this->settings['solrcore']);
778
        if (!$solr->ready) {
779
            $this->logger->error('Apache Solr not available');
780
            return $documentSet;
781
        }
782
        if (intval($this->settings['solr_limit']) > 0) {
783
            $solr->limit = intval($this->settings['solr_limit']);
784
        }
785
        // We only care about the UID in the results and want them sorted
786
        $parameters = [
787
            "fields" => "uid",
788
            "sort" => [
789
                "uid" => "asc"
790
            ]
791
        ];
792
        $result = $solr->search_raw($solr_query, $parameters);
793
        if (empty($result)) {
794
            $this->error = 'noRecordsMatch';
795
            return;
796
        }
797
        foreach ($result as $doc) {
798
            $documentSet[] = $doc->uid;
799
        }
800
        return $documentSet;
801
    }
802
803
    /**
804
     * Fetch more information for document list
805
     * @access protected
806
     *
807
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
808
     *
809
     * @return array of enriched records
810
     */
811
    protected function generateOutputForDocumentList(DocumentList $documentListSet)
812
    {
813
        $documentsToProcess = $documentListSet->removeRange(0, (int)$this->settings['limit']);
814
        $verb = $this->parameters['verb'];
815
816
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
817
            ->getConnectionForTable('tx_dlf_documents');
818
819
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
820
            'FROM `tx_dlf_documents` ' .
821
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
822
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
823
            'WHERE `tx_dlf_documents`.`uid` IN ( ? ) ' .
824
            'AND `tx_dlf_documents`.`pid` = ? ' .
825
            'AND `tx_dlf_collections`.`pid` = ? ' .
826
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
827
            'AND ' . Helper::whereExpression('tx_dlf_collections') . ' ' .
828
            'GROUP BY `tx_dlf_documents`.`uid` ' .
829
            'LIMIT ?';
830
831
        $values = [
832
            $documentsToProcess,
833
            $this->settings['pages'],
834
            $this->settings['pages'],
835
            $this->settings['limit']
836
        ];
837
        $types = [
838
            Connection::PARAM_INT_ARRAY,
839
            Connection::PARAM_INT,
840
            Connection::PARAM_INT,
841
            Connection::PARAM_INT
842
        ];
843
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
844
        $documents = $connection->executeQuery($sql, $values, $types);
845
846
        $records = [];
847
        while ($resArray = $documents->fetch()) {
848
            // we need the collections as array later
849
            $resArray['collections'] = explode(' ', $resArray['collections']);
850
851
            if ($verb === 'ListRecords') {
852
                // Add metadata node.
853
                $metadataPrefix = $this->parameters['metadataPrefix'];
854
                if (!$metadataPrefix) {
855
                    // If we resume an action the metadataPrefix is stored with the documentSet
856
                    $metadataPrefix = $documentListSet->metadata['metadataPrefix'];
857
                }
858
                switch ($metadataPrefix) {
859
                    case 'oai_dc':
860
                        $resArray['metadata'] = $this->getDcData($resArray);
861
                        break;
862
                    case 'epicur':
863
                        $resArray['metadata'] = $resArray;
864
                        break;
865
                    case 'mets':
866
                        $resArray['metadata'] = $this->getMetsData($resArray);
867
                        break;
868
                }
869
            }
870
871
            $records[] = $resArray;
872
        }
873
874
        $this->generateResumptionTokenForDocumentListSet($documentListSet);
875
876
        return $records;
877
    }
878
879
    /**
880
     * Generate resumption token
881
     *
882
     * @access protected
883
     *
884
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
885
     *
886
     * @return void
887
     */
888
    protected function generateResumptionTokenForDocumentListSet(DocumentList $documentListSet)
889
    {
890
        if ($documentListSet->count() !== 0) {
891
            $token = uniqid('', false);
892
893
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_dlf_tokens');
894
            $affectedRows = $queryBuilder
895
                ->insert('tx_dlf_tokens')
896
                ->values([
897
                    'tstamp' => $GLOBALS['EXEC_TIME'],
898
                    'token' => $token,
899
                    'options' => serialize($documentListSet),
900
                    'ident' => 'oai',
901
                ])
902
                ->execute();
903
904
            if ($affectedRows === 1) {
905
                $resumptionToken = $token;
906
            } else {
907
                $this->logger->error('Could not create resumption token');
908
                $this->error = 'badResumptionToken';
909
                return;
910
            }
911
        } else {
912
            // Result set complete. We don't need a token.
913
            $resumptionToken = '';
914
        }
915
916
        $resumptionTokenInfo = [];
917
        $resumptionTokenInfo['token'] = $resumptionToken;
918
        $resumptionTokenInfo['cursor'] = $documentListSet->metadata['completeListSize'] - count($documentListSet);
919
        $resumptionTokenInfo['completeListSize'] = $documentListSet->metadata['completeListSize'];
920
        $expireDateTime = new \DateTime();
921
        $expireDateTime->add(new \DateInterval('PT' .$this->settings['expired'] . 'S'));
922
        $resumptionTokenInfo['expired'] = $expireDateTime;
923
924
        $this->view->assign('resumptionToken', $resumptionTokenInfo);
925
    }
926
}
927