diff --git a/filter/ArticleCrossrefXmlFilter.php b/filter/ArticleCrossrefXmlFilter.php index b69c264..b3add14 100644 --- a/filter/ArticleCrossrefXmlFilter.php +++ b/filter/ArticleCrossrefXmlFilter.php @@ -25,6 +25,8 @@ use DOMDocument; use DOMElement; use Illuminate\Support\Carbon; +use PKP\author\contributorRole\ContributorRoleIdentifier; +use PKP\author\contributorRole\ContributorType; use PKP\citation\Citation; use PKP\citation\enum\CitationSourceType; use PKP\citation\enum\CitationType; @@ -347,14 +349,51 @@ public function createContributorsNode(DOMDocument $doc, Publication $publicatio $isFirst = true; foreach ($publication->getData('authors') as $author) { /** @var Author $author */ - $personNameNode = $doc->createElementNS($deployment->getNamespace(), 'person_name'); - $personNameNode->setAttribute('contributor_role', 'author'); - if ($isFirst) { - $personNameNode->setAttribute('sequence', 'first'); - } else { - $personNameNode->setAttribute('sequence', 'additional'); + $contribRoleIds = $author->getContributorRoleIdentifiers(); + + // Role 'other' not supported yet in 5.4.0, do not export that role and skip if no other roles are assigned. + $contribRoleIds = array_values(array_diff($contribRoleIds, [ContributorRoleIdentifier::OTHER->getName()])); + if (empty($contribRoleIds)) { + continue; + } + + // Crossref allows only one role per person_name, so prioritize + // 'author' if present, otherwise use the first assigned role + // https://www.crossref.org/documentation/schema-library/markup-guide-metadata-segments/contributors/#00011 + $contributorRole = in_array(ContributorRoleIdentifier::AUTHOR->getName(), $contribRoleIds) + ? ContributorRoleIdentifier::AUTHOR->getName() + : $contribRoleIds[0]; + + $contributorRole = strtolower(str_replace('_', '-', $contributorRole)); + $contributorType = $author->getData('contributorType'); + $sequence = $isFirst ? 'first' : 'additional'; + + // Contributor type ORGANIZATION + if ($contributorType === ContributorType::ORGANIZATION->getName()) { + $organizationNode = $doc->createElementNS($deployment->getNamespace(), 'organization', htmlspecialchars($author->getLocalizedOrganizationName($locale), ENT_COMPAT, 'UTF-8')); + $organizationNode->setAttribute('contributor_role', $contributorRole); + $organizationNode->setAttribute('sequence', $sequence); + $contributorsNode->appendChild($organizationNode); + $isFirst = false; + continue; + } + + // Contributor type ANONYMOUS + if ($contributorType === ContributorType::ANONYMOUS->getName()) { + $anonymousNode = $doc->createElementNS($deployment->getNamespace(), 'anonymous'); + $anonymousNode->setAttribute('contributor_role', $contributorRole); + $anonymousNode->setAttribute('sequence', $sequence); + $this->appendAffiliationsNode($doc, $anonymousNode, $author, $locale); + $contributorsNode->appendChild($anonymousNode); + $isFirst = false; + continue; } + // Contributor type PERSON + $personNameNode = $doc->createElementNS($deployment->getNamespace(), 'person_name'); + $personNameNode->setAttribute('contributor_role', $contributorRole); + $personNameNode->setAttribute('sequence', $sequence); + $familyNames = $author->getFamilyName(null); $givenNames = $author->getGivenName(null); @@ -367,29 +406,7 @@ public function createContributorsNode(DOMDocument $doc, Publication $publicatio $personNameNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'surname', htmlspecialchars($givenNames[$locale], ENT_COMPAT, 'UTF-8'))); } - $affiliationsNode = null; - foreach ($author->getAffiliations() as $affiliation) { - $institutionName = $affiliation->getLocalizedName($locale); - if (trim($institutionName ?? '') === '') { - continue; - } - if ($affiliationsNode === null) { - $affiliationsNode = $doc->createElementNS($deployment->getNamespace(), 'affiliations'); - } - $institutionNode = $doc->createElementNS($deployment->getNamespace(), 'institution'); - $institutionNameNode = $doc->createElementNS($deployment->getNamespace(), 'institution_name', htmlspecialchars($institutionName, ENT_COMPAT, 'UTF-8')); - $institutionNode->appendChild($institutionNameNode); - $rorId = $affiliation->getRor(); - if ($rorId) { - $institutionIdNode = $doc->createElementNS($deployment->getNamespace(), 'institution_id', $rorId); - $institutionIdNode->setAttribute('type', 'ror'); - $institutionNode->appendChild($institutionIdNode); - } - $affiliationsNode->appendChild($institutionNode); - } - if ($affiliationsNode !== null) { - $personNameNode->appendChild($affiliationsNode); - } + $this->appendAffiliationsNode($doc, $personNameNode, $author, $locale); if ($author->getData('orcid')) { $orcidNode = $doc->createElementNS($deployment->getNamespace(), 'ORCID', $author->getData('orcid')); @@ -424,9 +441,40 @@ public function createContributorsNode(DOMDocument $doc, Publication $publicatio $contributorsNode->appendChild($personNameNode); $isFirst = false; } + return $contributorsNode; } + /** + * Append an affiliations node + */ + public function appendAffiliationsNode(DOMDocument $doc, DOMElement $parentNode, Author $author, string $locale): void + { + $affiliationsNode = null; + foreach ($author->getAffiliations() as $affiliation) { + $institutionName = $affiliation->getLocalizedName($locale); + if (trim($institutionName ?? '') === '') { + continue; + } + if ($affiliationsNode === null) { + $affiliationsNode = $doc->createElementNS($this->getDeployment()->getNamespace(), 'affiliations'); + } + $institutionNode = $doc->createElementNS($this->getDeployment()->getNamespace(), 'institution'); + $institutionNode->appendChild( + $doc->createElementNS($this->getDeployment()->getNamespace(), 'institution_name', htmlspecialchars($institutionName, ENT_COMPAT, 'UTF-8')) + ); + if ($rorId = $affiliation->getRor()) { + $institutionIdNode = $doc->createElementNS($this->getDeployment()->getNamespace(), 'institution_id', $rorId); + $institutionIdNode->setAttribute('type', 'ror'); + $institutionNode->appendChild($institutionIdNode); + } + $affiliationsNode->appendChild($institutionNode); + } + if ($affiliationsNode) { + $parentNode->appendChild($affiliationsNode); + } + } + /** * Append jats:abstract node */