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

OaiPmhController::mainAction()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 25
c 2
b 0
f 0
dl 0
loc 36
rs 8.5866
cc 7
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 $metadata: The mapped metadata array
0 ignored issues
show
Documentation Bug introduced by
The doc comment $metadata: The at position 0 could not be parsed: Unknown type name '$metadata' at position 0 in $metadata: The.
Loading history...
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 array Array of uids
296
     */
297
    protected function resume()
298
    {
299
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
300
            ->getQueryBuilderForTable('tx_dlf_tokens');
301
302
        // Get resumption token.
303
        $result = $queryBuilder
304
            ->select('tx_dlf_tokens.options AS options')
305
            ->from('tx_dlf_tokens')
306
            ->where(
307
                $queryBuilder->expr()->eq('tx_dlf_tokens.ident', $queryBuilder->createNamedParameter('oai')),
308
                $queryBuilder->expr()->eq('tx_dlf_tokens.token',
309
                    $queryBuilder->expr()->literal($this->parameters['resumptionToken'])
310
                )
311
            )
312
            ->setMaxResults(1)
313
            ->execute();
314
315
        if ($resArray = $result->fetch()) {
316
            return unserialize($resArray['options']);
317
        } else {
318
            // No resumption token found or resumption token expired.
319
            $this->error = 'badResumptionToken';
320
            return [];
321
        }
322
    }
323
324
    /**
325
     * Process verb "GetRecord"
326
     *
327
     * @access protected
328
     *
329
     * @return void
330
     */
331
    protected function verbGetRecord()
332
    {
333
        if (count($this->parameters) !== 3 || empty($this->parameters['metadataPrefix']) || empty($this->parameters['identifier'])) {
334
            $this->error = 'badArgument'     ;
335
            return;
336
        }
337
        if (!array_key_exists($this->parameters['metadataPrefix'], $this->formats)) {
338
            $this->error = 'cannotDisseminateFormat'     ;
339
            return;
340
        }
341
        $where = '';
342
        if (!$this->settings['show_userdefined']) {
343
            $where .= 'AND tx_dlf_collections.fe_cruser_id=0 ';
344
        }
345
346
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_dlf_documents');
347
348
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
349
            'FROM `tx_dlf_documents` ' .
350
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
351
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
352
            'WHERE `tx_dlf_documents`.`record_id` = ? ' .
353
            'AND `tx_dlf_documents`.`pid` = ? ' .
354
            'AND `tx_dlf_collections`.`pid` = ? ' .
355
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
356
            $where .
357
            'AND ' . Helper::whereExpression('tx_dlf_collections');
358
359
        $values = [
360
            $this->parameters['identifier'],
361
            $this->settings['pages'],
362
            $this->settings['pages']
363
        ];
364
        $types = [
365
            Connection::PARAM_STR,
366
            Connection::PARAM_INT,
367
            Connection::PARAM_INT
368
        ];
369
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
370
        $statement = $connection->executeQuery($sql, $values, $types);
371
372
        $resArray = $statement->fetch();
373
374
        if (!$resArray['uid']) {
375
            return $this->error = 'idDoesNotExist';
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error = 'idDoesNotExist' returns the type string which is incompatible with the documented return type void.
Loading history...
376
        }
377
378
        // Check for required fields.
379
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
380
            if (empty($resArray[$required])) {
381
                return $this->error = 'cannotDisseminateFormat';
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error = 'cannotDisseminateFormat' returns the type string which is incompatible with the documented return type void.
Loading history...
382
            }
383
        }
384
385
        // we need the collections as array later
386
        $resArray['collections'] = explode(' ', $resArray['collections']);
387
388
        // Add metadata
389
        switch ($this->parameters['metadataPrefix']) {
390
            case 'oai_dc':
391
                $resArray['metadata'] = $this->getDcData($resArray);
392
                break;
393
            case 'epicur':
394
                $resArray['metadata'] = $resArray;
395
                break;
396
            case 'mets':
397
                $resArray['metadata'] = $this->getMetsData($resArray);
398
                break;
399
        }
400
401
        $this->view->assign('record', $resArray);
402
    }
403
404
    /**
405
     * Process verb "Identify"
406
     *
407
     * @access protected
408
     *
409
     * @return void
410
     */
411
    protected function verbIdentify()
412
    {
413
        // Get repository name and administrative contact.
414
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
415
            ->getQueryBuilderForTable('tx_dlf_libraries');
416
417
        $result = $queryBuilder
418
            ->select(
419
                'tx_dlf_libraries.oai_label AS oai_label',
420
                'tx_dlf_libraries.contact AS contact'
421
            )
422
            ->from('tx_dlf_libraries')
423
            ->where(
424
                $queryBuilder->expr()->eq('tx_dlf_libraries.pid', intval($this->settings['pages'])),
425
                $queryBuilder->expr()->eq('tx_dlf_libraries.uid', intval($this->settings['library']))
426
            )
427
            ->setMaxResults(1)
428
            ->execute();
429
430
        $oaiIdentifyInfo = $result->fetch();
431
        if (!$oaiIdentifyInfo) {
432
            $this->logger->notice('Incomplete plugin configuration');
433
        }
434
435
        // Use default values for an installation with incomplete plugin configuration.
436
        if (empty($oaiIdentifyInfo['oai_label'])) {
437
            $oaiIdentifyInfo['oai_label'] = 'Kitodo.Presentation OAI-PMH Interface (default configuration)';
438
            $this->logger->notice('Incomplete plugin configuration (oai_label is missing)');
439
        }
440
441
        if (empty($oaiIdentifyInfo['contact'])) {
442
            $oaiIdentifyInfo['contact'] = '[email protected]';
443
            $this->logger->notice('Incomplete plugin configuration (contact is missing)');
444
        }
445
446
        // Get earliest datestamp. Use a default value if that fails.
447
        $earliestDatestamp = '0000-00-00T00:00:00Z';
0 ignored issues
show
Unused Code introduced by
The assignment to $earliestDatestamp is dead and can be removed.
Loading history...
448
449
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
450
            ->getQueryBuilderForTable('tx_dlf_documents');
451
452
        $result = $queryBuilder
453
            ->select('tx_dlf_documents.tstamp AS tstamp')
454
            ->from('tx_dlf_documents')
455
            ->where(
456
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($this->settings['pages']))
457
            )
458
            ->orderBy('tx_dlf_documents.tstamp')
459
            ->setMaxResults(1)
460
            ->execute();
461
462
        if ($resArray = $result->fetch()) {
463
            $oaiIdentifyInfo['earliestDatestamp'] = gmdate('Y-m-d\TH:i:s\Z', $resArray['tstamp']);
464
        } else {
465
            $this->logger->notice('No records found with PID ' . $this->settings['pages']);
466
        }
467
        $this->view->assign('oaiIdentifyInfo', $oaiIdentifyInfo);
468
    }
469
470
    /**
471
     * Process verb "ListIdentifiers"
472
     *
473
     * @access protected
474
     *
475
     * @return void
476
     */
477
    protected function verbListIdentifiers()
478
    {
479
        // If we have a resumption token we can continue our work
480
        if (!empty($this->parameters['resumptionToken'])) {
481
            // "resumptionToken" is an exclusive argument.
482
            if (count($this->parameters) > 2) {
483
                $this->error = 'badArgument';
484
                return;
485
            } else {
486
                // return next chunk of documents
487
                $resultSet = $this->resume();
488
                if ($resultSet instanceof DocumentList) {
0 ignored issues
show
introduced by
$resultSet is never a sub-type of Kitodo\Dlf\Common\DocumentList.
Loading history...
489
                    $listIdentifiers =  $this->generateOutputForDocumentList($resultSet);
490
                    $this->view->assign('listIdentifiers', $listIdentifiers);
491
                }
492
                return;
493
            }
494
        }
495
        // "metadataPrefix" is required and "identifier" is not allowed.
496
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
497
            $this->error = 'badArgument';
498
            return;
499
        }
500
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
501
            $this->error = 'cannotDisseminateFormat';
502
            return;
503
        }
504
        try {
505
            $documentSet = $this->fetchDocumentUIDs();
506
        } catch (\Exception $exception) {
507
            $this->error = 'idDoesNotExist';
508
            return;
509
        }
510
        // create new and empty documentlist
511
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
512
        $resultSet->reset();
513
        $resultSet->add($documentSet);
514
        $resultSet->metadata = [
515
            'completeListSize' => count($documentSet),
516
            'metadataPrefix' => $this->parameters['metadataPrefix'],
517
        ];
518
519
        $listIdentifiers =  $this->generateOutputForDocumentList($resultSet);
520
        $this->view->assign('listIdentifiers', $listIdentifiers);
521
    }
522
523
    /**
524
     * Process verb "ListMetadataFormats"
525
     *
526
     * @access protected
527
     *
528
     * @return void
529
     */
530
    protected function verbListMetadataFormats()
531
    {
532
        $resArray = [];
533
        // check for the optional "identifier" parameter
534
        if (isset($this->parameters['identifier'])) {
535
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
536
                ->getQueryBuilderForTable('tx_dlf_documents');
537
538
            // Check given identifier.
539
            $result = $queryBuilder
540
                ->select('tx_dlf_documents.*')
541
                ->from('tx_dlf_documents')
542
                ->where(
543
                    $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($this->settings['pages'])),
544
                    $queryBuilder->expr()->eq('tx_dlf_documents.record_id',
545
                    $queryBuilder->expr()->literal($this->parameters['identifier']))
546
                )
547
                ->orderBy('tx_dlf_documents.tstamp')
548
                ->setMaxResults(1)
549
                ->execute();
550
551
            $resArray = $result->fetch();
552
        }
553
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);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $resultSet does not seem to be defined for all execution paths leading up to this point.
Loading history...
568
569
        return $ListMetadaFormats;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ListMetadaFormats seems to be never defined.
Loading history...
570
    }
571
572
    /**
573
     * Process verb "ListRecords"
574
     *
575
     * @access protected
576
     *
577
     * @return void
578
     */
579
    protected function verbListRecords()
580
    {
581
        // Check for invalid arguments.
582
        if (!empty($this->parameters['resumptionToken'])) {
583
            // "resumptionToken" is an exclusive argument.
584
            if (count($this->parameters) > 2) {
585
                $this->error = 'badArgument';
586
                return;
587
            } else {
588
                // return next chunk of documents
589
                $resultSet = $this->resume();
590
                $listRecords =  $this->generateOutputForDocumentList($resultSet);
0 ignored issues
show
Bug introduced by
$resultSet of type array is incompatible with the type Kitodo\Dlf\Common\DocumentList expected by parameter $documentListSet of Kitodo\Dlf\Controller\Oa...OutputForDocumentList(). ( Ignorable by Annotation )

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

590
                $listRecords =  $this->generateOutputForDocumentList(/** @scrutinizer ignore-type */ $resultSet);
Loading history...
591
                $this->parameters['metadataPrefix'] = $resultSet->metadata['metadataPrefix'];
592
                $this->view->assign('listRecords', $listRecords);
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
        while ($resArray = $documents->fetch()) {
847
            // we need the collections as array later
848
            $resArray['collections'] = explode(' ', $resArray['collections']);
849
850
            if ($verb === 'ListRecords') {
851
                // Add metadata node.
852
                $metadataPrefix = $this->parameters['metadataPrefix'];
853
                if (!$metadataPrefix) {
854
                    // If we resume an action the metadataPrefix is stored with the documentSet
855
                    $metadataPrefix = $documentListSet->metadata['metadataPrefix'];
856
                }
857
                switch ($metadataPrefix) {
858
                    case 'oai_dc':
859
                        $resArray['metadata'] = $this->getDcData($resArray);
860
                        break;
861
                    case 'epicur':
862
                        $resArray['metadata'] = $resArray;
863
                        break;
864
                    case 'mets':
865
                        $resArray['metadata'] = $this->getMetsData($resArray);
866
                        break;
867
                }
868
            }
869
870
            $records[] = $resArray;
871
        }
872
873
        $this->generateResumptionTokenForDocumentListSet($documentListSet);
874
875
        return $records;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $records does not seem to be defined for all execution paths leading up to this point.
Loading history...
876
    }
877
878
    /**
879
     * Generate resumption token
880
     *
881
     * @access protected
882
     *
883
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
884
     *
885
     * @return void
886
     */
887
    protected function generateResumptionTokenForDocumentListSet(DocumentList $documentListSet)
888
    {
889
        if ($documentListSet->count() !== 0) {
890
            $token = uniqid('', false);
891
892
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_dlf_tokens');
893
            $affectedRows = $queryBuilder
894
                ->insert('tx_dlf_tokens')
895
                ->values([
896
                    'tstamp' => $GLOBALS['EXEC_TIME'],
897
                    'token' => $token,
898
                    'options' => serialize($documentListSet),
899
                    'ident' => 'oai',
900
                ])
901
                ->execute();
902
903
            if ($affectedRows === 1) {
904
                $resumptionToken = $token;
905
            } else {
906
                $this->logger->error('Could not create resumption token');
907
                $this->error = 'badResumptionToken';
908
                return;
909
            }
910
        } else {
911
            // Result set complete. We don't need a token.
912
            $resumptionToken = '';
913
        }
914
915
        $resumptionTokenInfo = [];
916
        $resumptionTokenInfo['token'] = $resumptionToken;
917
        $resumptionTokenInfo['cursor'] = $documentListSet->metadata['completeListSize'] - count($documentListSet);
918
        $resumptionTokenInfo['completeListSize'] = $documentListSet->metadata['completeListSize'];
919
        $expireDateTime = new \DateTime();
920
        $expireDateTime->add(new \DateInterval('PT' .$this->settings['expired'] . 'S'));
921
        $resumptionTokenInfo['expired'] = $expireDateTime;
922
923
        $this->view->assign('resumptionToken', $resumptionTokenInfo);
924
    }
925
}
926