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

OaiPmhController::generateOutputForDocumentList()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 66
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 46
c 2
b 0
f 0
dl 0
loc 66
rs 8.2448
cc 7
nc 10
nop 1

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

Loading history...
108
        }
109
    }
110
111
    /**
112
     * Load URL parameters
113
     *
114
     * @access protected
115
     *
116
     * @return void
117
     */
118
    protected function getUrlParams()
119
    {
120
        $allowedParams = [
121
            'verb',
122
            'identifier',
123
            'metadataPrefix',
124
            'from',
125
            'until',
126
            'set',
127
            'resumptionToken'
128
        ];
129
        // Clear plugin variables.
130
        $this->parameters = [];
131
        // Set only allowed parameters.
132
        foreach ($allowedParams as $param) {
133
            if (GeneralUtility::_GP($param)) {
134
                $this->parameters[$param] = GeneralUtility::_GP($param);
135
            }
136
        }
137
    }
138
139
    /**
140
     * Get unqualified Dublin Core data.
141
     * @see http://www.openarchives.org/OAI/openarchivesprotocol.html#dublincore
142
     *
143
     * @access protected
144
     *
145
     * @param array $record : The full record array
146
     *
147
     * @return array $metadata: The mapped metadata array
148
     */
149
    protected function getDcData(array $record)
150
    {
151
        $metadata = [];
152
153
        $metadata[] = ['dc:identifier' => $record['record_id']];
154
155
        if (!empty($record['purl'])) {
156
            $metadata[] = ['dc:identifier' => $record['purl']];
157
        }
158
        if (!empty($record['prod_id'])) {
159
            $metadata[] = ['dc:identifier' => $record['prod_id']];
160
        }
161
        if (!empty($record['urn'])) {
162
            $metadata[] = ['dc:identifier' => $record['urn']];
163
        }
164
        if (!empty($record['title'])) {
165
            $metadata[] = ['dc:title' => $record['title']];
166
        }
167
        if (!empty($record['author'])) {
168
            $metadata[] = ['dc:creator' => $record['author']];
169
        }
170
        if (!empty($record['year'])) {
171
            $metadata[] = ['dc:date' => $record['year']];
172
        }
173
        if (!empty($record['place'])) {
174
            $metadata[] = ['dc:coverage' => $record['place']];
175
        }
176
        $record[] = ['dc:format' => $record['application/mets+xml']];
177
        $record[] = ['dc:type' => $record['Text']];
178
        if (!empty($record['partof'])) {
179
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
180
                ->getQueryBuilderForTable('tx_dlf_documents');
181
182
            $result = $queryBuilder
183
                ->select('tx_dlf_documents.record_id')
184
                ->from('tx_dlf_documents')
185
                ->where(
186
                    $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($metadata['partof'])),
187
                    Helper::whereExpression('tx_dlf_documents')
188
                )
189
                ->setMaxResults(1)
190
                ->execute();
191
192
            if ($partof = $result->fetch()) {
193
                $metadata[] = ['dc:relation' => $partof['record_id']];
194
            }
195
        }
196
        if (!empty($record['license'])) {
197
            $metadata[] = ['dc:rights' => $record['license']];
198
        }
199
        if (!empty($record['terms'])) {
200
            $metadata[] = ['dc:rights' => $record['terms']];
201
        }
202
        if (!empty($record['restrictions'])) {
203
            $metadata[] = ['dc:rights' => $record['restrictions']];
204
        }
205
        if (!empty($record['out_of_print'])) {
206
            $metadata[] = ['dc:rights' => $record['out_of_print']];
207
        }
208
        if (!empty($record['rights_info'])) {
209
            $metadata[] = ['dc:rights' => $record['rights_info']];
210
        }
211
        return $metadata;
212
    }
213
214
215
    /**
216
     * Get METS data.
217
     * @see http://www.loc.gov/standards/mets/docs/mets.v1-7.html
218
     *
219
     * @access protected
220
     *
221
     * @param array $record : The full record array
222
     *
223
     * @return string: The fetched METS XML
224
     */
225
    protected function getMetsData(array $record)
226
    {
227
        $mets = null;
228
        // Load METS file.
229
        $xml = new \DOMDocument();
230
        if ($xml->load($record['location'])) {
231
            // Get root element.
232
            $root = $xml->getElementsByTagNameNS($this->formats['mets']['namespace'], 'mets');
233
            if ($root->item(0) instanceof \DOMNode) {
234
                // Import node into \DOMDocument.
235
                $mets = $xml->saveXML();
236
                // Remove leading line
237
                $mets = substr($mets, strpos($mets, '>'));
238
            } else {
239
                $this->logger->error('No METS part found in document with location "' . $record['location'] . '"');
240
            }
241
        } else {
242
            $this->logger->error('Could not load XML file from "' . $record['location'] . '"');
243
        }
244
        return $mets;
245
    }
246
247
    /**
248
     * The main method of the plugin
249
     *
250
     * @return void
251
     */
252
    public function mainAction()
253
    {
254
        // Get allowed GET and POST variables.
255
        $this->getUrlParams();
256
257
        // Get extension configuration.
258
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
259
260
        // Delete expired resumption tokens.
261
        $this->deleteExpiredTokens();
262
263
        switch ($this->parameters['verb']) {
264
            case 'GetRecord':
265
                $this->verbGetRecord();
266
                break;
267
            case 'Identify':
268
                $this->verbIdentify();
269
                break;
270
            case 'ListIdentifiers':
271
                $this->verbListIdentifiers();
272
                break;
273
            case 'ListMetadataFormats':
274
                $this->verbListMetadataFormats();
275
                break;
276
            case 'ListRecords':
277
                $this->verbListRecords();
278
                break;
279
            case 'ListSets':
280
                $this->verbListSets();
281
                break;
282
        }
283
284
        $this->view->assign('parameters', $this->parameters);
285
        $this->view->assign('error', $this->error);
286
287
        return;
288
    }
289
290
    /**
291
     * Continue with resumption token
292
     *
293
     * @access protected
294
     *
295
     * @return \Kitodo\Dlf\Common\DocumentList list 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 [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type Kitodo\Dlf\Common\DocumentList.
Loading history...
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) {
0 ignored issues
show
introduced by
$resultSet is always a sub-type of Kitodo\Dlf\Common\DocumentList.
Loading history...
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
                $listRecords =  $this->generateOutputForDocumentList($resultSet);
589
                $this->parameters['metadataPrefix'] = $resultSet->metadata['metadataPrefix'];
590
                $this->view->assign('listRecords', $listRecords);
591
                return;
592
            }
593
        }
594
        if (empty($this->parameters['metadataPrefix']) || !empty($this->parameters['identifier'])) {
595
            // "metadataPrefix" is required and "identifier" is not allowed.
596
            $this->error = 'badArgument';
597
            return;
598
        }
599
        // Check "metadataPrefix" for valid value.
600
        if (!in_array($this->parameters['metadataPrefix'], array_keys($this->formats))) {
601
            $this->error = 'cannotDisseminateFormat';
602
            return;
603
        }
604
        try {
605
            $documentSet = $this->fetchDocumentUIDs();
606
        } catch (\Exception $exception) {
607
            $this->error = 'idDoesNotExist';
608
            return;
609
        }
610
        $resultSet = GeneralUtility::makeInstance(DocumentList::class);
611
        $resultSet->reset();
612
        $resultSet->add($documentSet);
613
        $resultSet->metadata = [
614
            'completeListSize' => count($documentSet),
615
            'metadataPrefix' => $this->parameters['metadataPrefix'],
616
        ];
617
618
        $resultSet =  $this->generateOutputForDocumentList($resultSet);
619
        $this->view->assign('listRecords', $resultSet);
620
    }
621
622
    /**
623
     * Process verb "ListSets"
624
     *
625
     * @access protected
626
     *
627
     * @return void
628
     */
629
    protected function verbListSets()
630
    {
631
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
632
            ->getQueryBuilderForTable('tx_dlf_collections');
633
634
        // Check for invalid arguments.
635
        if (count($this->parameters) > 1) {
636
            if (!empty($this->parameters['resumptionToken'])) {
637
                $this->error = 'badResumptionToken';
638
                return;
639
            } else {
640
                $this->error = 'badArgument';
641
                return;
642
            }
643
        }
644
        $where = '';
645
        if (!$this->settings['show_userdefined']) {
646
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
647
        }
648
649
        $result = $queryBuilder
650
            ->select(
651
                'tx_dlf_collections.oai_name AS oai_name',
652
                'tx_dlf_collections.label AS label'
653
            )
654
            ->from('tx_dlf_collections')
655
            ->where(
656
                $queryBuilder->expr()->in('tx_dlf_collections.sys_language_uid', [-1, 0]),
657
                $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
658
                $queryBuilder->expr()->neq('tx_dlf_collections.oai_name', $queryBuilder->createNamedParameter('')),
659
                $where,
660
                Helper::whereExpression('tx_dlf_collections')
661
            )
662
            ->orderBy('tx_dlf_collections.oai_name')
663
            ->execute();
664
665
        $allResults = $result->fetchAll();
666
667
        $this->view->assign('oaiSets', $allResults);
668
    }
669
670
    /**
671
     * Fetch records
672
     *
673
     * @access protected
674
     *
675
     * @return array Array of matching records
676
     */
677
    protected function fetchDocumentUIDs()
678
    {
679
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
680
            ->getQueryBuilderForTable('tx_dlf_collections');
681
682
        $solr_query = '';
683
        $where = '';
684
        if (!$this->settings['show_userdefined']) {
685
            $where = $queryBuilder->expr()->eq('tx_dlf_collections.fe_cruser_id', 0);
686
        }
687
        // Check "set" for valid value.
688
        if (!empty($this->parameters['set'])) {
689
            // For SOLR we need the index_name of the collection,
690
            // For DB Query we need the UID of the collection
691
            $result = $queryBuilder
692
                ->select(
693
                    'tx_dlf_collections.index_name AS index_name',
694
                    'tx_dlf_collections.uid AS uid',
695
                    'tx_dlf_collections.index_search as index_query'
696
                )
697
                ->from('tx_dlf_collections')
698
                ->where(
699
                    $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($this->settings['pages'])),
700
                    $queryBuilder->expr()->eq('tx_dlf_collections.oai_name',
701
                        $queryBuilder->expr()->literal($this->parameters['set'])),
702
                    $where,
703
                    Helper::whereExpression('tx_dlf_collections')
704
                )
705
                ->setMaxResults(1)
706
                ->execute();
707
708
            $allResults = $result->fetchAll();
709
710
            if (count($allResults) < 1) {
711
                $this->error = 'noSetHierarchy';
712
                return;
713
            }
714
            $resArray = $allResults[0];
715
            if ($resArray['index_query'] != "") {
716
                $solr_query .= '(' . $resArray['index_query'] . ')';
717
            } else {
718
                $solr_query .= 'collection:' . '"' . $resArray['index_name'] . '"';
719
            }
720
        } else {
721
            // If no set is specified we have to query for all collections
722
            $solr_query .= 'collection:* NOT collection:""';
723
        }
724
        // Check for required fields.
725
        foreach ($this->formats[$this->parameters['metadataPrefix']]['requiredFields'] as $required) {
726
            $solr_query .= ' NOT ' . $required . ':""';
727
        }
728
        // toplevel="true" is always required
729
        $solr_query .= ' AND toplevel:true';
730
        $from = "*";
731
        // Check "from" for valid value.
732
        if (!empty($this->parameters['from'])) {
733
            // Is valid format?
734
            if (
735
                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...
736
                || is_array($date_array = strptime($this->parameters['from'], '%Y-%m-%d'))
737
            ) {
738
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
739
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
740
                $from = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.000Z';
741
            } else {
742
                $this->error = 'badArgument';
743
                return;
744
            }
745
        }
746
        $until = "*";
747
        // Check "until" for valid value.
748
        if (!empty($this->parameters['until'])) {
749
            // Is valid format?
750
            if (
751
                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...
752
                || is_array($date_array = strptime($this->parameters['until'], '%Y-%m-%d'))
753
            ) {
754
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'],
755
                    $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
756
                $until = date("Y-m-d", $timestamp) . 'T' . date("H:i:s", $timestamp) . '.999Z';
757
                if ($from != "*" && $from > $until) {
758
                    $this->error = 'badArgument';
759
                }
760
            } else {
761
                $this->error = 'badArgument';
762
            }
763
        }
764
        // Check "from" and "until" for same granularity.
765
        if (
766
            !empty($this->parameters['from'])
767
            && !empty($this->parameters['until'])
768
        ) {
769
            if (strlen($this->parameters['from']) != strlen($this->parameters['until'])) {
770
                $this->error = 'badArgument';
771
            }
772
        }
773
        $solr_query .= ' AND timestamp:[' . $from . ' TO ' . $until . ']';
774
        $documentSet = [];
775
        $solr = Solr::getInstance($this->settings['solrcore']);
776
        if (!$solr->ready) {
777
            $this->logger->error('Apache Solr not available');
778
            return $documentSet;
779
        }
780
        if (intval($this->settings['solr_limit']) > 0) {
781
            $solr->limit = intval($this->settings['solr_limit']);
782
        }
783
        // We only care about the UID in the results and want them sorted
784
        $parameters = [
785
            "fields" => "uid",
786
            "sort" => [
787
                "uid" => "asc"
788
            ]
789
        ];
790
        $result = $solr->search_raw($solr_query, $parameters);
791
        if (empty($result)) {
792
            $this->error = 'noRecordsMatch';
793
            return;
794
        }
795
        foreach ($result as $doc) {
796
            $documentSet[] = $doc->uid;
797
        }
798
        return $documentSet;
799
    }
800
801
    /**
802
     * Fetch more information for document list
803
     * @access protected
804
     *
805
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
806
     *
807
     * @return array of enriched records
808
     */
809
    protected function generateOutputForDocumentList(DocumentList $documentListSet)
810
    {
811
        $documentsToProcess = $documentListSet->removeRange(0, (int)$this->settings['limit']);
812
        $verb = $this->parameters['verb'];
813
814
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
815
            ->getConnectionForTable('tx_dlf_documents');
816
817
        $sql = 'SELECT `tx_dlf_documents`.*, GROUP_CONCAT(DISTINCT `tx_dlf_collections`.`oai_name` ORDER BY `tx_dlf_collections`.`oai_name` SEPARATOR " ") AS `collections` ' .
818
            'FROM `tx_dlf_documents` ' .
819
            'INNER JOIN `tx_dlf_relations` ON `tx_dlf_relations`.`uid_local` = `tx_dlf_documents`.`uid` ' .
820
            'INNER JOIN `tx_dlf_collections` ON `tx_dlf_collections`.`uid` = `tx_dlf_relations`.`uid_foreign` ' .
821
            'WHERE `tx_dlf_documents`.`uid` IN ( ? ) ' .
822
            'AND `tx_dlf_documents`.`pid` = ? ' .
823
            'AND `tx_dlf_collections`.`pid` = ? ' .
824
            'AND `tx_dlf_relations`.`ident`="docs_colls" ' .
825
            'AND ' . Helper::whereExpression('tx_dlf_collections') . ' ' .
826
            'GROUP BY `tx_dlf_documents`.`uid` ' .
827
            'LIMIT ?';
828
829
        $values = [
830
            $documentsToProcess,
831
            $this->settings['pages'],
832
            $this->settings['pages'],
833
            $this->settings['limit']
834
        ];
835
        $types = [
836
            Connection::PARAM_INT_ARRAY,
837
            Connection::PARAM_INT,
838
            Connection::PARAM_INT,
839
            Connection::PARAM_INT
840
        ];
841
        // Create a prepared statement for the passed SQL query, bind the given params with their binding types and execute the query
842
        $documents = $connection->executeQuery($sql, $values, $types);
843
844
        $records = [];
845
        while ($resArray = $documents->fetch()) {
846
            // we need the collections as array later
847
            $resArray['collections'] = explode(' ', $resArray['collections']);
848
849
            if ($verb === 'ListRecords') {
850
                // Add metadata node.
851
                $metadataPrefix = $this->parameters['metadataPrefix'];
852
                if (!$metadataPrefix) {
853
                    // If we resume an action the metadataPrefix is stored with the documentSet
854
                    $metadataPrefix = $documentListSet->metadata['metadataPrefix'];
855
                }
856
                switch ($metadataPrefix) {
857
                    case 'oai_dc':
858
                        $resArray['metadata'] = $this->getDcData($resArray);
859
                        break;
860
                    case 'epicur':
861
                        $resArray['metadata'] = $resArray;
862
                        break;
863
                    case 'mets':
864
                        $resArray['metadata'] = $this->getMetsData($resArray);
865
                        break;
866
                }
867
            }
868
869
            $records[] = $resArray;
870
        }
871
872
        $this->generateResumptionTokenForDocumentListSet($documentListSet);
873
874
        return $records;
875
    }
876
877
    /**
878
     * Generate resumption token
879
     *
880
     * @access protected
881
     *
882
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
883
     *
884
     * @return void
885
     */
886
    protected function generateResumptionTokenForDocumentListSet(DocumentList $documentListSet)
887
    {
888
        if ($documentListSet->count() !== 0) {
889
            $token = uniqid('', false);
890
891
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_dlf_tokens');
892
            $affectedRows = $queryBuilder
893
                ->insert('tx_dlf_tokens')
894
                ->values([
895
                    'tstamp' => $GLOBALS['EXEC_TIME'],
896
                    'token' => $token,
897
                    'options' => serialize($documentListSet),
898
                    'ident' => 'oai',
899
                ])
900
                ->execute();
901
902
            if ($affectedRows === 1) {
903
                $resumptionToken = $token;
904
            } else {
905
                $this->logger->error('Could not create resumption token');
906
                $this->error = 'badResumptionToken';
907
                return;
908
            }
909
        } else {
910
            // Result set complete. We don't need a token.
911
            $resumptionToken = '';
912
        }
913
914
        $resumptionTokenInfo = [];
915
        $resumptionTokenInfo['token'] = $resumptionToken;
916
        $resumptionTokenInfo['cursor'] = $documentListSet->metadata['completeListSize'] - count($documentListSet);
917
        $resumptionTokenInfo['completeListSize'] = $documentListSet->metadata['completeListSize'];
918
        $expireDateTime = new \DateTime();
919
        $expireDateTime->add(new \DateInterval('PT' .$this->settings['expired'] . 'S'));
920
        $resumptionTokenInfo['expired'] = $expireDateTime;
921
922
        $this->view->assign('resumptionToken', $resumptionTokenInfo);
923
    }
924
}
925