Passed
Pull Request — master (#123)
by
unknown
04:52
created

Mods::getAuthorFromOrcidApi()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 12
rs 10
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Format;
14
15
use Kitodo\Dlf\Api\Orcid\Profile as OrcidProfile;
16
use Kitodo\Dlf\Api\Viaf\Profile as ViafProfile;
17
use Kitodo\Dlf\Common\MetadataInterface;
18
19
/**
20
 * Metadata MODS format class for the 'dlf' extension
21
 *
22
 * @package TYPO3
23
 * @subpackage dlf
24
 *
25
 * @access public
26
 */
27
class Mods implements MetadataInterface
28
{
29
    /**
30
     * @access private
31
     * @var \SimpleXMLElement The metadata XML
32
     **/
33
    private $xml;
34
35
    /**
36
     * @access private
37
     * @var array The metadata array
38
     **/
39
    private $metadata;
40
41
    /**
42
     * @access private
43
     * @var bool The metadata array
44
     **/
45
    private $useExternalApis;
46
47
    /**
48
     * This extracts the essential MODS metadata from XML
49
     *
50
     * @access public
51
     *
52
     * @param \SimpleXMLElement $xml The XML to extract the metadata from
53
     * @param array &$metadata The metadata array to fill
54
     * @param bool $useExternalApis true if external APIs should be called, false otherwise
55
     *
56
     * @return void
57
     */
58
    public function extractMetadata(\SimpleXMLElement $xml, array &$metadata, bool $useExternalApis): void
59
    {
60
        $this->xml = $xml;
61
        $this->metadata = $metadata;
62
        $this->useExternalApis = $useExternalApis;
63
64
        $this->xml->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
65
66
        $this->getAuthors();
67
        $this->getHolders();
68
        $this->getPlaces();
69
        $this->getYears();
70
71
        $metadata = $this->metadata;
72
    }
73
74
    /**
75
     * Get "author" and "author_sorting".
76
     *
77
     * @access private
78
     *
79
     * @return void
80
     */
81
    private function getAuthors(): void
82
    {
83
        $authors = $this->xml->xpath('./mods:name[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="aut"]');
84
85
        // Get "author" and "author_sorting" again if that was too sophisticated.
86
        if (empty($authors)) {
87
            // Get all names which do not have any role term assigned and assume these are authors.
88
            $authors = $this->xml->xpath('./mods:name[not(./mods:role)]');
89
        }
90
        if (!empty($authors)) {
91
            for ($i = 0, $j = count($authors); $i < $j; $i++) {
92
                $authors[$i]->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
93
94
                $identifier = $authors[$i]->xpath('./mods:name/mods:nameIdentifier[@type="orcid"]');
95
                if ($this->useExternalApis && !empty((string) $identifier[0])) {
96
                    $this->getAuthorFromOrcidApi((string) $identifier[0], $authors, $i);
97
                } else {
98
                    $this->getAuthorFromXml($authors, $i);
99
                }
100
            }
101
        }
102
    }
103
104
    /**
105
     * Get author from ORCID API.
106
     *
107
     * @access private
108
     *
109
     * @param string $orcidId
110
     * @param array $authors
111
     * @param int $i
112
     *
113
     * @return void
114
     */
115
    private function getAuthorFromOrcidApi(string $orcidId, array $authors, int $i): void
116
    {
117
        $profile = new OrcidProfile($orcidId);
118
        $name = $profile->getFullName();
119
        if (!empty($name)) {
120
            $this->metadata['author'][$i] = [
121
                'name' => $name,
122
                'url' => 'https://orcid.org/' . $orcidId
123
            ];
124
        } else {
125
            //fallback into display form
126
            $this->getAuthorFromXmlDisplayForm($authors, $i);
127
        }
128
    }
129
130
    /**
131
     * Get author from XML.
132
     *
133
     * @access private
134
     *
135
     * @param array $authors
136
     * @param int $i
137
     *
138
     * @return void
139
     */
140
    private function getAuthorFromXml(array $authors, int $i): void
141
    {
142
        $this->getAuthorFromXmlDisplayForm($authors, $i);
143
144
        $nameParts = $authors[$i]->xpath('./mods:namePart');
145
146
        if (empty($this->metadata['author'][$i]) && $nameParts) {
147
            $name = [];
148
            $k = 4;
149
            foreach ($nameParts as $namePart) {
150
                if (
151
                    isset($namePart['type'])
152
                    && (string) $namePart['type'] == 'family'
153
                ) {
154
                    $name[0] = (string) $namePart;
155
                } elseif (
156
                    isset($namePart['type'])
157
                    && (string) $namePart['type'] == 'given'
158
                ) {
159
                    $name[1] = (string) $namePart;
160
                } elseif (
161
                    isset($namePart['type'])
162
                    && (string) $namePart['type'] == 'termsOfAddress'
163
                ) {
164
                    $name[2] = (string) $namePart;
165
                } elseif (
166
                    isset($namePart['type'])
167
                    && (string) $namePart['type'] == 'date'
168
                ) {
169
                    $name[3] = (string) $namePart;
170
                } else {
171
                    $name[$k] = (string) $namePart;
172
                }
173
                $k++;
174
            }
175
            ksort($name);
176
            $this->metadata['author'][$i] = trim(implode(', ', $name));
177
        }
178
        // Append "valueURI" to name using Unicode unit separator.
179
        if (isset($authors[$i]['valueURI'])) {
180
            $this->metadata['author'][$i] .= pack('C', 31) . (string) $authors[$i]['valueURI'];
181
        }
182
    }
183
184
    /**
185
     * Get author from XML display form.
186
     *
187
     * @access private
188
     *
189
     * @param array $authors
190
     * @param int $i
191
     *
192
     * @return void
193
     */
194
    private function getAuthorFromXmlDisplayForm(array $authors, int $i): void
195
    {
196
        $displayForm = $authors[$i]->xpath('./mods:displayForm');
197
        if ($displayForm) {
198
            $this->metadata['author'][$i] = (string) $displayForm[0];
199
        }
200
    }
201
202
    /**
203
     * Get holder.
204
     *
205
     * @access private
206
     *
207
     * @return void
208
     */
209
    private function getHolders(): void
210
    {
211
        $holders = $this->xml->xpath('./mods:name[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="prv"]');
212
213
        if (!empty($holders)) {
214
            for ($i = 0, $j = count($holders); $i < $j; $i++) {
215
                $holders[$i]->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
216
217
                $identifier = $holders[$i]->xpath('./mods:name/mods:nameIdentifier[@type="viaf"]');
218
                if ($this->useExternalApis && !empty((string) $identifier[0])) {
219
                    $this->getHolderFromViafApi((string) $identifier[0], $holders, $i);
220
                } else {
221
                    $this->getHolderFromXml($holders, $i);
222
                }
223
            }
224
        }
225
    }
226
227
    /**
228
     * Get holder from VIAF API.
229
     *
230
     * @access private
231
     *
232
     * @param string $viafId
233
     * @param array $holders
234
     * @param int $i
235
     *
236
     * @return void
237
     */
238
    private function getHolderFromViafApi(string $viafId, array $holders, int $i): void
239
    {
240
        $profile = new ViafProfile($viafId);
241
        $name = $profile->getFullName();
242
        if (!empty($name)) {
243
            $this->metadata['holder'][$i] = [
244
                'name' => $name,
245
                'url' => 'http://viaf.org/viaf/' . $viafId
246
            ];
247
        } else {
248
            //fallback into display form
249
            $this->getHolderFromXmlDisplayForm($holders, $i);
250
        }
251
    }
252
253
    /**
254
     * Get holder from XML.
255
     *
256
     * @access private
257
     *
258
     * @param array $holders
259
     * @param int $i
260
     *
261
     * @return void
262
     */
263
    private function getHolderFromXml(array $holders, int $i): void
264
    {
265
        $this->getHolderFromXmlDisplayForm($holders, $i);
266
        // Append "valueURI" to name using Unicode unit separator.
267
        if (isset($holders[$i]['valueURI'])) {
268
            $this->metadata['holder'][$i] .= pack('C', 31) . (string) $holders[$i]['valueURI'];
269
        }
270
    }
271
272
    /**
273
     * Get holder from XML display form.
274
     *
275
     * @access private
276
     * 
277
     * @param array $holders
278
     * @param int $i
279
     *
280
     * @return void
281
     */
282
    private function getHolderFromXmlDisplayForm(array $holders, int $i): void
283
    {
284
        // Check if there is a display form.
285
        $displayForm = $holders[$i]->xpath('./mods:displayForm');
286
        if ($displayForm) {
287
            $this->metadata['holder'][$i] = (string) $displayForm[0];
288
        }
289
    }
290
291
    /**
292
     * Get "place" and "place_sorting".
293
     *
294
     * @access private
295
     *
296
     * @return void
297
     */
298
    private function getPlaces(): void
299
    {
300
        $places = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:place/mods:placeTerm');
301
        // Get "place" and "place_sorting" again if that was to sophisticated.
302
        if (empty($places)) {
303
            // Get all places and assume these are places of publication.
304
            $places = $this->xml->xpath('./mods:originInfo/mods:place/mods:placeTerm');
305
        }
306
        if (!empty($places)) {
307
            foreach ($places as $place) {
308
                $this->metadata['place'][] = (string) $place;
309
                if (!$this->metadata['place_sorting'][0]) {
310
                    $this->metadata['place_sorting'][0] = preg_replace('/[[:punct:]]/', '', (string) $place);
311
                }
312
            }
313
        }
314
    }
315
316
    /**
317
     * Get "year" and "year_sorting".
318
     *
319
     * @access private
320
     *
321
     * @return void
322
     */
323
    private function getYears(): void
324
    {
325
        // Get "year_sorting".
326
        $yearsSorting = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:dateOther[@type="order" and @encoding="w3cdtf"]');
327
        if ($yearsSorting) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $yearsSorting of type array<mixed,SimpleXMLElement> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
328
            foreach ($yearsSorting as $yearSorting) {
329
                $this->metadata['year_sorting'][0] = (int) $yearSorting;
330
            }
331
        }
332
        // Get "year" and "year_sorting" if not specified separately.
333
        $years = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:dateIssued[@keyDate="yes"]');
334
        // Get "year" and "year_sorting" again if that was to sophisticated.
335
        if (empty($years)) {
336
            // Get all dates and assume these are dates of publication.
337
            $years = $this->xml->xpath('./mods:originInfo/mods:dateIssued');
338
        }
339
        if (!empty($years)) {
340
            foreach ($years as $year) {
341
                $this->metadata['year'][] = (string) $year;
342
                if (!$this->metadata['year_sorting'][0]) {
343
                    $yearSorting = str_ireplace('x', '5', preg_replace('/[^\d.x]/i', '', (string) $year));
344
                    if (
345
                        strpos($yearSorting, '.')
0 ignored issues
show
Bug introduced by
It seems like $yearSorting can also be of type array; however, parameter $haystack of strpos() does only seem to accept string, 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

345
                        strpos(/** @scrutinizer ignore-type */ $yearSorting, '.')
Loading history...
346
                        || strlen($yearSorting) < 3
0 ignored issues
show
Bug introduced by
It seems like $yearSorting can also be of type array; however, parameter $string of strlen() does only seem to accept string, 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

346
                        || strlen(/** @scrutinizer ignore-type */ $yearSorting) < 3
Loading history...
347
                    ) {
348
                        $yearSorting = (((int) trim($yearSorting, '.') - 1) * 100) + 50;
0 ignored issues
show
Bug introduced by
It seems like $yearSorting can also be of type array; however, parameter $string of trim() does only seem to accept string, 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

348
                        $yearSorting = (((int) trim(/** @scrutinizer ignore-type */ $yearSorting, '.') - 1) * 100) + 50;
Loading history...
349
                    }
350
                    $this->metadata['year_sorting'][0] = (int) $yearSorting;
351
                }
352
            }
353
        }
354
    }
355
}
356