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
Push — master ( 14584b...f60880 )
by Sebastian
02:42
created

OaiPmh::fetchDocumentUIDs()   F

Complexity

Conditions 20
Paths 686

Size

Total Lines 94
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 63
nc 686
nop 0
dl 0
loc 94
rs 0.4361
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
namespace Kitodo\Dlf\Plugin;
3
4
/**
5
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
6
 *
7
 * This file is part of the Kitodo and TYPO3 projects.
8
 *
9
 * @license GNU General Public License version 3 or later.
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 */
13
14
use Kitodo\Dlf\Common\DocumentList;
15
use Kitodo\Dlf\Common\Helper;
16
use Kitodo\Dlf\Common\Solr;
17
18
/**
19
 * Plugin 'OAI-PMH Interface' for the 'dlf' extension
20
 *
21
 * @author Sebastian Meyer <[email protected]>
22
 * @package TYPO3
23
 * @subpackage dlf
24
 * @access public
25
 */
26
class OaiPmh extends \Kitodo\Dlf\Common\AbstractPlugin {
27
    public $scriptRelPath = 'Classes/Plugin/OaiPmh.php';
28
29
    /**
30
     * Did an error occur?
31
     *
32
     * @var boolean
33
     * @access protected
34
     */
35
    protected $error = FALSE;
36
37
    /**
38
     * This holds the OAI DOM object
39
     *
40
     * @var \DOMDocument
41
     * @access protected
42
     */
43
    protected $oai;
44
45
    /**
46
     * This holds the configuration for all supported metadata prefixes
47
     *
48
     * @var array
49
     * @access protected
50
     */
51
    protected $formats = [
52
        'oai_dc' => [
53
            'schema' => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
54
            'namespace' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
55
            'requiredFields' => ['record_id'],
56
        ],
57
        'epicur' => [
58
            'schema' => 'http://www.persistent-identifier.de/xepicur/version1.0/xepicur.xsd',
59
            'namespace' => 'urn:nbn:de:1111-2004033116',
60
            'requiredFields' => ['purl', 'urn'],
61
        ],
62
        'mets' => [
63
            'schema' => 'http://www.loc.gov/standards/mets/version17/mets.v1-7.xsd',
64
            'namespace' => 'http://www.loc.gov/METS/',
65
            'requiredFields' => ['location'],
66
        ]
67
    ];
68
69
    /**
70
     * Delete expired resumption tokens
71
     *
72
     * @access protected
73
     *
74
     * @return void
75
     */
76
    protected function deleteExpiredTokens() {
77
        // Delete expired resumption tokens.
78
        $GLOBALS['TYPO3_DB']->exec_DELETEquery(
79
            'tx_dlf_tokens',
80
            'tx_dlf_tokens.ident="oai" AND tx_dlf_tokens.tstamp<'.intval($GLOBALS['EXEC_TIME'] - $this->conf['expired'])
81
        );
82
        if ($GLOBALS['TYPO3_DB']->sql_affected_rows() === -1) {
83
            // Deletion failed.
84
            Helper::devLog('Could not delete expired resumption tokens', DEVLOG_SEVERITY_WARNING);
85
        }
86
    }
87
88
    /**
89
     * Process error
90
     *
91
     * @access protected
92
     *
93
     * @param string $type: Error type
94
     *
95
     * @return \DOMElement XML node to add to the OAI response
96
     */
97
    protected function error($type) {
98
        $this->error = TRUE;
99
        $error = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'error', htmlspecialchars($this->pi_getLL($type, $type, FALSE), ENT_NOQUOTES, 'UTF-8'));
100
        $error->setAttribute('code', $type);
101
        return $error;
102
    }
103
104
    /**
105
     * Load URL parameters
106
     *
107
     * @access protected
108
     *
109
     * @return void
110
     */
111
    protected function getUrlParams() {
112
        $allowedParams = [
113
            'verb',
114
            'identifier',
115
            'metadataPrefix',
116
            'from',
117
            'until',
118
            'set',
119
            'resumptionToken'
120
        ];
121
        // Clear plugin variables.
122
        $this->piVars = [];
123
        // Set only allowed parameters.
124
        foreach ($allowedParams as $param) {
125
            if (\TYPO3\CMS\Core\Utility\GeneralUtility::_GP($param)) {
126
                $this->piVars[$param] = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP($param);
127
            }
128
        }
129
    }
130
131
    /**
132
     * Get unqualified Dublin Core data.
133
     * @see http://www.openarchives.org/OAI/openarchivesprotocol.html#dublincore
134
     *
135
     * @access protected
136
     *
137
     * @param array $metadata: The metadata array
138
     *
139
     * @return \DOMElement XML node to add to the OAI response
140
     */
141
    protected function getDcData(array $metadata) {
142
        $oai_dc = $this->oai->createElementNS($this->formats['oai_dc']['namespace'], 'oai_dc:dc');
143
        $oai_dc->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/');
144
        $oai_dc->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
145
        $oai_dc->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation', $this->formats['oai_dc']['namespace'].' '.$this->formats['oai_dc']['schema']);
146
        $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:identifier', htmlspecialchars($metadata['record_id'], ENT_NOQUOTES, 'UTF-8')));
147
        if (!empty($metadata['purl'])) {
148
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:identifier', htmlspecialchars($metadata['purl'], ENT_NOQUOTES, 'UTF-8')));
149
        }
150
        if (!empty($metadata['urn'])) {
151
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:identifier', htmlspecialchars($metadata['urn'], ENT_NOQUOTES, 'UTF-8')));
152
        }
153
        if (!empty($metadata['title'])) {
154
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:title', htmlspecialchars($metadata['title'], ENT_NOQUOTES, 'UTF-8')));
155
        }
156
        if (!empty($metadata['author'])) {
157
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:creator', htmlspecialchars($metadata['author'], ENT_NOQUOTES, 'UTF-8')));
158
        }
159
        if (!empty($metadata['year'])) {
160
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:date', htmlspecialchars($metadata['year'], ENT_NOQUOTES, 'UTF-8')));
161
        }
162
        if (!empty($metadata['place'])) {
163
            $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:coverage', htmlspecialchars($metadata['place'], ENT_NOQUOTES, 'UTF-8')));
164
        }
165
        $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:format', 'application/mets+xml'));
166
        $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:type', 'Text'));
167
        if (!empty($metadata['partof'])) {
168
            $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
169
                'tx_dlf_documents.record_id',
170
                'tx_dlf_documents',
171
                'tx_dlf_documents.uid='.intval($metadata['partof'])
172
                    .Helper::whereClause('tx_dlf_documents'),
173
                '',
174
                '',
175
                '1'
176
            );
177
            if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
178
                $partof = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
179
                $oai_dc->appendChild($this->oai->createElementNS('http://purl.org/dc/elements/1.1/', 'dc:relation', htmlspecialchars($partof['record_id'], ENT_NOQUOTES, 'UTF-8')));
180
            }
181
        }
182
        return $oai_dc;
183
    }
184
185
    /**
186
     * Get epicur data.
187
     * @see http://www.persistent-identifier.de/?link=210
188
     *
189
     * @access protected
190
     *
191
     * @param array $metadata: The metadata array
192
     *
193
     * @return \DOMElement XML node to add to the OAI response
194
     */
195
    protected function getEpicurData(array $metadata) {
196
        // Define all XML elements with or without qualified namespace.
197
        if (empty($this->conf['unqualified_epicur'])) {
198
            $epicur = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:epicur');
199
            $admin = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:administrative_data');
200
            $delivery = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:delivery');
201
            $update = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:update_status');
202
            $transfer = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:transfer');
203
            $format = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:format', 'text/html');
204
            $record = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:record');
205
            $identifier = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:identifier', htmlspecialchars($metadata['urn'], ENT_NOQUOTES, 'UTF-8'));
206
            $resource = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:resource');
207
            $ident = $this->oai->createElementNS($this->formats['epicur']['namespace'], 'epicur:identifier', htmlspecialchars($metadata['purl'], ENT_NOQUOTES, 'UTF-8'));
208
        } else {
209
            $epicur = $this->oai->createElement('epicur');
210
            $epicur->setAttribute('xmlns', $this->formats['epicur']['namespace']);
211
            $admin = $this->oai->createElement('administrative_data');
212
            $delivery = $this->oai->createElement('delivery');
213
            $update = $this->oai->createElement('update_status');
214
            $transfer = $this->oai->createElement('transfer');
215
            $format = $this->oai->createElement('format', 'text/html');
216
            $record = $this->oai->createElement('record');
217
            $identifier = $this->oai->createElement('identifier', htmlspecialchars($metadata['urn'], ENT_NOQUOTES, 'UTF-8'));
218
            $resource = $this->oai->createElement('resource');
219
            $ident = $this->oai->createElement('identifier', htmlspecialchars($metadata['purl'], ENT_NOQUOTES, 'UTF-8'));
220
        }
221
        // Add attributes and build XML tree.
222
        $epicur->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
223
        $epicur->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation', $this->formats['epicur']['namespace'].' '.$this->formats['epicur']['schema']);
224
        // Do we update an URN or register a new one?
225
        if ($metadata['tstamp'] == $metadata['crdate']) {
226
            $update->setAttribute('type', 'urn_new');
227
        } else {
228
            $update->setAttribute('type', 'url_update_general');
229
        }
230
        $delivery->appendChild($update);
231
        $transfer->setAttribute('type', 'http');
232
        $delivery->appendChild($transfer);
233
        $admin->appendChild($delivery);
234
        $epicur->appendChild($admin);
235
        $identifier->setAttribute('scheme', 'urn:nbn:de');
236
        $record->appendChild($identifier);
237
        $ident->setAttribute('scheme', 'url');
238
        $ident->setAttribute('type', 'frontpage');
239
        $ident->setAttribute('role', 'primary');
240
        $resource->appendChild($ident);
241
        $format->setAttribute('scheme', 'imt');
242
        $resource->appendChild($format);
243
        $record->appendChild($resource);
244
        $epicur->appendChild($record);
245
        return $epicur;
246
    }
247
248
    /**
249
     * Get METS data.
250
     * @see http://www.loc.gov/standards/mets/docs/mets.v1-7.html
251
     *
252
     * @access protected
253
     *
254
     * @param array $metadata: The metadata array
255
     *
256
     * @return \DOMElement XML node to add to the OAI response
257
     */
258
    protected function getMetsData(array $metadata) {
259
        $mets = NULL;
260
        // Load METS file.
261
        $xml = new \DOMDocument();
262
        if ($xml->load($metadata['location'])) {
263
            // Get root element.
264
            $root = $xml->getElementsByTagNameNS($this->formats['mets']['namespace'], 'mets');
265
            if ($root->item(0) instanceof \DOMNode) {
266
                // Import node into \DOMDocument.
267
                $mets = $this->oai->importNode($root->item(0), TRUE);
268
            } else {
269
                Helper::devLog('No METS part found in document with location "'.$metadata['location'].'"', DEVLOG_SEVERITY_ERROR);
270
            }
271
        } else {
272
            Helper::devLog('Could not load XML file from "'.$metadata['location'].'"', DEVLOG_SEVERITY_ERROR);
273
        }
274
        if ($mets === NULL) {
275
            $mets = $this->oai->createElementNS('http://kitodo.org/', 'kitodo:error', htmlspecialchars($this->pi_getLL('error', 'Error!', FALSE), ENT_NOQUOTES, 'UTF-8'));
276
        }
277
        return $mets;
278
    }
279
280
    /**
281
     * The main method of the PlugIn
282
     *
283
     * @access public
284
     *
285
     * @param string $content: The PlugIn content
286
     * @param array $conf: The PlugIn configuration
287
     *
288
     * @return void
289
     */
290
    public function main($content, $conf) {
291
        // Initialize plugin.
292
        $this->init($conf);
293
        // Turn cache off.
294
        $this->setCache(FALSE);
295
        // Get GET and POST variables.
296
        $this->getUrlParams();
297
        // Delete expired resumption tokens.
298
        $this->deleteExpiredTokens();
299
        // Create XML document.
300
        $this->oai = new \DOMDocument('1.0', 'UTF-8');
301
        // Add processing instruction (aka XSL stylesheet).
302
        if (!empty($this->conf['stylesheet'])) {
303
            // Resolve "EXT:" prefix in file path.
304
            if (substr($this->conf['stylesheet'], 0, 4) == 'EXT:') {
305
                list ($extKey, $filePath) = explode('/', substr($this->conf['stylesheet'], 4), 2);
306
                if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extKey)) {
307
                    $this->conf['stylesheet'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($extKey).$filePath;
308
                }
309
            }
310
            $stylesheet = \TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl($this->conf['stylesheet']);
311
        } else {
312
            // Use default stylesheet if no custom stylesheet is given.
313
            $stylesheet = \TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($this->extKey).'Resources/Public/Stylesheets/OaiPmh.xsl');
314
        }
315
        $this->oai->appendChild($this->oai->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="'.htmlspecialchars($stylesheet, ENT_NOQUOTES, 'UTF-8').'"'));
316
        // Create root element.
317
        $root = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'OAI-PMH');
318
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
319
        $root->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation', 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd');
320
        // Add response date.
321
        $root->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'responseDate', gmdate('Y-m-d\TH:i:s\Z', $GLOBALS['EXEC_TIME'])));
322
        // Get response data.
323
        switch ($this->piVars['verb']) {
324
            case 'GetRecord':
325
                $response = $this->verbGetRecord();
326
                break;
327
            case 'Identify':
328
                $response = $this->verbIdentify();
329
                break;
330
            case 'ListIdentifiers':
331
                $response = $this->verbListIdentifiers();
332
                break;
333
            case 'ListMetadataFormats':
334
                $response = $this->verbListMetadataFormats();
335
                break;
336
            case 'ListRecords':
337
                $response = $this->verbListRecords();
338
                break;
339
            case 'ListSets':
340
                $response = $this->verbListSets();
341
                break;
342
            default:
343
                $response = $this->error('badVerb');
344
        }
345
        // Add request.
346
        $linkConf = [
347
            'parameter' => $GLOBALS['TSFE']->id,
348
            'forceAbsoluteUrl' => 1
349
        ];
350
        $request = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'request', htmlspecialchars($this->cObj->typoLink_URL($linkConf), ENT_NOQUOTES, 'UTF-8'));
351
        if (!$this->error) {
352
            foreach ($this->piVars as $key => $value) {
353
                $request->setAttribute($key, htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8'));
354
            }
355
        }
356
        $root->appendChild($request);
357
        $root->appendChild($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type string; however, parameter $newnode of DOMNode::appendChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

357
        $root->appendChild(/** @scrutinizer ignore-type */ $response);
Loading history...
358
        $this->oai->appendChild($root);
359
        $content = $this->oai->saveXML();
360
        // Clean output buffer.
361
        \TYPO3\CMS\Core\Utility\GeneralUtility::cleanOutputBuffers();
0 ignored issues
show
Bug introduced by
The method cleanOutputBuffers() does not exist on TYPO3\CMS\Core\Utility\GeneralUtility. ( Ignorable by Annotation )

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

361
        \TYPO3\CMS\Core\Utility\GeneralUtility::/** @scrutinizer ignore-call */ 
362
                                                cleanOutputBuffers();

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...
362
        // Send headers.
363
        header('HTTP/1.1 200 OK');
364
        header('Cache-Control: no-cache');
365
        header('Content-Length: '.strlen($content));
366
        header('Content-Type: text/xml; charset=utf-8');
367
        header('Date: '.date('r', $GLOBALS['EXEC_TIME']));
368
        header('Expires: '.date('r', $GLOBALS['EXEC_TIME'] + $this->conf['expired']));
369
        echo $content;
370
        // Flush output buffer and end script processing.
371
        ob_end_flush();
372
        exit;
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
373
    }
374
375
    /**
376
     * Continue with resumption token
377
     *
378
     * @access protected
379
     *
380
     * @return string Substitution for subpart "###RESPONSE###"
381
     */
382
    protected function resume() {
383
        // Get resumption token.
384
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
385
            'tx_dlf_tokens.options AS options',
386
            'tx_dlf_tokens',
387
            'tx_dlf_tokens.ident="oai"'
388
                .' AND tx_dlf_tokens.token='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['resumptionToken'], 'tx_dlf_tokens'),
389
            '',
390
            '',
391
            '1'
392
        );
393
        if (!$GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
394
            // No resumption token found or resumption token expired.
395
            return $this->error('badResumptionToken');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badResumptionToken') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
396
        }
397
        $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
398
        $resultSet = unserialize($resArray['options']);
399
        return $this->generateOutputForDocumentList($resultSet);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->generateOu...ocumentList($resultSet) returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
400
    }
401
402
    /**
403
     * Process verb "GetRecord"
404
     *
405
     * @access protected
406
     *
407
     * @return string Substitution for subpart "###RESPONSE###"
408
     */
409
    protected function verbGetRecord() {
410
        if (count($this->piVars) != 3 || empty($this->piVars['metadataPrefix']) || empty($this->piVars['identifier'])) {
411
            return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
412
        }
413
        if (!in_array($this->piVars['metadataPrefix'], array_keys($this->formats))) {
414
            return $this->error('cannotDisseminateFormat');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('cannotDisseminateFormat') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
415
        }
416
        $where = '';
417
        if (!$this->conf['show_userdefined']) {
418
            $where .= ' AND tx_dlf_collections.fe_cruser_id=0';
419
        }
420
        $record = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
421
            'tx_dlf_documents.*,GROUP_CONCAT(DISTINCT tx_dlf_collections.oai_name ORDER BY tx_dlf_collections.oai_name SEPARATOR " ") AS collections',
422
            'tx_dlf_documents',
423
            'tx_dlf_relations',
424
            'tx_dlf_collections',
425
            'AND tx_dlf_documents.record_id='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['identifier'], 'tx_dlf_documents')
426
                .' AND tx_dlf_documents.pid='.intval($this->conf['pages'])
427
                .' AND tx_dlf_collections.pid='.intval($this->conf['pages'])
428
                .' AND tx_dlf_relations.ident='.$GLOBALS['TYPO3_DB']->fullQuoteStr('docs_colls', 'tx_dlf_relations')
429
                .$where
430
                .Helper::whereClause('tx_dlf_collections'),
431
            'tx_dlf_documents.uid',
432
            'tx_dlf_documents.tstamp',
433
            '1'
434
        );
435
        if (!$GLOBALS['TYPO3_DB']->sql_num_rows($record)) {
436
            return $this->error('idDoesNotExist');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('idDoesNotExist') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
437
        }
438
        $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($record);
439
        // Check for required fields.
440
        foreach ($this->formats[$this->piVars['metadataPrefix']]['requiredFields'] as $required) {
441
            if (empty($resArray[$required])) {
442
                return $this->error('cannotDisseminateFormat');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('cannotDisseminateFormat') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
443
            }
444
        }
445
        $GetRecord = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'GetRecord');
446
        $recordNode = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'record');
447
        $headerNode = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'header');
448
        $headerNode->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'identifier', htmlspecialchars($resArray['record_id'], ENT_NOQUOTES, 'UTF-8')));
449
        $headerNode->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'datestamp', gmdate('Y-m-d\TH:i:s\Z', $resArray['tstamp'])));
450
        // Handle deleted documents.
451
        // TODO: Use TYPO3 API functions here!
452
        if ($resArray['deleted']
453
            || $resArray['hidden']) {
454
            $headerNode->setAttribute('status', 'deleted');
455
            $recordNode->appendChild($headerNode);
456
        } else {
457
            foreach (explode(' ', $resArray['collections']) as $spec) {
458
                $headerNode->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'setSpec', htmlspecialchars($spec, ENT_NOQUOTES, 'UTF-8')));
459
            }
460
            $recordNode->appendChild($headerNode);
461
            $metadataNode = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'metadata');
462
            switch ($this->piVars['metadataPrefix']) {
463
                case 'oai_dc':
464
                    $metadataNode->appendChild($this->getDcData($resArray));
465
                    break;
466
                case 'epicur':
467
                    $metadataNode->appendChild($this->getEpicurData($resArray));
468
                    break;
469
                case 'mets':
470
                    $metadataNode->appendChild($this->getMetsData($resArray));
471
                    break;
472
            }
473
            $recordNode->appendChild($metadataNode);
474
        }
475
        $GetRecord->appendChild($recordNode);
476
        return $GetRecord;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $GetRecord returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
477
    }
478
479
    /**
480
     * Process verb "Identify"
481
     *
482
     * @access protected
483
     *
484
     * @return \DOMElement XML node to add to the OAI response
485
     */
486
    protected function verbIdentify() {
487
        // Check for invalid arguments.
488
        if (count($this->piVars) > 1) {
489
            return $this->error('badArgument');
490
        }
491
        // Get repository name and administrative contact.
492
        // Use default values for an installation with incomplete plugin configuration.
493
        $adminEmail = '[email protected]';
494
        $repositoryName = 'Kitodo.Presentation OAI-PMH Interface (default configuration)';
495
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
496
            'tx_dlf_libraries.oai_label AS oai_label,tx_dlf_libraries.contact AS contact',
497
            'tx_dlf_libraries',
498
            'tx_dlf_libraries.pid='.intval($this->conf['pages'])
499
                .' AND tx_dlf_libraries.uid='.intval($this->conf['library'])
500
                .Helper::whereClause('tx_dlf_libraries'),
501
            '',
502
            '',
503
            ''
504
        );
505
        if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
506
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
507
            $adminEmail = htmlspecialchars(trim(str_replace('mailto:', '', $resArray['contact'])), ENT_NOQUOTES);
508
            $repositoryName = htmlspecialchars($resArray['oai_label'], ENT_NOQUOTES);
509
        } else {
510
            Helper::devLog('Incomplete plugin configuration', DEVLOG_SEVERITY_NOTICE);
511
        }
512
        // Get earliest datestamp. Use a default value if that fails.
513
        $earliestDatestamp = '0000-00-00T00:00:00Z';
514
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
515
            'tx_dlf_documents.tstamp AS tstamp',
516
            'tx_dlf_documents',
517
            'tx_dlf_documents.pid='.intval($this->conf['pages']),
518
            '',
519
            'tx_dlf_documents.tstamp ASC',
520
            '1'
521
        );
522
        if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
523
            list ($timestamp) = $GLOBALS['TYPO3_DB']->sql_fetch_row($result);
524
            $earliestDatestamp = gmdate('Y-m-d\TH:i:s\Z', $timestamp);
525
        } else {
526
            Helper::devLog('No records found with PID '.$this->conf['pages'], DEVLOG_SEVERITY_NOTICE);
527
        }
528
        $linkConf = [
529
            'parameter' => $GLOBALS['TSFE']->id,
530
            'forceAbsoluteUrl' => 1
531
        ];
532
        $baseURL = htmlspecialchars($this->cObj->typoLink_URL($linkConf), ENT_NOQUOTES);
533
        // Add identification node.
534
        $Identify = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'Identify');
535
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'repositoryName', $repositoryName));
536
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'baseURL', $baseURL));
537
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'protocolVersion', '2.0'));
538
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'adminEmail', $adminEmail));
539
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'earliestDatestamp', $earliestDatestamp));
540
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'deletedRecord', 'transient'));
541
        $Identify->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'granularity', 'YYYY-MM-DDThh:mm:ssZ'));
542
        return $Identify;
543
    }
544
545
    /**
546
     * Process verb "ListIdentifiers"
547
     *
548
     * @access protected
549
     *
550
     * @return string Substitution for subpart "###RESPONSE###"
551
     */
552
    protected function verbListIdentifiers() {
553
        // If we have a resumption token we can continue our work
554
        if (!empty($this->piVars['resumptionToken'])) {
555
            // "resumptionToken" is an exclusive argument.
556
            if (count($this->piVars) > 2) {
557
                return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
558
            } else {
559
                return $this->resume();
560
            }
561
        }
562
        // "metadataPrefix" is required and "identifier" is not allowed.
563
        if (empty($this->piVars['metadataPrefix']) || !empty($this->piVars['identifier'])) {
564
            return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
565
        }
566
        if (!in_array($this->piVars['metadataPrefix'], array_keys($this->formats))) {
567
            return $this->error('cannotDisseminateFormat');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('cannotDisseminateFormat') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
568
        }
569
        try {
570
            $documentSet = $this->fetchDocumentUIDs();
571
        } catch (\Exception $exception) {
572
            return $this->error($exception->getMessage());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error($exception->getMessage()) returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
573
        }
574
        $resultSet = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class);
575
        $resultSet->reset();
576
        $resultSet->add($documentSet);
577
        $resultSet->metadata = [
578
            'completeListSize' => count($documentSet),
579
            'metadataPrefix' => $this->piVars['metadataPrefix'],
580
        ];
581
        return $this->generateOutputForDocumentList($resultSet);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->generateOu...ocumentList($resultSet) returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
582
    }
583
584
    /**
585
     * Process verb "ListMetadataFormats"
586
     *
587
     * @access protected
588
     *
589
     * @return \DOMElement XML node to add to the OAI response
590
     */
591
    protected function verbListMetadataFormats() {
592
        $resArray = [];
593
        // Check for invalid arguments.
594
        if (count($this->piVars) > 1) {
595
            if (empty($this->piVars['identifier']) || count($this->piVars) > 2) {
596
                return $this->error('badArgument');
597
            }
598
            // Check given identifier.
599
            $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
600
                'tx_dlf_documents.*',
601
                'tx_dlf_documents',
602
                'tx_dlf_documents.pid='.intval($this->conf['pages'])
603
                    .' AND tx_dlf_documents.record_id='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['identifier'], 'tx_dlf_documents'),
604
                '',
605
                '',
606
                '1'
607
            );
608
            if (!$GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
609
                return $this->error('idDoesNotExist');
610
            }
611
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
612
        }
613
        // Add metadata formats node.
614
        $ListMetadaFormats = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'ListMetadataFormats');
615
        foreach ($this->formats as $prefix => $details) {
616
            if (!empty($resArray)) {
617
                foreach ($details['requiredFields'] as $required) {
618
                    if (empty($resArray[$required])) {
619
                        // Skip metadata formats whose requirements are not met.
620
                        continue 2;
621
                    }
622
                }
623
            }
624
            // Add format node.
625
            $format = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'metadataFormat');
626
            $format->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'metadataPrefix', htmlspecialchars($prefix, ENT_NOQUOTES, 'UTF-8')));
627
            $format->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'schema', htmlspecialchars($details['schema'], ENT_NOQUOTES, 'UTF-8')));
628
            $format->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'metadataNamespace', htmlspecialchars($details['namespace'], ENT_NOQUOTES, 'UTF-8')));
629
            $ListMetadaFormats->appendChild($format);
630
        }
631
        return $ListMetadaFormats;
632
    }
633
634
    /**
635
     * Process verb "ListRecords"
636
     *
637
     * @access protected
638
     *
639
     * @return string Substitution for subpart "###RESPONSE###"
640
     */
641
    protected function verbListRecords() {
642
        // Check for invalid arguments.
643
        if (!empty($this->piVars['resumptionToken'])) {
644
            // "resumptionToken" is an exclusive argument.
645
            if (count($this->piVars) > 2) {
646
                return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
647
            } else {
648
                return $this->resume();
649
            }
650
        }
651
        if (empty($this->piVars['metadataPrefix']) || !empty($this->piVars['identifier'])) {
652
            // "metadataPrefix" is required and "identifier" is not allowed.
653
            return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
654
        }
655
        // Check "metadataPrefix" for valid value.
656
        if (!in_array($this->piVars['metadataPrefix'], array_keys($this->formats))) {
657
            return $this->error('cannotDisseminateFormat');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('cannotDisseminateFormat') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
658
        }
659
        try {
660
            $documentSet = $this->fetchDocumentUIDs();
661
        } catch (\Exception $exception) {
662
            return $this->error($exception->getMessage());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error($exception->getMessage()) returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
663
        }
664
        $resultSet = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class);
665
        $resultSet->reset();
666
        $resultSet->add($documentSet);
667
        $resultSet->metadata = [
668
            'completeListSize' => count($documentSet),
669
            'metadataPrefix' => $this->piVars['metadataPrefix'],
670
        ];
671
        return $this->generateOutputForDocumentList($resultSet);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->generateOu...ocumentList($resultSet) returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
672
    }
673
674
    /**
675
     * Process verb "ListSets"
676
     *
677
     * @access protected
678
     *
679
     * @return string Substitution for subpart "###RESPONSE###"
680
     */
681
    protected function verbListSets() {
682
        // Check for invalid arguments.
683
        if (count($this->piVars) > 1) {
684
            if (!empty($this->piVars['resumptionToken'])) {
685
                return $this->error('badResumptionToken');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badResumptionToken') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
686
            } else {
687
                return $this->error('badArgument');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('badArgument') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
688
            }
689
        }
690
        $where = '';
691
        if (!$this->conf['show_userdefined']) {
692
            $where = ' AND tx_dlf_collections.fe_cruser_id=0';
693
        }
694
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
695
            'tx_dlf_collections.oai_name AS oai_name,tx_dlf_collections.label AS label',
696
            'tx_dlf_collections',
697
            'tx_dlf_collections.sys_language_uid IN (-1,0)'
698
                .' AND NOT tx_dlf_collections.oai_name=""'
699
                .' AND tx_dlf_collections.pid='.intval($this->conf['pages'])
700
                .$where
701
                .Helper::whereClause('tx_dlf_collections'),
702
            'tx_dlf_collections.oai_name',
703
            'tx_dlf_collections.oai_name',
704
            ''
705
        );
706
        if (!$GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
707
            return $this->error('noSetHierarchy');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->error('noSetHierarchy') returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
708
        }
709
        $ListSets = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'ListSets');
710
        while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
711
            $set = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'set');
712
            $set->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'setSpec', htmlspecialchars($resArray['oai_name'], ENT_NOQUOTES, 'UTF-8')));
713
            $set->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'setName', htmlspecialchars($resArray['label'], ENT_NOQUOTES, 'UTF-8')));
714
            $ListSets->appendChild($set);
715
        }
716
        return $ListSets;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ListSets returns the type DOMElement which is incompatible with the documented return type string.
Loading history...
717
    }
718
719
    /**
720
     * Fetch records
721
     *
722
     * @access protected
723
     *
724
     * @return array Array of matching records
725
     * @throws \Exception
726
     */
727
    protected function fetchDocumentUIDs() {
728
        $solr_query = '';
729
        if (!$this->conf['show_userdefined']) {
730
            $where = ' AND tx_dlf_collections.fe_cruser_id=0';
731
        }
732
        // Check "set" for valid value.
733
        if (!empty($this->piVars['set'])) {
734
            // For SOLR we need the index_name of the collection,
735
            // For DB Query we need the UID of the collection
736
            $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
737
                'tx_dlf_collections.index_name AS index_name, tx_dlf_collections.uid AS uid, tx_dlf_collections.index_search as index_query ',
738
                'tx_dlf_collections',
739
                'tx_dlf_collections.pid='.intval($this->conf['pages'])
740
                    .' AND tx_dlf_collections.oai_name='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->piVars['set'], 'tx_dlf_collections')
741
                    .$where
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $where does not seem to be defined for all execution paths leading up to this point.
Loading history...
742
                    .Helper::whereClause('tx_dlf_collections'),
743
                '',
744
                '',
745
                '1'
746
            );
747
            if (!$GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
748
                throw new \Exception('noSetHierarchy');
749
            }
750
            $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
751
            if ($resArray['index_query'] != "") {
752
                $solr_query .= '('.$resArray['index_query'].')';
753
            } else {
754
                $solr_query .= 'collection:'.'"'.$resArray['index_name'].'"';
755
            }
756
        } else {
757
            // If no set is specified we have to query for all collections
758
            $solr_query .= 'collection:* NOT collection:""';
759
        }
760
        // Check for required fields.
761
        foreach ($this->formats[$this->piVars['metadataPrefix']]['requiredFields'] as $required) {
762
            $solr_query .= ' NOT '.$required.':""';
763
        }
764
        // toplevel="true" is always required
765
        $solr_query .= ' AND toplevel:"true"';
766
        $from = "*";
767
        // Check "from" for valid value.
768
        if (!empty($this->piVars['from'])) {
769
            // Is valid format?
770
            if (is_array($date_array = strptime($this->piVars['from'], '%Y-%m-%dT%H:%M:%SZ'))
771
                || is_array($date_array = strptime($this->piVars['from'], '%Y-%m-%d'))) {
772
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'], $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
773
                $from = date("Y-m-d", $timestamp).'T'.date("H:i:s", $timestamp).'.000Z';
774
            } else {
775
                throw new \Exception('badArgument');
776
            }
777
        }
778
        $until = "*";
779
        // Check "until" for valid value.
780
        if (!empty($this->piVars['until'])) {
781
            // Is valid format?
782
            if (is_array($date_array = strptime($this->piVars['until'], '%Y-%m-%dT%H:%M:%SZ'))
783
                || is_array($date_array = strptime($this->piVars['until'], '%Y-%m-%d'))) {
784
                $timestamp = gmmktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'], $date_array['tm_mon'] + 1, $date_array['tm_mday'], $date_array['tm_year'] + 1900);
785
                $until = date("Y-m-d", $timestamp).'T'.date("H:i:s", $timestamp).'.999Z';
786
                if ($from != "*" && $from > $until) {
787
                    throw new \Exception('badArgument');
788
                }
789
            } else {
790
                throw new \Exception('badArgument');
791
            }
792
        }
793
        // Check "from" and "until" for same granularity.
794
        if (!empty($this->piVars['from'])
795
            && !empty($this->piVars['until'])) {
796
            if (strlen($this->piVars['from']) != strlen($this->piVars['until'])) {
797
                throw new \Exception('badArgument');
798
            }
799
        }
800
        $solr_query .= ' AND timestamp:['.$from.' TO '.$until.']';
801
        $documentSet = [];
802
        $solr = Solr::getInstance($this->conf['solrcore']);
803
        if (intval($this->conf['solr_limit']) > 0) {
804
            $solr->limit = intval($this->conf['solr_limit']);
0 ignored issues
show
Bug Best Practice introduced by
The property $limit is declared protected in Kitodo\Dlf\Common\Solr. Since you implement __set, consider adding a @property or @property-write.
Loading history...
805
        }
806
        // We only care about the UID in the results and want them sorted
807
        $parameters = [
808
            "fields" => "uid",
809
            "sort" => [
810
                "uid" => "asc"
811
            ]
812
        ];
813
        $result = $solr->search_raw($solr_query, $parameters);
814
        if (empty($result)) {
815
            throw new \Exception('noRecordsMatch');
816
        }
817
        foreach ($result as $doc) {
818
            $documentSet[] = $doc->uid;
819
        }
820
        return $documentSet;
821
    }
822
823
    /**
824
     * Fetch more information for document list
825
     * @access protected
826
     *
827
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
828
     *
829
     * @return \DOMElement XML of enriched records
830
     */
831
    protected function generateOutputForDocumentList(DocumentList $documentListSet) {
832
        $documentsToProcess = $documentListSet->removeRange(0, intval($this->conf['limit']));
833
        $verb = $this->piVars['verb'];
834
        $documents = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
835
            'tx_dlf_documents.*,GROUP_CONCAT(DISTINCT tx_dlf_collections.oai_name ORDER BY tx_dlf_collections.oai_name SEPARATOR " ") AS collections',
836
            'tx_dlf_documents',
837
            'tx_dlf_relations',
838
            'tx_dlf_collections',
839
            'AND tx_dlf_documents.uid IN ('.implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($documentsToProcess)).')'
840
                .' AND tx_dlf_documents.pid='.intval($this->conf['pages'])
841
                .' AND tx_dlf_collections.pid='.intval($this->conf['pages'])
842
                .' AND tx_dlf_relations.ident='.$GLOBALS['TYPO3_DB']->fullQuoteStr('docs_colls', 'tx_dlf_relations')
843
                .Helper::whereClause('tx_dlf_collections'),
844
            'tx_dlf_documents.uid',
845
            'tx_dlf_documents.tstamp',
846
            $this->conf['limit']
847
        );
848
        $output = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', $verb);
849
        while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($documents)) {
850
            // Add header node.
851
            $header = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'header');
852
            $header->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'identifier', htmlspecialchars($resArray['record_id'], ENT_NOQUOTES, 'UTF-8')));
853
            $header->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'datestamp', gmdate('Y-m-d\TH:i:s\Z', $resArray['tstamp'])));
854
            // Check if document is deleted or hidden.
855
            // TODO: Use TYPO3 API functions here!
856
            if ($resArray['deleted']
857
                || $resArray['hidden']) {
858
                // Add "deleted" status.
859
                $header->setAttribute('status', 'deleted');
860
                if ($verb == 'ListRecords') {
861
                    // Add record node.
862
                    $record = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'record');
863
                    $record->appendChild($header);
864
                    $output->appendChild($record);
865
                } elseif ($verb == 'ListIdentifiers') {
866
                    $output->appendChild($header);
867
                }
868
            } else {
869
                // Add sets.
870
                foreach (explode(' ', $resArray['collections']) as $spec) {
871
                    $header->appendChild($this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'setSpec', htmlspecialchars($spec, ENT_NOQUOTES, 'UTF-8')));
872
                }
873
                if ($verb == 'ListRecords') {
874
                    // Add record node.
875
                    $record = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'record');
876
                    $record->appendChild($header);
877
                    // Add metadata node.
878
                    $metadata = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'metadata');
879
                    $metadataPrefix = $this->piVars['metadataPrefix'];
880
                    if (!$metadataPrefix) {
881
                        // If we resume an action the metadataPrefix is stored with the documentSet
882
                        $metadataPrefix = $documentListSet->metadata['metadataPrefix'];
0 ignored issues
show
Bug Best Practice introduced by
The property $metadata is declared protected in Kitodo\Dlf\Common\DocumentList. Since you implement __get, consider adding a @property or @property-read.
Loading history...
883
                    }
884
                    switch ($metadataPrefix) {
885
                        case 'oai_dc':
886
                            $metadata->appendChild($this->getDcData($resArray));
887
                            break;
888
                        case 'epicur':
889
                            $metadata->appendChild($this->getEpicurData($resArray));
890
                            break;
891
                        case 'mets':
892
                            $metadata->appendChild($this->getMetsData($resArray));
893
                            break;
894
                    }
895
                    $record->appendChild($metadata);
896
                    $output->appendChild($record);
897
                } elseif ($verb == 'ListIdentifiers') {
898
                    $output->appendChild($header);
899
                }
900
            }
901
        }
902
        $output->appendChild($this->generateResumptionTokenForDocumentListSet($documentListSet));
903
        return $output;
904
    }
905
906
    /**
907
     * Generate resumption token
908
     *
909
     * @access protected
910
     *
911
     * @param \Kitodo\Dlf\Common\DocumentList $documentListSet
912
     *
913
     * @return \DOMElement XML for resumption token
914
     */
915
    protected function generateResumptionTokenForDocumentListSet(DocumentList $documentListSet) {
916
        if ($documentListSet->count() != 0) {
917
            $token = uniqid();
918
            $GLOBALS['TYPO3_DB']->exec_INSERTquery(
919
                'tx_dlf_tokens',
920
                [
921
                    'tstamp' => $GLOBALS['EXEC_TIME'],
922
                    'token' => $token,
923
                    'options' => serialize($documentListSet),
924
                    'ident' => 'oai',
925
                ]
926
            );
927
            if ($GLOBALS['TYPO3_DB']->sql_affected_rows() == 1) {
928
                $resumptionToken = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'resumptionToken', htmlspecialchars($token, ENT_NOQUOTES, 'UTF-8'));
929
            } else {
930
                Helper::devLog('Could not create resumption token', DEVLOG_SEVERITY_ERROR);
931
            }
932
        } else {
933
            // Result set complete. We don't need a token.
934
            $resumptionToken = $this->oai->createElementNS('http://www.openarchives.org/OAI/2.0/', 'resumptionToken');
935
        }
936
        $resumptionToken->setAttribute('cursor', intval($documentListSet->metadata['completeListSize']) - count($documentListSet));
0 ignored issues
show
Bug Best Practice introduced by
The property $metadata is declared protected in Kitodo\Dlf\Common\DocumentList. Since you implement __get, consider adding a @property or @property-read.
Loading history...
Comprehensibility Best Practice introduced by
The variable $resumptionToken does not seem to be defined for all execution paths leading up to this point.
Loading history...
937
        $resumptionToken->setAttribute('completeListSize', $documentListSet->metadata['completeListSize']);
938
        $resumptionToken->setAttribute('expirationDate', gmdate('Y-m-d\TH:i:s\Z', $GLOBALS['EXEC_TIME'] + $this->conf['expired']));
939
        return $resumptionToken;
940
    }
941
}
942