From d94d6a0cf8911a65cc6d95ba64cc792b421d008f Mon Sep 17 00:00:00 2001
From: Daniel Berthereau <Daniel.github@Berthereau.net>
Date: Mon, 13 Aug 2018 00:00:00 +0200
Subject: [PATCH] Added the simple but non-standard format "oai_dcterms".

---
 README.md                                     |   7 +
 config/module.config.php                      |   1 +
 src/OaiPmh/Metadata/Mets.php                  |  32 ++--
 src/OaiPmh/Metadata/OaiDc.php                 |  40 +++--
 src/OaiPmh/Metadata/OaiDcterms.php            | 161 ++++++++++++++++++
 .../OaiPmh/Metadata/OaiDctermsFactory.php     |  30 ++++
 6 files changed, 247 insertions(+), 24 deletions(-)
 create mode 100644 src/OaiPmh/Metadata/OaiDcterms.php
 create mode 100644 src/Service/OaiPmh/Metadata/OaiDctermsFactory.php

diff --git a/README.md b/README.md
index d3021ab..1ad0a0a 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,13 @@ The Dublin Core is required by the OAI-PMH specification for all repositories.
 Omeka S metadata fields are mapped one-to-one with fields for this output
 format.
 
+### [Dublin Core Terms] (prefix `oai_dcterms`)
+
+The OAI-PMH standard does not manage the complete set of Dublin Core, but it is
+largely used. So this format is similar to `oai_dc`, but with all 55 terms.
+
+**NOTE**: The namespace and the schema don’t exist.
+
 ### [CDWA Lite] (prefix `cdwalite`)
 
 The mapping between Omeka’s metadata and CDWA Lite metadata is more complicated,
diff --git a/config/module.config.php b/config/module.config.php
index 3de75aa..a713c0c 100644
--- a/config/module.config.php
+++ b/config/module.config.php
@@ -85,6 +85,7 @@ return [
                 'mets' => Service\OaiPmh\Metadata\MetsFactory::class,
                 'mods' => Service\OaiPmh\Metadata\ModsFactory::class,
                 'oai_dc' => Service\OaiPmh\Metadata\OaiDcFactory::class,
+                'oai_dcterms' => Service\OaiPmh\Metadata\OaiDctermsFactory::class,
             ],
         ],
         'oai_set_formats' => [
diff --git a/src/OaiPmh/Metadata/Mets.php b/src/OaiPmh/Metadata/Mets.php
index 8989bff..a11e238 100755
--- a/src/OaiPmh/Metadata/Mets.php
+++ b/src/OaiPmh/Metadata/Mets.php
@@ -59,18 +59,30 @@ class Mets extends AbstractMetadata
         $dcXml = $this->appendNewElement($dcWrap, 'xmlData');
         $dcXml->setAttribute('xmlns:dc', self::DC_NAMESPACE_URI);
 
-        $dcElementNames = [
-            'title', 'creator', 'subject', 'description', 'publisher',
-            'contributor', 'date', 'type', 'format', 'identifier', 'source',
-            'language', 'relation', 'coverage', 'rights',
+        $localNames = [
+            'title',
+            'creator',
+            'subject',
+            'description',
+            'publisher',
+            'contributor',
+            'date',
+            'type',
+            'format',
+            'identifier',
+            'source',
+            'language',
+            'relation',
+            'coverage',
+            'rights',
         ];
 
-        foreach ($dcElementNames as $elementName) {
-            $term = 'dcterms:' . $elementName;
+        foreach ($localNames as $localName) {
+            $term = 'dcterms:' . $localName;
             $values = $item->value($term, ['all' => true, 'default' => []]);
             $values = $this->filterValues($item, $term, $values);
             foreach ($values as $value) {
-                $this->appendNewElement($dcXml, "dc:$elementName", (string) $value);
+                $this->appendNewElement($dcXml, 'dc:' . $localName, (string) $value);
             }
         }
 
@@ -112,12 +124,12 @@ class Mets extends AbstractMetadata
 
                     $fileIds[] = $fileId;
 
-                    foreach ($dcElementNames as $elementName) {
-                        $term = 'dcterms:' . $elementName;
+                    foreach ($localNames as $localName) {
+                        $term = 'dcterms:' . $localName;
                         $values = $media->value($term, ['all' => true, 'default' => []]);
                         $values = $this->filterValues($media, $term, $values);
                         foreach ($values as $value) {
-                            $this->appendNewElement($fileDcXml, 'dc:' . $elementName, (string) $value);
+                            $this->appendNewElement($fileDcXml, 'dc:' . $localName, (string) $value);
                         }
                     }
                 }
diff --git a/src/OaiPmh/Metadata/OaiDc.php b/src/OaiPmh/Metadata/OaiDc.php
index 6ef1385..d6258ed 100644
--- a/src/OaiPmh/Metadata/OaiDc.php
+++ b/src/OaiPmh/Metadata/OaiDc.php
@@ -38,48 +38,60 @@ class OaiDc extends AbstractMetadata
     {
         $document = $metadataElement->ownerDocument;
 
-        $oai_dc = $document->createElementNS(self::METADATA_NAMESPACE, 'oai_dc:dc');
-        $metadataElement->appendChild($oai_dc);
+        $oai = $document->createElementNS(self::METADATA_NAMESPACE, 'oai_dc:dc');
+        $metadataElement->appendChild($oai);
 
         /* Must manually specify XML schema uri per spec, but DOM won't include
          * a redundant xmlns:xsi attribute, so we just set the attribute
          */
-        $oai_dc->setAttribute('xmlns:dc', self::DC_NAMESPACE_URI);
-        $oai_dc->setAttribute('xmlns:xsi', parent::XML_SCHEMA_NAMESPACE_URI);
-        $oai_dc->setAttribute('xsi:schemaLocation', self::METADATA_NAMESPACE . ' ' .
+        $oai->setAttribute('xmlns:dc', self::DC_NAMESPACE_URI);
+        $oai->setAttribute('xmlns:xsi', parent::XML_SCHEMA_NAMESPACE_URI);
+        $oai->setAttribute('xsi:schemaLocation', self::METADATA_NAMESPACE . ' ' .
             self::METADATA_SCHEMA);
 
         /* Each of the 15 unqualified Dublin Core elements, in the order
          * specified by the oai_dc XML schema
          */
-        $dcElementNames = [
-            'title', 'creator', 'subject', 'description', 'publisher',
-            'contributor', 'date', 'type', 'format', 'identifier', 'source',
-            'language', 'relation', 'coverage', 'rights',
+        $localNames = [
+            'title',
+            'creator',
+            'subject',
+            'description',
+            'publisher',
+            'contributor',
+            'date',
+            'type',
+            'format',
+            'identifier',
+            'source',
+            'language',
+            'relation',
+            'coverage',
+            'rights',
         ];
 
         /* Must create elements using createElement to make DOM allow a
          * top-level xmlns declaration instead of wasteful and non-
          * compliant per-node declarations.
          */
-        foreach ($dcElementNames as $elementName) {
-            $term = 'dcterms:' . $elementName;
+        foreach ($localNames as $localName) {
+            $term = 'dcterms:' . $localName;
             $values = $item->value($term, ['all' => true, 'default' => []]);
             $values = $this->filterValues($item, $term, $values);
             foreach ($values as $value) {
-                $this->appendNewElement($oai_dc, "dc:$elementName", (string) $value);
+                $this->appendNewElement($oai, 'dc:' . $localName, (string) $value);
             }
         }
 
         $appendIdentifier = $this->singleIdentifier($item);
         if ($appendIdentifier) {
-            $this->appendNewElement($oai_dc, 'dc:identifier', $appendIdentifier);
+            $this->appendNewElement($oai, 'dc:identifier', $appendIdentifier);
         }
 
         // Also append an identifier for each file
         if ($this->settings->get('oaipmhrepository_expose_media', false)) {
             foreach ($item->media() as $media) {
-                $this->appendNewElement($oai_dc, 'dc:identifier', $media->originalUrl());
+                $this->appendNewElement($oai, 'dc:identifier', $media->originalUrl());
             }
         }
     }
diff --git a/src/OaiPmh/Metadata/OaiDcterms.php b/src/OaiPmh/Metadata/OaiDcterms.php
new file mode 100644
index 0000000..f7faeb3
--- /dev/null
+++ b/src/OaiPmh/Metadata/OaiDcterms.php
@@ -0,0 +1,161 @@
+<?php
+/**
+ * @author John Flatness, Yu-Hsun Lin
+ * @copyright Copyright 2009 John Flatness, Yu-Hsun Lin
+ * @copyright BibLibre, 2016
+ * @copyright Daniel Berthereau, 2014-2018
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ */
+namespace OaiPmhRepository\OaiPmh\Metadata;
+
+use DOMElement;
+use Omeka\Api\Representation\ItemRepresentation;
+
+/**
+ * Class implementing metadata output for the oai_dcterms metadata format.
+ * oai_dcterms is output of the 55 Dublin Core terms.
+ *
+ * This format is not standardized, but used by some repositories.
+ * Note: the namespace and the schema don’t exist. It is designed as an extended
+ * version of oai_dc.
+ *
+ * @link http://www.bl.uk/schemas/
+ * @link http://dublincore.org/documents/dc-xml-guidelines/
+ * @link http://dublincore.org/schemas/xmls/qdc/dcterms.xsd
+ */
+class OaiDcterms extends AbstractMetadata
+{
+    /** OAI-PMH metadata prefix */
+    const METADATA_PREFIX = 'oai_dcterms';
+
+    /** XML namespace for output format */
+    const METADATA_NAMESPACE = 'http://www.openarchives.org/OAI/2.0/oai_dcterms/';
+
+    /** XML schema for output format */
+    const METADATA_SCHEMA = 'http://www.openarchives.org/OAI/2.0/oai_dcterms.xsd';
+
+    /** XML namespace for Dublin Core */
+    const DCTERMS_NAMESPACE_URI = 'http://purl.org/dc/terms/';
+
+    /**
+     * Appends Dublin Core terms metadata.
+     *
+     * {@inheritDoc}
+     */
+    public function appendMetadata(DOMElement $metadataElement, ItemRepresentation $item)
+    {
+        $document = $metadataElement->ownerDocument;
+
+        $oai = $document->createElementNS(self::METADATA_NAMESPACE, 'oai_dcterms:dcterms');
+        $metadataElement->appendChild($oai);
+
+        /* Must manually specify XML schema uri per spec, but DOM won't include
+         * a redundant xmlns:xsi attribute, so we just set the attribute
+         */
+        $oai->setAttribute('xmlns:dcterms', self::DCTERMS_NAMESPACE_URI);
+        $oai->setAttribute('xmlns:xsi', parent::XML_SCHEMA_NAMESPACE_URI);
+        $oai->setAttribute('xsi:schemaLocation', self::METADATA_NAMESPACE . ' ' .
+            self::METADATA_SCHEMA);
+
+        // Each of the 55 Dublin Core terms, in the Omeka order.
+        $localNames = [
+            // Dublin Core Elements.
+            'title',
+            'creator',
+            'subject',
+            'description',
+            'publisher',
+            'contributor',
+            'date',
+            'type',
+            'format',
+            'identifier',
+            'source',
+            'language',
+            'relation',
+            'coverage',
+            'rights',
+            // Dublin Core terms.
+            'audience',
+            'alternative',
+            'tableOfContents',
+            'abstract',
+            'created',
+            'valid',
+            'available',
+            'issued',
+            'modified',
+            'extent',
+            'medium',
+            'isVersionOf',
+            'hasVersion',
+            'isReplacedBy',
+            'replaces',
+            'isRequiredBy',
+            'requires',
+            'isPartOf',
+            'hasPart',
+            'isReferencedBy',
+            'references',
+            'isFormatOf',
+            'hasFormat',
+            'conformsTo',
+            'spatial',
+            'temporal',
+            'mediator',
+            'dateAccepted',
+            'dateCopyrighted',
+            'dateSubmitted',
+            'educationLevel',
+            'accessRights',
+            'bibliographicCitation',
+            'license',
+            'rightsHolder',
+            'provenance',
+            'instructionalMethod',
+            'accrualMethod',
+            'accrualPeriodicity',
+            'accrualPolicy',
+        ];
+
+        /* Must create elements using createElement to make DOM allow a
+         * top-level xmlns declaration instead of wasteful and non-
+         * compliant per-node declarations.
+         */
+        foreach ($localNames as $localName) {
+            $term = 'dcterms:' . $localName;
+            $values = $item->value($term, ['all' => true, 'default' => []]);
+            $values = $this->filterValues($item, $term, $values);
+            foreach ($values as $value) {
+                $this->appendNewElement($oai, $term, (string) $value);
+            }
+        }
+
+        $appendIdentifier = $this->singleIdentifier($item);
+        if ($appendIdentifier) {
+            $this->appendNewElement($oai, 'dcterms:identifier', $appendIdentifier);
+        }
+
+        // Also append an identifier for each file
+        if ($this->settings->get('oaipmhrepository_expose_media', false)) {
+            foreach ($item->media() as $media) {
+                $this->appendNewElement($oai, 'dcterms:identifier', $media->originalUrl());
+            }
+        }
+    }
+
+    public function getMetadataPrefix()
+    {
+        return self::METADATA_PREFIX;
+    }
+
+    public function getMetadataSchema()
+    {
+        return self::METADATA_SCHEMA;
+    }
+
+    public function getMetadataNamespace()
+    {
+        return self::METADATA_NAMESPACE;
+    }
+}
diff --git a/src/Service/OaiPmh/Metadata/OaiDctermsFactory.php b/src/Service/OaiPmh/Metadata/OaiDctermsFactory.php
new file mode 100644
index 0000000..7ec8865
--- /dev/null
+++ b/src/Service/OaiPmh/Metadata/OaiDctermsFactory.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace OaiPmhRepository\Service\OaiPmh\Metadata;
+
+use Interop\Container\ContainerInterface;
+use OaiPmhRepository\OaiPmh\Metadata\OaiDcterms;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+class OaiDctermsFactory implements FactoryInterface
+{
+    /**
+     * Prepare the OaiDcterms format.
+     *
+     * @return OaiDcterms
+     */
+    public function __invoke(ContainerInterface $services, $requestedName, array $options = null)
+    {
+        $settings = $services->get('Omeka\Settings');
+        $oaiSetManager = $services->get('OaiPmhRepository\OaiPmh\OaiSetManager');
+        $oaiSet = $oaiSetManager->get($settings->get('oaipmhrepository_oai_set_format', 'base'));
+        $metadataFormat = new OaiDcterms();
+        $metadataFormat->setEventManager($services->get('EventManager'));
+        $metadataFormat->setSettings($settings);
+        $metadataFormat->setOaiSet($oaiSet);
+        $isGlobalRepository = !$services->get('ControllerPluginManager')
+            ->get('params')->fromRoute('__SITE__', false);
+        $metadataFormat->setIsGlobalRepository($isGlobalRepository);
+        return $metadataFormat;
+    }
+}
-- 
GitLab