diff --git a/build.xml b/build.xml index 7a64863c3..055e30283 100644 --- a/build.xml +++ b/build.xml @@ -156,10 +156,12 @@ + + diff --git a/composer.json b/composer.json index 45252ddd5..9a6e4132e 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ "homepage": "http://opus4.kobv.de", "require": { "zendframework/zendframework1": "1.12.*", - "solarium/solarium": "3.4.*" + "solarium/solarium": "3.4.*", + "opus4-repo/opus4-doi": "4.6.2" }, "autoload": { "psr-0": { @@ -28,7 +29,6 @@ "phpunit/phpunit-selenium": "1.4.2", "doctrine/instantiator": "1.0.5", "phploc/phploc": "*", - "phpdocumentor/phpdocumentor": "2.*", "phpmd/phpmd" : "2.4.3", "sebastian/phpcpd": "*", "mayflower/php-codebrowser": "~1.1", diff --git a/db/schema/009-doi_support.sql b/db/schema/009-doi_support.sql new file mode 100644 index 000000000..9d32ed935 --- /dev/null +++ b/db/schema/009-doi_support.sql @@ -0,0 +1,14 @@ +START TRANSACTION; + +-- Add two columns to document_identifiers for DOI support + +ALTER TABLE `document_identifiers` + ADD `status` ENUM('registered', 'verified') NULL COMMENT 'DOI registration status', + ADD `registration_ts` TIMESTAMP NULL COMMENT 'timestamp of DOI registration'; + +-- Update database version + +TRUNCATE TABLE `schema_version`; +INSERT INTO `schema_version` (`version`) VALUES (9); + +COMMIT; diff --git a/db/schema/opus4schema.sql b/db/schema/opus4schema.sql index 92fcdc54a..8c4515bf2 100755 --- a/db/schema/opus4schema.sql +++ b/db/schema/opus4schema.sql @@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS `opus_version` ( -- The values are generated through svn checkin. -- Do not edit here. -- ----------------------------------------------------- -INSERT INTO `schema_version` (`version`) VALUES (8); +INSERT INTO `schema_version` (`version`) VALUES (9); -- ----------------------------------------------------- -- Table `documents` @@ -74,6 +74,8 @@ CREATE TABLE IF NOT EXISTS `document_identifiers` ( `document_id` INT UNSIGNED NOT NULL COMMENT 'Foreign key to: documents.documents_id.' , `type` ENUM('doi', 'handle', 'urn', 'std-doi', 'url', 'cris-link', 'splash-url', 'isbn', 'issn', 'opus3-id', 'opac-id', 'uuid', 'serial', 'old', 'pmid', 'arxiv') NOT NULL COMMENT 'Type of the identifier.' , `value` TEXT NOT NULL COMMENT 'Value of the identifier.' , + `status` ENUM('registered', 'verified') NULL COMMENT 'DOI registration status' , + `registration_ts` TIMESTAMP NULL COMMENT 'timestamp of DOI registration' , PRIMARY KEY (`id`) , INDEX `fk_document_identifiers_documents` (`document_id` ASC) , INDEX `fk_document_identifiers_documents_type` (`document_id` ASC, `type` ASC) , diff --git a/library/Opus/Document.php b/library/Opus/Document.php index ad187155b..c1680d57d 100644 --- a/library/Opus/Document.php +++ b/library/Opus/Document.php @@ -80,7 +80,8 @@ class Opus_Document extends Opus_Model_AbstractDb { protected $_plugins = array( 'Opus_Document_Plugin_Index' => null, 'Opus_Document_Plugin_XmlCache' => null, - 'Opus_Document_Plugin_IdentifierUrn' => null + 'Opus_Document_Plugin_IdentifierUrn' => null, + 'Opus_Document_Plugin_IdentifierDoi' => null ); /** diff --git a/library/Opus/Document/Plugin/IdentifierDoi.php b/library/Opus/Document/Plugin/IdentifierDoi.php new file mode 100644 index 000000000..caa41fd91 --- /dev/null +++ b/library/Opus/Document/Plugin/IdentifierDoi.php @@ -0,0 +1,186 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +/** + * Plugin for generating identifiers of type DOI. + * + */ +class Opus_Document_Plugin_IdentifierDoi extends Opus_Model_Plugin_Abstract { + + // was muss hier alles ausgewertet werden: + // automatische Generierung einer DOI für das vorliegende Dokument, wenn + // 1. noch keine DOI vorhanden + // 2. Enrichment opus.doi.autoCreate wurde gesetzt + + // außerdem automatische Registrierung der DOI (Aufruf MDS-Webservice von DataCite) + // wenn DOI vorhanden und die Konfigurationseinstellung doi.registerAtPublish ist auf true/1 gesetzt + + + // laut Spezifikation: jedes OPUS-Dokument kann maximal eine zugeordnete DOI haben + // diese DOI ist entweder lokal oder extern + // im Rahmen der automatischen DOI-Registrierung werden nur lokale DOIs betrachtet + public function postStoreInternal(Opus_Model_AbstractDb $model) { + + $log = Zend_Registry::get('Zend_Log'); + + if (!($model instanceof Opus_Document)) { + $log->err('found unexpected model class ' . get_class($model)); + return; + } + + $serverState = $model->getServerState(); + $log->debug(__CLASS__ . ' postStoreInternal for ' . $model->getDisplayName() . ' and target state ' . $serverState); + + if ($serverState == 'published') { + $this->handlePublishEvent($model, $log); + return; + } + + if ($serverState == 'deleted') { + $this->handleDeleteEvent($model); + return; + } + + $log->err('plugin ' . __CLASS__ . ' is not applicable for documents with server state ' . $serverState); + return; + } + + private function handleDeleteEvent($document) { + // Metadatensatz für DOI auf den Status "inactive" setzen + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi($document); + } + + private function handlePublishEvent($document, $log) { + // prüfe zuerst, ob das Dokument das Enrichment opus.doi.autoCreate besitzt + // in diesem Fall wird nun eine DOI gemäß der Konfigurationseinstellungen generiert + $generateDoi = null; + $enrichment = $document->getEnrichment('opus.doi.autoCreate'); + if (!is_null($enrichment)) { + $enrichmentValue = $enrichment->getValue(); + $generateDoi = ($enrichmentValue == 'true'); + $log->debug('found enrichment opus.doi.autoCreate with value ' . $enrichmentValue); + } + + $config = Zend_Registry::get('Zend_Config'); + + if (is_null($generateDoi)) { + // Enrichment opus.doi.autoCreate wurde nicht gefunden - verwende Standardwert für die DOI-Erzeugung aus Konfiguration + $generateDoi = (isset($config->doi->autoCreate) && ($config->doi->autoCreate || $config->doi->autoCreate == '1')); + } + + // prüfe, ob bereits eine DOI mit dem Dokument verknüpft ist + if (!empty($document->getIdentifierDoi())) { + $log->debug('could not assign more than one DOI to document ' . $document->getId()); + } + else { + // $generateDoi kann hier nicht mehr null sein: aktueller Wert entscheidet, ob neue DOI generiert wird + if ($generateDoi) { + try { + $this->addDoi($document, $log); + } + catch (Exception $e) { + $log->err('could not generate local DOI for document ' . $document->getId() . ' - abort DOI registration procedure'); + return; + } + } + } + + // prüfe, ob DOI bei DataCite registriert werden soll -> wenn ja, dann Versuch der synchronen Registrierung + $this->registerDoi($document, $log, $config); + } + + /** + * Fügt zum Dokument $model eine DOI hinzu, sofern noch keine existiert und die Konfiguration + * entsprechend gesetzt ist. + * + * @param $model Opus_Document zu dem die DOI hinzugefügt werden soll + */ + private function addDoi($model, $log) { + + try { + $doiManager = new Opus_Doi_DoiManager(); + $doiValue = $doiManager->generateNewDoi($model); + } + catch (Opus_Doi_DoiException $e) { + $message = 'could not generate DOI value for document ' . $model->getId() . ': ' . $e->getMessage(); + $log->err($message); + throw new Exception($message); + } + + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue($doiValue); + + $identifiers = $model->getIdentifier(); + if (is_null($identifiers)) { + $identifiers = array(); + } + $identifiers[] = $doi; + $model->setIdentifier($identifiers); + + $log->debug('DOI ' . $doiValue . ' was generated for document ' . $model->getId()); + } + + /** + * Registriert die mit dem Dokument verknüpfte DOI bei DataCite unter Verwendung des DoiManagers + * + * @param $model + */ + private function registerDoi($model, $log, $config) { + + // prüfe ob Konfigurationseinstellung eine Registrierung vorgibt + if (!isset($config->doi->registerAtPublish) || !($config->doi->registerAtPublish || $config->doi->registerAtPublish == '1')) { + $log->debug('registration of DOIs at publish time is disabled in configuration'); + return; + } + + // führe die Registrierung durch + $log->info('start registration of DOI for document ' . $model->getId()); + + try { + $doiManager = new Opus_Doi_DoiManager(); + $registeredDoi = $doiManager->register($model); + if (is_null($registeredDoi)) { + $log->err('could not apply DOI registration on document ' . $model->getId()); + } + } + catch (Opus_Doi_RegistrationException $e) { + $log->err('unexpected error in registration of DOI ' . $e->getDoi() . ' of document ' . $model->getId() . ': ' . $e->getMessage()); + } + catch (Opus_Doi_DoiException $e) { + $log->err('unexpected error in DOI-registration of document ' . $model->getId() . ': ' . $e->getMessage()); + } + + } +} diff --git a/library/Opus/Document/Plugin/IdentifierUrn.php b/library/Opus/Document/Plugin/IdentifierUrn.php index 91f51da03..49c9193e7 100644 --- a/library/Opus/Document/Plugin/IdentifierUrn.php +++ b/library/Opus/Document/Plugin/IdentifierUrn.php @@ -29,9 +29,8 @@ * @author Thoralf Klein * @copyright Copyright (c) 2009-2010 * Saechsische Landesbibliothek - Staats- und Universitaetsbibliothek Dresden (SLUB) - * @copyright Copyright (c) 2010-2012, OPUS 4 development team + * @copyright Copyright (c) 2010-2018, OPUS 4 development team * @license http://www.gnu.org/licenses/gpl.html General Public License - * @version $Id$ */ /** @@ -41,14 +40,15 @@ * @package Opus_Document_Plugin * @uses Opus_Model_Plugin_Abstract */ -class Opus_Document_Plugin_IdentifierUrn extends Opus_Model_Plugin_Abstract { +class Opus_Document_Plugin_IdentifierUrn extends Opus_Model_Plugin_Abstract +{ /** * Generates a new URN for any document that has no URN assigned yet. * URN's are generated for Opus_Document instances only. */ - public function postStoreInternal(Opus_Model_AbstractDb $model) { - + public function postStoreInternal(Opus_Model_AbstractDb $model) + { if(!($model instanceof Opus_Document)) return; @@ -61,18 +61,40 @@ public function postStoreInternal(Opus_Model_AbstractDb $model) { $log->debug('IdentifierUrn postStoreInternal for ' . $model->getDisplayName()); - if(!isset($config->urn->autoCreate) or $config->urn->autoCreate != '1') { + // prüfe zuerst, ob das Dokument das Enrichment opus.urn.autoCreate besitzt + // in diesem Fall bestimmt der Wert des Enrichments, ob eine URN beim Publish generiert wird + $generateUrn = null; + $enrichment = $model->getEnrichment('opus.urn.autoCreate'); + if (!is_null($enrichment)) { + $enrichmentValue = $enrichment->getValue(); + $generateUrn = ($enrichmentValue == 'true'); + $log->debug('found enrichment opus.urn.autoCreate with value ' . $enrichmentValue); + } + + if (is_null($generateUrn)) { + // Enrichment opus.urn.autoCreate wurde nicht gefunden - verwende Standardwert für die URN-Erzeugung aus Konfiguration + $generateUrn = (isset($config->urn->autoCreate) && ($config->urn->autoCreate || $config->urn->autoCreate == '1')); + } + + if (!$generateUrn) { $log->debug('URN auto creation is not configured. skipping...'); return; } - if(!isset($config->urn->nid) || !isset($config->urn->nss)) { + if (!isset($config->urn->nid) || !isset($config->urn->nss)) { throw new Opus_Document_Exception('URN data is not present in config. Aborting...'); + // FIXME hier sollte keine Exception geworfen werden, weil sonst + // die Ausführung aller nachfolgenden Plugins im Plugin-Array abgebrochen wird + // Plugins werden nämlich in Schleife nacheinander aufgerufen (ohne Exception Handling zwischen + // den einzelnen Aufrufen) + + // FIXME außerdem ist der Exception Type schlecht gewählt, weil es sich in diesem + // Fall ja um einen Konfigurationsfehler handelt und nicht um einen Fehler im Dokument } $log->debug('config.ini is set to support urn auto generation'); - if($this->urnAlreadyPresent($model)) { + if ($this->urnAlreadyPresent($model)) { $log->debug('Model ' . $model->getDisplayName() . ' already has a URN. Skipping automatic generation.'); return; } @@ -96,6 +118,12 @@ public function postStoreInternal(Opus_Model_AbstractDb $model) { $model->addIdentifierUrn($urn_model); } + /** + * Liefert true, wenn das vorliegende Dokument bereits einen Identifier vom Typ URN besitzt; andernfalls false. + * + * @param $document + * @return bool + */ public function urnAlreadyPresent($document) { $identifierUrns = $document->getIdentifierUrn(); if(count($identifierUrns) > 0) { @@ -112,10 +140,17 @@ public function urnAlreadyPresent($document) { return false; } - public function allowUrnOnThisDocument($document) { + /** + * Liefert true, wenn das vorliegende Dokumente mindestens eine Datei mit OAI-Sichtbarkeit besitzt (nur für solche + * Dokumente kann bei der DNB eine URN registriert werden) + * + * @param $document + * @return bool + */ + public function allowUrnOnThisDocument($document) + { $files = array_filter($document->getFile(), function ($f) { return $f->getVisibleInOai() == 1; }); return count($files) > 0; } } - diff --git a/library/Opus/Doi/DataCiteXmlGenerationException.php b/library/Opus/Doi/DataCiteXmlGenerationException.php new file mode 100644 index 000000000..02fd44ff6 --- /dev/null +++ b/library/Opus/Doi/DataCiteXmlGenerationException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DataCiteXmlGenerationException extends Opus_Doi_DoiException { + +} \ No newline at end of file diff --git a/library/Opus/Doi/DataCiteXmlGenerator.php b/library/Opus/Doi/DataCiteXmlGenerator.php new file mode 100644 index 000000000..616e43546 --- /dev/null +++ b/library/Opus/Doi/DataCiteXmlGenerator.php @@ -0,0 +1,199 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DataCiteXmlGenerator { + + const STYLESHEET_FILENAME = 'datacite.xslt'; + + /** + * Erzeugt für das übergebene OPUS-Dokument eine XML-Repräsentation, die von DataCite als + * Metadatenbeschreibung des Dokuments bei der DOI-Registrierung akzeptiert wird. + * + * @param $doc Opus_Document + */ + public function getXml($doc) { + + // DataCite-XML wird mittels XSLT aus OPUS-XML erzeugt + $xslt = new DOMDocument(); + $xsltPath = dirname(__FILE__) . DIRECTORY_SEPARATOR . self::STYLESHEET_FILENAME; + + $success = false; + if (file_exists($xsltPath)) { + $success = $xslt->load($xsltPath); + } + + $log = Zend_Registry::get('Zend_Log'); + if (!$success) { + $message = "could not find XSLT file $xsltPath"; + $log->err($message); + throw new Opus_Doi_DataCiteXmlGenerationException($message); + } + + $proc = new XSLTProcessor(); + $proc->importStyleSheet($xslt); + + if (!$this->checkRequiredFields($doc, $log)) { + throw new Opus_Doi_DataCiteXmlGenerationException('required fields are missing in document ' . $doc->getId() . ' - check log for details'); + } + + $modelXml = $this->getModelXml($doc); + $log->debug('OPUS-XML: ' . $modelXml->saveXML()); + + $this->handleLibXmlErrors($log, true); + $result = $proc->transformToDoc($modelXml); + if (!$result) { + $message = 'errors occurred in XSLT transformation of document ' . $doc->getId(); + $log->err($message); + $this->handleLibXmlErrors($log); + throw new Opus_Doi_DataCiteXmlGenerationException($message); + } + + $log->debug('DataCite-XML: '. $result->saveXML()); + + $this->handleLibXmlErrors($log, true); + + $xsdPath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'metadata.xsd'; + if (!file_exists($xsdPath)) { + $message = 'could not load schema file from ' . $xsdPath; + $log->err($message); + throw new Opus_Doi_DataCiteXmlGenerationException($message); + } + + // Validierung des erzeugten DataCite-XML findet bereits hier statt, da ein invalides XML + // beim späteren Registrierungsversuch einen HTTP Fehler 400 auslöst + $validationResult = $result->schemaValidate($xsdPath); + if (!$validationResult) { + $message = 'generated DataCite XML for document ' . $doc->getId() . ' is NOT valid'; + $log->err($message); + $this->handleLibXmlErrors($log); + throw new Opus_Doi_DataCiteXmlGenerationException($message); + } + + return $result->saveXML(); + } + + /** + * @param $doc Opus_Document + */ + private function checkRequiredFields($doc, $log) { + + // mind. ein Autor mit einem nicht-leeren LastName oder FirstName oder CreatingCorporation darf nicht leer sein + $authorOk = false; + $authors = $doc->getPersonAuthor(); + foreach ($authors as $author) { + if ($author->getLastName() != '' or $author->getFirstName() != '') { + $authorOk = true; + break; + } + } + if (!$authorOk) { + if ($doc->getCreatingCorporation() == '') { + $log->err('document ' . $doc->getId() . ' does not provide content for element creatorName'); + return false; + } + } + + // mind. ein nicht-leerer TitleMain oder TitleSub + $titleOk = false; + $titles = $doc->getTitleMain(); + foreach ($titles as $title) { + if ($title->getValue() != '') { + $titleOk = true; + break; + } + } + if (!$titleOk) { + $titles = $doc->getTitleSub(); + foreach ($titles as $title) { + if ($title->getValue() != '') { + $titleOk = true; + break; + } + } + if (!$titleOk) { + $log->err('document ' . $doc->getId() . ' does not provide content for element title'); + return false; + } + } + + // PublisherName nicht leer oder mind. ein ThesisPublisher mit nicht-leerem Namen + // FIXME was passiert, wenn mehr als ein ThesisPublisher mit Dokument verknüpft ist? + $publisherOk = false; + if ($doc->getPublisherName() != '') { + $publisherOk = true; + } + if (!$publisherOk) { + $thesisPublishers = $doc->getThesisPublisher(); + foreach ($thesisPublishers as $thesisPublisher) { + if ($thesisPublisher->getName() != '') { + $publisherOk = true; + break; + } + } + if (!$publisherOk) { + $log->err('document ' . $doc->getId() . ' does not provide content for element publisher'); + return false; + } + } + + // CompletedYear muss gefüllt sein + // FIXME alternativ auch andere Datumsfelder betrachten? + if ($doc->getCompletedYear() == '') { + $log->err('document ' . $doc->getId() . ' does not provide content for element publicationYear'); + return false; + } + + return true; + } + + private function handleLibXmlErrors($log, $reset = false) { + if ($reset) { + libxml_clear_errors(); + } + else { + foreach (libxml_get_errors() as $error) { + $log->err("libxml error: {$error->message}"); + } + } + libxml_use_internal_errors($reset); + } + + private function getModelXml($doc) { + $xmlDoc = new Opus_Model_Xml(); + $xmlDoc->setModel($doc); + $xmlDoc->excludeEmptyFields(); + $xmlDoc->setStrategy(new Opus_Model_Xml_Version1); + return $xmlDoc->getDomDocument(); + } +} + diff --git a/library/Opus/Doi/DoiException.php b/library/Opus/Doi/DoiException.php new file mode 100644 index 000000000..fafcb7a19 --- /dev/null +++ b/library/Opus/Doi/DoiException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiException extends Exception { + +} \ No newline at end of file diff --git a/library/Opus/Doi/DoiMailNotification.php b/library/Opus/Doi/DoiMailNotification.php new file mode 100644 index 000000000..0a8baea3b --- /dev/null +++ b/library/Opus/Doi/DoiMailNotification.php @@ -0,0 +1,295 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiMailNotification { + + private $config; + + private $log; + + /** + * @var bool Ist die Benachrichtigung über DOI-Ereignisse via E-Mail aktiviert + */ + private $enabled; + + /** + * @var array Namen und E-Mail-Adressen der Empfänger von DOI-Benachrichtigungen + */ + private $recipients; + + public function __construct() { + $this->notifications = array(); + $this->config = Zend_Registry::get('Zend_Config'); + $this->log = Zend_Registry::get('Zend_Log'); + + // check if email notifications for DOI events are enabled in general + if (isset($this->config->doi->notificationEmailEnabled) && + ($this->config->doi->notificationEmailEnabled || $this->config->doi->notificationEmailEnabled == '1')) { + $this->enabled = true; + } + else { + $this->log->info('configuration setting doi.notificationEmailEnabled was not set - DOI notifications are disabled'); + $this->enabled = false; + return; + } + + // check if any recipients for DOI notification emails are configured (otherwise notifications are disabled) + if (!isset($this->config->doi->notificationEmail) || empty($this->config->doi->notificationEmail->toArray()) || $this->config->doi->notificationEmail->toArray()[0] == '') { + $this->log->info('configuration setting doi.notificationEmail[] was not set - DOI notifications are disabled'); + $this->enabled = false; + } + else { + $this->initRecipients(); + } + } + + /** + * Initialisiert die Namen und Adressen der Empfänger von DOI-Benachrichtigungen. + */ + private function initRecipients() { + $recipientAddresses = $this->config->doi->notificationEmail->toArray(); + $this->recipients = array(); + foreach ($recipientAddresses as $recipient) { + $entry = array(); + $entry['name'] = $recipient; + $entry['address'] = $recipient; + $this->recipients[] = $entry; + } + } + + /** + * Liefert true, wenn das Verschicken von E-Mail-Benachrichtigungen aktiviert und korrekt konfiguriert ist. + * + * @return bool + */ + public function isEnabled() { + return $this->enabled; + } + + /** + * Fügt eine Benachrichtigung für die übergebene DOI zur E-Mail hinzu. + * + * @param $docId + * @param $doi + * @param null $errorMessage + */ + public function addNotification($docId, $doi, $errorMessage = null) { + $entry = array(); + $entry['docId'] = $docId; + $entry['doi'] = $doi; + $entry['errorMessage'] = $errorMessage; + $this->notifications[] = $entry; + } + + /** + * Versand einer E-Mail-Benachrichtigung nach der Registrierung von DOIs. + * Grundsätzlich werden E-Mail-Benachrichtigungen nur bei der asynchronen Registierung von DOIs verschickt. + * + * Wird statt einer einzelnen DOI eine Menge von DOIs in einem Vorgang registriert, so erfolgt + * der gebündelte Versand aller DOI-Benachrichtigungen in einer E-Mail. + * + */ + public function sendRegistrationEmail() { + $this->prepareMail('Registrierung'); + } + + /** + * Versand einer E-Mail-Benachrichtigung nach der Prüfung von DOIs. + * Grundsätzlich werden E-Mail-Benachrichtigungen nur bei der asynchronen Prüfung von DOIs verschickt. + * + * Wird statt einer einzelnen DOI eine Menge von DOIs in einem Vorgang geprüft, so erfolgt + * der gebündelte Versand aller DOI-Benachrichtigungen in einer E-Mail. + * + */ + public function sendVerificationEmail() { + $this->prepareMail('Prüfung'); + } + + /** + * Liefert eine Zeile für die zu erzeugende E-Mail-Benachrichtigung. + * + * @param $docId ID des OPUS-Dokuments, zu dem die DOI gehört + * @param $doi DOI auf die sich die Nachricht bezieht + * @param $errorMessage ggf. Meldung des aufgetretenen Fehlers bei Registrierung oder Prüfung + */ + private function buildMessageLine($docId, $doi, $errorMessage) { + $result = $doi->getValue() . ' '; + + $frontdoorUrl = $this->getUrl('frontdoor/index/index/' . $docId); + $result .= $frontdoorUrl . ' ' . $doi->getStatus(); + if (!is_null($errorMessage)) { + $result .= ' Fehlermeldung: ' . $errorMessage; + } + $result .= "\r\n"; + return $result; + } + + /** + * Bereitet Betreff und Inhalt für die E-Mail-Benachrichtigung vor. Löst am Ende den Versand + * der E-Mail-Benachrichtigung aus. + * + * @param $mode Registrierung oder Prüfung von DOIs (wird in den Betreff der E-Mail geschrieben) + */ + private function prepareMail($mode) { + if (!$this->enabled || empty($this->notifications)) { + // E-Mail-Versand ist nicht aktiviert / konfiguriert bzw. es gibt keinen Inhalt für den Bericht + return; + } + + $subject = 'Statusbericht über DOI-' . $mode; + + if (count($this->notifications) == 1) { + // Benachrichtigung für genau eine DOI: erweitere den Betreff der E-Mail-Benachrichtigung + $notification = $this->notifications[0]; + $doi = $notification['doi']; + $docId = $notification['docId']; + $errorMessage = $notification['errorMessage']; + $subject .= ' von DOI ' . $doi->getValue() . ' für Dokument mit ID ' . $docId; + $message = $this->buildMessageLine($docId, $doi, $errorMessage); + } + else { + // Versand einer gebündelten E-Mail-Benachrichtigung für mehrere DOIs + $message = ''; + foreach ($this->notifications as $notification) { + $doi = $notification['doi']; + $docId = $notification['docId']; + $errorMessage = $notification['errorMessage']; + $message .= $this->buildMessageLine($docId, $doi, $errorMessage); + } + } + + $message .= $this->addMailFooter(); + + // lösche den Nachrichtenpuffer, so dass Benachrichtigungen nicht mehrfach verschickt werden + $this->notifications = array(); + + $this->sendEmailNotification($subject, $message); + } + + /** + * Liefert den Footer für die E-Mail-Benachrichtigung. + * + * @return string + */ + private function addMailFooter() { + $result = "\r\n--\r\nDiese automatische E-Mail-Benachrichtigung wurde von OPUS4 verschickt.\r\n"; + $reportUrl = $this->getUrl('admin/report/doi'); + $result .= "Unter $reportUrl können Sie den Registrierungstatus aller lokalen DOIs einsehen.\r\n"; + return $result; + } + + private function sendEmailNotification($subject, $message) { + $from = $this->_getFrom(); + $fromName = $this->_getFromName(); + $replyTo = $this->_getReplyTo(); + $replyToName = $this->_getReplyToName(); + $returnPath = $this->_getReturnPath(); + + $this->log->debug('try to send DOI notification email with subject ' . $subject); + try { + $mailSendMail = new Opus_Mail_SendMail(); + $mailSendMail->sendMail( + $from, + $fromName, + $subject, + $message, + $this->recipients, + $replyTo, + $replyToName, + $returnPath); + $this->log->info('successful sending of DOI notification email with subject ' . $subject); + } + catch (Opus_Mail_Exception $e) { + $this->log->err('could not send DOI notification email with subject ' . $subject . ': ' . $e->getMessage()); + } + } + + protected function _getFrom() { + if (isset($this->config->mail->opus->address)) { + return $this->config->mail->opus->address; + } + return 'not configured'; + } + + protected function _getFromName() { + if (isset($this->config->mail->opus->name)) { + return $this->config->mail->opus->name; + } + return 'not configured'; + } + + protected function _getReplyTo() { + if (isset($this->config->mail->opus->replyTo)) { + return $this->config->mail->opus->replyTo; + } + return null; + } + + protected function _getReplyToName() { + if (isset($this->config->mail->opus->replyToName)) { + return $this->config->mail->opus->replyToName; + } + return null; + } + + protected function _getReturnPath() { + if (isset($this->config->mail->opus->returnPath)) { + return $this->config->mail->opus->returnPath; + } + return null; + } + + /** + * Erzeugt eine absolute URL auf Basis der in der Konfiguration definierten Basis-URL. + * Wird benötigt, damit in den E-Mail-Benachrichtigungen z.B. URLs von Dokument-Frontdoors + * aufgenommen werden können (Zend-MVC und die zugehörigen View-Helper stehen bei CLI-Skripten + * nicht zur Verfügung, so dass die Basis-URL nicht aus dem Request ermittelt werden kann) + * + * @param $path + * @return string + */ + private function getUrl($path) { + $result = ''; + if (isset($this->config->url)) { + $result .= $this->config->url; + // check if $result ends with '/' otherwise add one + if (!(substr($result, -strlen($result)) === '/')) { + $result .= '/'; + } + } + + $result .= $path; + return $result; + } + +} \ No newline at end of file diff --git a/library/Opus/Doi/DoiManager.php b/library/Opus/Doi/DoiManager.php new file mode 100644 index 000000000..53f582eaf --- /dev/null +++ b/library/Opus/Doi/DoiManager.php @@ -0,0 +1,660 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiManager { + + private $doiLog; + + private $defaultLog; + + private $config; + + private $landingPageUrl; + + public function __construct() { + $this->config = Zend_Registry::get('Zend_Config'); + $this->defaultLog = Zend_Registry::get('Zend_Log'); + + $logfilePath = $this->config->workspacePath . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR . 'opus-doi.log'; + $logfile = @fopen($logfilePath, 'a', false); + $writer = new Zend_Log_Writer_Stream($logfile); + + $format = '%timestamp% %priorityName%: %message%' . PHP_EOL; + $formatter = new Zend_Log_Formatter_Simple($format); + $writer->setFormatter($formatter); + + $this->doiLog = new Zend_Log($writer); + + if (isset($this->config->url)) { + $this->landingPageUrl = $this->config->url; + if (substr($this->landingPageUrl, -1) != '/') { + $this->landingPageUrl .= '/'; + } + $this->landingPageUrl .= 'frontdoor/index/index/'; + } + } + + /** + * Registriert die mit dem übergebenen Opus_Document verknüpfte lokale DOI bei DataCite. + * Liefert im Erfolgsfall die registrierte DOI zurück. Liefert null zurück, wenn das Dokument keine lokale + * DOI besitzt, die registriert werden kann. + * + * @param $doc Opus_Document oder ID eines Opus_Document als String + * @param $store wenn true, dann wird am Ende der Methode store() auf dem übergebenen $doc aufgerufen + * wenn die Methode im Kontext eines Store-Plugins aufgerufen wird, dann erfolgt der Aufruf + * von store() an anderer Stelle (sonst gibt es eine Endlosschleife) + * + * @throws DoiException wenn das referenzierte Dokument nicht in der Datenbank existiert + * @throws RegistrationException wenn bei dem Versuch der Registrierung bei DataCite ein Fehler auftritt + */ + public function register($doc, $store = false) { + if (is_string($doc)) { + $docId = $doc; + try { + $doc = new Opus_Document($docId); + } + catch (Opus_Model_NotFoundException $e) { + $message = 'could not find document with ID ' . $docId . ' in database'; + $this->defaultLog->err($message); + throw new Opus_Doi_DoiException($message); + } + } + + if (is_null($doc) || !($doc instanceof Opus_Document)) { + $message = 'unexpected document class'; + if (!is_null($doc)) { + $message .= ' ' . get_class($doc); + } + $this->defaultLog->err($message); + throw new Opus_Doi_DoiException($message); + } + + // prüfe, ob es überhaupt eine lokale DOI gibt, die registriert werden kann + $localDoi = $this->checkForLocalRegistrableDoi($doc); + if (is_null($localDoi)) { + $message = 'document ' . $doc->getId() . ' does not provide a local DOI that can be registered: abort DOI registration process'; + $this->doiLog->info($message); + $this->defaultLog->info($message); + return null; + } + + // prüfe, dass die lokale DOI nicht bereits registriert wurde + if (!is_null($localDoi->getStatus())) { + $message = 'document ' . $doc->getId() . ' does not provide a local unregistered DOI: abort DOI registration process'; + $this->doiLog->info($message); + $this->defaultLog->info($message); + return null; + } + + // nun müssen wir noch prüfen, ob die lokale DOI tatsächlich nur genau einmal in der Instanz vorkommt + if (!$this->checkDoiUniqueness($localDoi)) { + $message = 'document ' . $doc->getId() . ' does not provide a unique local DOI: abort DOI registration process'; + $this->doiLog->err($message); + $this->defaultLog->err($message); + return null; + } + + $xmlGen = new Opus_Doi_DataCiteXmlGenerator(); + try { + $xmlStr = $xmlGen->getXml($doc); + } + catch (Opus_Doi_DataCiteXmlGenerationException $e) { + $message = 'could not generate DataCite-XML for DOI registration of document ' . $doc->getId() . ': ' . $e->getMessage(); + $this->doiLog->err($message); + $this->defaultLog->err($message); + $doiException = new Opus_Doi_RegistrationException($message); + $doiException->setDoi($localDoi); + throw $doiException; + } + + try { + $client = new \Opus\Doi\Client($this->config, $this->defaultLog); + $client->registerDoi($localDoi->getValue(), $xmlStr, $this->getLandingPageUrlOfDoc($doc)); + } + catch (\Opus\Doi\ClientException $e) { + $message = 'an error occurred while registering DOI ' . $localDoi->getValue() . ' for document ' . $doc->getId() . ': ' . $e->getMessage(); + $this->doiLog->err($message); + $this->defaultLog->err($message); + $doiException = new Opus_Doi_RegistrationException($message); + $doiException->setDoi($localDoi); + throw $doiException; + } + + // set status and timestamp after successful DOI registration + $localDoi->setStatus('registered'); + $dateTimeZone = new DateTimeZone(date_default_timezone_get()); + $dateTime = new DateTime('now', $dateTimeZone); + $localDoi->setRegistrationTs($dateTime->format('Y-m-d H:i:s')); + if ($store) { + $doc->store(); + } + + $message = 'DOI ' . $localDoi->getValue() . ' of document ' . $doc->getId() . ' was registered successfully'; + $this->doiLog->info($message); + $this->defaultLog->info($message); + + return $localDoi; + } + + /** + * Gibt true zurück, wenn der Wert der übergebenen DOI nur genau einmal innerhalb der OPUS-Datenbank existiert. + * + * @param $doi Opus_Identifier (vom Typ doi) + */ + private function checkDoiUniqueness($doi) { + return $doi->isDoiUnique(); + } + + private function getDoi($doc) { + $identifiers = $doc->getIdentifier(); + if (is_null($identifiers) || empty($identifiers)) { + return null; + } + + foreach ($identifiers as $identifier) { + if ($identifier->getType() != 'doi') { + continue; + } + + // wenn das Dokument mehr als eine DOI hat, dann ist es ein Dokument, das bereits vor der Einführung des + // DOI-Supports in OPUS4 erstellt wurde: in diesem Fall wird für die Überprüfung nur die erste DOI betrachtet + if (is_null($identifier->getStatus())) { + // lokale DOI kann nur registriert werden, wenn ihr status auf null gesetzt ist + return $identifier; + } + } + + return null; + } + + /** + * Liefert null zurück, wenn das übergebene Opus_Document keine lokale DOI hat, die bei DataCite registriert werden + * kann oder eine bereits registrierte lokale DOI hat. Andernfalls gibt die Methode die lokale DOI (Objekt vom + * Typ Opus_Identifier) für die weitere Verarbeitung, d.h. Registrierung, zurück. + * + * Mit Jens vereinbart: eine lokale DOI wird anhand des "prefix" identifiziert. Der "localPrefix" wird bei der + * Erkennung von lokalen DOIs nur berücksichtigt, wenn er gesetzt ist. + * + */ + private function checkForLocalRegistrableDoi($doc) + { + $doiToBeChecked = $this->getDoi($doc); + if (is_null($doiToBeChecked)) { + $this->defaultLog->debug('document ' . $doc->getId() . ' does not provide an identifier of type DOI that can be registered'); + return null; + } + + // prüfe, dass es sich um eine lokale DOI handelt + $doiValue = $doiToBeChecked->getValue(); + $this->defaultLog->debug('check DOI ' . $doiValue); + + if (!isset($this->config->doi->prefix)) { + $message = 'configuration setting doi.prefix is not set - DOI registration cannot be applied'; + $this->doiLog->err($message); + $this->defaultLog->err($message); + return null; + } + + if ($this->isLocalDoi($doiValue)) { + return $doiToBeChecked; + } + + return null; + } + + /** + * Liefert true, wenn der übergebene Wert den Wert einer lokale DOI darstellt; andernfalls false. + * + * @param $value Wert einer DOI, der auf Lokalität geprüft werden soll + */ + private function isLocalDoi($value) + { + $doi = new Opus_Identifier(); + $doi->setValue($value); + return $doi->isLocalDoi(); + } + + /** + * Registriert alle lokalen DOIs mit status == null (d.h. die noch nicht bei DataCite registrierten lokalen DOIs) + * und liefert als Ergebnis die Anzahl der erfolgreich registrierten lokalen DOIs zurück. + * + * Wenn nicht anders gesetzt, dann werden DOIs nur für Dokumente im ServerState published registriert. Sollen + * alle Dokumente unabhängig von ihrem ServerState betrachtet werden, so muss als Aufrufargument null übergeben + * werden. + * + * @param $filterServerState Filter für Attribut ServerState (es werden nur Dokumente mit dem angegeben ServerState + * bei der Registrierung betrachtet); um alle Dokumente unabhängig vom ServerState zu + * betrachten, muss der Wert null übergeben werden (Default: published) + * + * @return Opus_Doi_DoiManagerStatus + * + */ + public function registerPending($filterServerState = 'published') { + $status = new Opus_Doi_DoiManagerStatus(); + + $docFinder = new Opus_DocumentFinder(); + $docFinder->setIdentifierTypeExists('doi'); + if (!is_null($filterServerState)) { + $docFinder->setServerState($filterServerState); + } + + $ids = $docFinder->ids(); + if (empty($ids)) { + $this->defaultLog->info('could not find documents that provide DOIs'); + return $status; + } + + $this->defaultLog->debug('registerPending found ' . count($ids) . ' published documents with DOIs that need to be checked'); + + $numOfSuccessfulRegistrations = 0; + $notification = new Opus_Doi_DoiMailNotification(); + + foreach ($ids as $id) { + try { + $doc = new Opus_Document($id); + } + catch (Opus_Model_NotFoundException $e) { + $this->defaultLog->err('could not find document ' . $id . ' in database'); + continue; + } + + // Registrierung der DOI durchführen, sofern es eine lokale DOI gibt, die noch nicht registriert wurde + try { + $registeredDoi = $this->register($doc, true); + if (!is_null($registeredDoi)) { + $numOfSuccessfulRegistrations++; + $status->addDocWithDoiStatus($id, $registeredDoi->getValue()); + + if ($notification->isEnabled()) { + $notification->addNotification($id, $registeredDoi); + } + } + } + catch (Opus_Doi_RegistrationException $e) { + $message = 'an error occurred in registration of DOI ' . $e->getDoi()->getValue() . ' of document ' . $id . ': ' . $e->getMessage(); + $this->defaultLog->err($message); + $this->doiLog->err($message); + $status->addDocWithDoiStatus($id, $message, true); + if ($notification->isEnabled()) { + $notification->addNotification($id, $e->getDoi(), $message); + } + } + catch (Opus_Doi_DoiException $e) { + $message = 'an error occurred in DOI registration for document ' . $id . ': ' . $e->getMessage(); + $this->defaultLog->err($message); + $this->doiLog->err($message); + $status->addDocWithDoiStatus($id, $message, true); + // hier kann kein Eintrag für die E-Mail erzeugt werden, da es keine DOI als Bezug gibt + } + } + + if ($numOfSuccessfulRegistrations > 0) { + $message = 'number of successful DOI registrations: ' . $numOfSuccessfulRegistrations; + $this->defaultLog->info($message); + $this->doiLog->info($message); + } + + if ($notification->isEnabled()) { + $notification->sendRegistrationEmail(); + } + + return $status; + } + + /** + * Prüfe alle registrierten DOIs (im Status registered) für alle OPUS-Dokumente in der Datenbank. + * + * @return Opus_Doi_DoiManagerStatus + */ + public function verifyRegistered() { + return $this->verifyRegisteredBefore(); + } + + /** + * Prüft, ob die lokale DOI des Dokuments mit der übergebenen ID erfolgreich auflösbar ist. + * Ist $allowReverification auf false, so werden nur DOIs berücksichtigt, die noch nicht geprüft wurden (Status ist + * registered); andernfalls werden auch DOIs mit Status verified erneut geprüft. + * + * Es kann eine zusätzliche Einschränkung der zu prüfenden DOIs auf Basis des Zeitstempels der DOI-Registrierung + * vorgenommen, sofern der Parameter $beforeDate gesetzt wird. + * + * Die Methode gibt im Erfolgsfall als auch bei fehlerhaftem Prüfungsergebnis die geprüfte lokale DOI zurück. + * Ist eine Prüfung nicht möglich, so gibt die Methode null zurück, z.B. wenn das Dokument mit der übergebenen ID + * gar keine DOI hat. + * + * @param $docId ID des zu überprüfenden OPUS-Dokuments + * @param $allowReverification wenn true, dann werden DOIs, die bereits geprüft wurden, erneut geprüft + * @param $beforeDate bei der Prüfung nur DOIs berücksichtigen, deren Registrierung vor dem übergebenen Zeitpunkt liegt + * @param Opus_Doi_DoiManagerStatus $managerStatus Objekt zum Ablegen von Statusinformationen der DOI-Prüfung + * + */ + public function verify($docId, $allowReverification = true, $beforeDate = null, $managerStatus = null) { + try { + $doc = new Opus_Document($docId); + } + catch (Opus_Model_NotFoundException $e) { + $message = 'could not find document with ID ' . $docId . ' in database'; + $this->doiLog->err($message); + $this->defaultLog->err($message); + return null; + } + + $dois = $doc->getIdentifierDoi(); + if (empty($dois)) { + // dieser Fall darf eigentlich nicht auftreten, da die Methode nur für Dokumente mit DOIs aufgerufen wird + $message = 'document ' . $docId . ' does not provide a DOI for verification'; + $this->doiLog->err($message); + $this->defaultLog->err($message); + return null; + } + + if (count($dois) > 1) { + // es wird grundsätzlich nur die erste DOI eines Dokuments betrachtet + // hat ein Dokument mehr als eine DOI, so muss es sich um ein Altdokument handeln, das vor der Einführung + // des DOI-Supports in OPUS4 angelegt wurde und bei dem noch mehrere DOIs angegeben werden durften + $this->defaultLog->info('document ' . $docId . ' provides ' . count($dois) . ' DOIs - consider only the first one for verification'); + } + + $doi = $dois[0]; + if (is_null($doi->getStatus())) { + // DOI wurde noch nicht registriert, so dass keine Prüfung möglich ist + $message = 'document ' . $docId . ' does not provide a registered DOI for verification'; + $this->doiLog->debug($message); + $this->defaultLog->debug($message); + return null; + } + + if (!$allowReverification && $doi->getStatus() == 'verified') { + // erneute Prüfung von bereits geprüften DOIs ist nicht gewünscht + $message = 'document ' . $docId . ' provides already verified DOI ' . $doi->getValue() . ' but DOI reverification is disabled'; + $this->doiLog->debug($message); + $this->defaultLog->debug($message); + return null; + } + + if (is_null($beforeDate) || (!is_null($beforeDate) && $doi->getRegistrationTs() <= $beforeDate)) { + + // prüfe, ob die DOI $doi bei DataCite erfolgreich registriert ist und setze dann den DOI-Status auf "verified" + try { + $client = new \Opus\Doi\Client($this->config, $this->defaultLog); + $result = $client->checkDoi($doi->getValue(), $this->getLandingPageUrlOfDoc($doc)); + if ($result) { + $message = 'verification of DOI ' . $doi->getValue() . ' of document ' . $docId . ' was successful'; + $this->doiLog->info($message); + $this->defaultLog->debug($message); + // Status-Upgrade durchführen + if ($doi->getStatus() != 'verified') { + $doi->setStatus('verified'); + $doc->store(); + } + if (!is_null($managerStatus)) { + $managerStatus->addDocWithDoiStatus($docId, $doi->getValue()); + } + } + else { + $message = 'verification of DOI ' . $doi->getValue() . ' in document ' . $docId . ' failed'; + $this->doiLog->err($message); + $this->defaultLog->err($message); + // Status-Downgrade durchführen + if ($doi->getStatus() == 'verified') { + $doi->setStatus('registered'); + $doc->store(); + } + if (!is_null($managerStatus)) { + $managerStatus->addDocWithDoiStatus($docId, $doi->getValue(), true); + } + } + return $doi; + } + catch (Exception $e) { + $message = 'could not get registration status of DOI ' . $doi->getValue() . ' in document ' . $docId . ': ' . $e->getMessage(); + $this->doiLog->err($message); + $this->defaultLog->err($message); + if (!is_null($managerStatus)) { + $managerStatus->addDocWithDoiStatus($docId, $message, true); + } + return $doi; + } + } + + return null; + } + + /** + * Prüfung aller registrierten DOIs, die vor einem definierten Zeitpunkt registriert wurden (z.B. vor 24 Stunden). + * Wird kein Zeitpunkt angegeben, so werden alle registrierten DOIs unabhängig vom Registrierungszeitraum geprüft. + * + * @param $beforeDate Zeitstempel der für die Bestimmung der zu prüfenden DOIs verwendet wird: es werden nur DOIs + * geprüft, die vor dem Zeitpunkt, der durch $beforeDate definiert ist, registriert wurden + * + * @return Opus_Doi_DoiManagerStatus + * + */ + public function verifyRegisteredBefore($beforeDate = null) { + $status = new Opus_Doi_DoiManagerStatus(); + + $docFinder = new Opus_DocumentFinder(); + $docFinder->setIdentifierTypeExists('doi'); + $ids = $docFinder->ids(); + + if (empty($ids)) { + return $status; + } + + $notification = new Opus_Doi_DoiMailNotification(); + + foreach ($ids as $id) { + $doi = $this->verify($id, false, $beforeDate, $status); + + if (is_null($doi)) { + $this->defaultLog->info('could not check DOI registration status of document ' . $id); + continue; + } + + if ($notification->isEnabled()) { + if ($doi->getStatus() == 'verified') { + // erfolgreiche Prüfung der DOI durchgeführt: Erfolg per E-Mail melden + $notification->addNotification($id, $doi); + } + else { + // fehlgeschlagene Prüfung der DOI: Fehler per E-Mail melden + $notification->addNotification($id, $doi, 'DOI-Prüfung war nicht erfolgreich'); + } + } + } + + if ($notification->isEnabled()) { + $notification->sendVerificationEmail(); + } + + return $status; + } + + /** + * Ermittelt alle OPUS-Dokumente, die eine lokale DOI mit dem übergebenen Status haben. + * Wenn $status nicht angegeben werden, so werden alle lokalen DOIs unabhängig vom Status betrachtet. + * + * @param $statusFilter Erlaubt die Filterung der zu berücksichtigenden DOIs nach ihrem Status. + */ + public function getAll($statusFilter = null) { + + // ermittle alle Dokumente, die eine lokale DOI haben + // wenn ein Dokument mehr als eine DOI haben sollte (Altdokument, das noch vor der + // Einführung des DOI-Supports angelegt wurde), dann wird nur die erste DOI betrachtet + // weil nur diese für eine DOI-Registrierung überhaupt in Frage kommt + + $result = array(); + + $docFinder = new Opus_DocumentFinder(); + $docFinder->setIdentifierTypeExists('doi'); + + foreach ($docFinder->ids() as $id) { + $doc = new Opus_Document($id); + $dois = $doc->getIdentifierDoi(); + $firstDoi = $dois[0]; + + // handelt es sich um eine lokale DOI? + if (!$this->isLocalDoi($firstDoi->getValue())) { + continue; + } + + // hat die lokale DOI den gesuchten Registrierungsstatus + $status = $firstDoi->getStatus(); + if (is_null($statusFilter) || + ($statusFilter == 'unregistered' && is_null($status)) || + ($statusFilter != 'unregistered' && $status == $statusFilter)) { + $result[] = $doc; + } + } + + return $result; + } + + /** + * Erzeugt auf Basis der konfigurierten DOI-Generator-Klasse einen DOI-Wert für das übergebene Dokument. + * Gibt den Wert zurück oder wirft eine Exception, wenn die Generierung nicht möglich ist. + * + * @param $doc OPUS-Dokument, für das ein DOI-Wert generiert werden soll oder ID eines OPUS-Dokuments + * wird eine ID als String übergeben, so wird versucht das zugehörige Opus_Document aus der Datenbank zu laden + * + * @throws DoiException + */ + public function generateNewDoi($doc) + { + $generator = null; + try { + $generator = Opus_Doi_Generator_DoiGeneratorFactory::create(); + } + catch (Opus_Doi_DoiException $e) { + $this->defaultLog->err($e->getMessage()); + $this->doiLog->err($e->getMessage()); + throw $e; + } + + if (is_string($doc) && is_numeric($doc)) { + $docId = $doc; + try { + $doc = new Opus_Document($docId); + } + catch (Opus_Model_NotFoundException $e) { + $message = 'could not find document ' . $docId . ' in database'; + $this->defaultLog->err($message); + throw new Opus_Doi_DoiException($message); + } + } + + if (is_null($doc) || !($doc instanceof Opus_Document)) { + $message = 'unexpected document class'; + $this->defaultLog->err($message); + throw new Opus_Doi_DoiException($message); + } + + try { + $doiValue = $generator->generate($doc); + } + catch (Opus_Doi_Generator_DoiGeneratorException $e) { + $message = 'could not generate DOI using generator class: ' . $e->getMessage(); + $this->defaultLog->err($message); + $this->doiLog->err($message); + throw new Opus_Doi_DoiException($message); + } + + return $doiValue; + } + + /** + * Markiert den Datensatz, der mit der lokalen DOI bei DataCite registriert ist, als inaktiv. + * + * @param $doc ID des Dokuments + */ + public function deleteMetadataForDoi($doc) + { + $dois = $doc->getIdentifierDoi(); + if (empty($dois)) { + $this->defaultLog->debug('document ' . $doc->getId() . ' does not provide a DOI - deregistration of DOI is not required'); + return; + } + + // wenn mehrere DOIs vorhanden, so wird nur die erste DOI betrachtet + $doi = $dois[0]; + if (!$doi->isLocalDoi()) { + // keine Behandlung von nicht-lokale DOIs erforderlich + $this->defaultLog->debug('document ' . $doc->getId() . ' does not provide a local DOI - deregistration of DOI is not required'); + return; + } + + $status = $doi->getStatus(); + if ($status != 'registered' && $status != 'verified') { + $this->defaultLog->debug('document ' . $doc->getId() . ' does not provide a registered local DOI - deregistration of DOI is not required'); + return; + } + + try { + $client = new \Opus\Doi\Client($this->config, $this->defaultLog); + $client->deleteMetadataForDoi($doi->getValue()); + $message = 'metadata deletion of DOI ' . $doi->getValue() . ' of document ' . $doc->getId() . ' was successful'; + $this->defaultLog->debug($message); + $this->doiLog->info($message); + // TODO sollte der Status der lokalen DOI auf "inactive" o.ä. gesetzt werden + } + catch (\Opus\Doi\ClientException $e) { + $message = 'an error occurred while deregistering DOI ' . $doi->getValue() . ' of document ' . $doc->getId() . ': ' . $e->getMessage(); + $this->doiLog->err($message); + $this->defaultLog->err($message); + // Exception wird nicht nach oben durchgereicht, weil bislang nur Aufruf aus Plugin erfolgt + } + } + + public function updateLandingPageUrlOfDoi($doiValue, $landingPageURL) + { + try { + $client = new \Opus\Doi\Client($this->config); + $client->updateUrlForDoi($doiValue, $landingPageURL); + } + catch (\Opus\Doi\ClientException $e) { + $message = 'could not update landing page URL of DOI ' . $doiValue . ' to ' . $landingPageURL; + $this->doiLog->err($message); + $this->defaultLog->err($message); + throw new Opus_Doi_DoiException($message); + } + } + + private function getLandingPageUrlOfDoc($doc) + { + if (is_null($this->landingPageUrl)) { + return null; + } + $result = $this->landingPageUrl . $doc->getId(); + return $result; + } +} diff --git a/library/Opus/Doi/DoiManagerStatus.php b/library/Opus/Doi/DoiManagerStatus.php new file mode 100644 index 000000000..e3bdb695a --- /dev/null +++ b/library/Opus/Doi/DoiManagerStatus.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiManagerStatus { + + private $docsWithDoiStatus = array(); + + public function isNoDocsToProcess() { + return empty($this->docsWithDoiStatus); + } + + public function addDocWithDoiStatus($docId, $statusMsg, $error = false) { + $this->docsWithDoiStatus[$docId] = + array( + 'msg' => $statusMsg, + 'error' => $error + ); + } + + public function getDocsWithDoiStatus() { + return $this->docsWithDoiStatus; + } +} \ No newline at end of file diff --git a/library/Opus/Doi/Generator/DefaultGenerator.php b/library/Opus/Doi/Generator/DefaultGenerator.php new file mode 100644 index 000000000..58f5bb376 --- /dev/null +++ b/library/Opus/Doi/Generator/DefaultGenerator.php @@ -0,0 +1,114 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_Generator_DefaultGenerator implements Opus_Doi_Generator_DoiGeneratorInterface { + + private $config; + + public function __construct() { + $this->config = Zend_Registry::get('Zend_Config'); + } + + /** + * Erzeugt auf Basis der Konfigurationseinstellungen eine DOI in der Form + * + * doi.prefix/doi.localPrefix-docId + * + * Ist doi.localPrefix in der Konfiguration nicht gesetzt, so wird die Form + * + * doi.prefix/docId + * + * verwendet. + * + * Schrägstrich / bzw. Bindestrich - werden im Bedarfsfall eingefügt, sofern in den Konfigurationswerten + * nicht angegeben. + * + * Der Konfigurationsparameter doi.suffixFormat wird von dieser DOI-Generierungsklasse NICHT berücksichtigt. + * Er ist fest auf {docId} gesetzt. + * + */ + public function generate($document) { + + if (!isset($this->config->doi->prefix) or $this->config->doi->prefix == '') { + throw new Opus_Doi_Generator_DoiGeneratorException('configuration setting doi.prefix is missing - DOI cannot be generated'); + } + $prefix = $this->config->doi->prefix; + // Schrägstrich als Trennzeichen, wenn Präfix nicht bereits einen Schrägstrich als Suffix besitzt + if (!$this->endsWithChar($prefix, '/')) { + $prefix .= '/'; + } + + if (isset($this->config->doi->localPrefix) and $this->config->doi->localPrefix != '') { + $prefix .= $this->config->doi->localPrefix; + + // DocID wird als Suffix mit Bindestrich an das Präfix angefügt (füge Bindestrich hinzu, wenn erforderlich) + if (!$this->endsWithChar($prefix, '-')) { + $prefix .= '-'; + } + } + + $generatedDOI = $prefix . $document->getId(); + return $generatedDOI; + } + + /** + * Liefert true zurück, wenn die übergebene DOI als lokale DOI zu betrachten ist. + * Im Falle der vorliegenden Implementierungsklasse muss eine lokale DOI folgenden + * Präfix haben: '{doi.prefix}/{doi.localPrefix}-' + * + */ + public function isLocal($doiValue) { + if (!isset($this->config->doi->prefix)) { + return false; + } + + $prefix = $this->config->doi->prefix; + if (!$this->endsWithChar($prefix, '/')) { + $prefix .= '/'; + } + + if (isset($this->config->doi->localPrefix)) { + $prefix .= $this->config->doi->localPrefix; + if (!$this->endsWithChar($prefix, '-')) { + $prefix .= '-'; + } + } + + $result = substr($doiValue, 0, strlen($prefix)) == $prefix; + return $result; + } + + private function endsWithChar($str, $suffix) { + return (substr($str, -1) == $suffix); + } +} \ No newline at end of file diff --git a/library/Opus/Doi/Generator/DoiGeneratorException.php b/library/Opus/Doi/Generator/DoiGeneratorException.php new file mode 100644 index 000000000..b323cb30a --- /dev/null +++ b/library/Opus/Doi/Generator/DoiGeneratorException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_Generator_DoiGeneratorException extends Opus_Doi_DoiException { + +} \ No newline at end of file diff --git a/library/Opus/Doi/Generator/DoiGeneratorFactory.php b/library/Opus/Doi/Generator/DoiGeneratorFactory.php new file mode 100644 index 000000000..98da2f6da --- /dev/null +++ b/library/Opus/Doi/Generator/DoiGeneratorFactory.php @@ -0,0 +1,64 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ +class Opus_Doi_Generator_DoiGeneratorFactory +{ + + public static function create() + { + $config = Zend_Registry::get('Zend_Config'); + + // versuche die Generierungsklasse für DOIs zu instanziieren + if (!isset($config->doi->generatorClass)) { + // Fehler: Name der Generierungsklasse für DOIs wurde nicht in Konfiguration definiert + throw new Opus_Doi_DoiException('mandatory configuration key doi.generatorClass is missing - check your configuration'); + } + + if ($config->doi->generatorClass == '') { + // Fehler: Name der Generierungsklasse für DOIs wurde nicht in Konfiguration definiert + throw new Opus_Doi_DoiException('mandatory configuration key doi.generatorClass is empty - check your configuration'); + } + + $generatorClassName = $config->doi->generatorClass; + + $classExists = Opus_Util_ClassLoaderHelper::classExists($generatorClassName); + + if (!$classExists) { + // Generierungsklasse für DOIs kann nicht gefunden oder geladen werden + throw new Opus_Doi_DoiException('DOI generator class ' . $generatorClassName . ' does not exist or is not instantiable - check configuration'); + } + + $generator = new $generatorClassName(); + + return $generator; + } +} diff --git a/library/Opus/Doi/Generator/DoiGeneratorInterface.php b/library/Opus/Doi/Generator/DoiGeneratorInterface.php new file mode 100644 index 000000000..42f6d583d --- /dev/null +++ b/library/Opus/Doi/Generator/DoiGeneratorInterface.php @@ -0,0 +1,40 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +interface Opus_Doi_Generator_DoiGeneratorInterface { + + public function generate($document); + + public function isLocal($doiValue); + +} \ No newline at end of file diff --git a/library/Opus/Doi/RegistrationException.php b/library/Opus/Doi/RegistrationException.php new file mode 100644 index 000000000..93e2a7fcc --- /dev/null +++ b/library/Opus/Doi/RegistrationException.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_RegistrationException extends Opus_Doi_DoiException { + + // DOI, die registriert bzw. geprüft werden sollte und die zum Fehler beim Aufruf der DataCite-API führte + private $doi; + + public function getDoi() { + return $this->doi; + } + + public function setDoi($doi) { + $this->doi = $doi; + } + +} \ No newline at end of file diff --git a/library/Opus/Doi/datacite.xslt b/library/Opus/Doi/datacite.xslt new file mode 100644 index 000000000..5c1ca4b11 --- /dev/null +++ b/library/Opus/Doi/datacite.xslt @@ -0,0 +1,496 @@ + + + + + + + + + + + + + + + + http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4/metadata.xsd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DOI + + + + + + + + + + ISSN + + + IsPartOf + + + + + + + + + + ISBN + + + + + + + + + URN + + + + + + + + + + + de + + + + + en + + + + + + + + + + + + + de + + + + + en + + + + + Subtitle + + + + + + + + + + de + + + Abstract + + + + + + en + + + Abstract + + + + + + + + + + + SeriesInformation + + + , + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + https://orcid.org/ + + + ORCID + + + + + + + + + + + + + + + + + + + + + Editor + + + + , + + + + + + https://orcid.org/ + + + ORCID + + + + + + + + + + + + Created + + + - + + - + + + + + + + + + + + + + + + de + + + en + + + + + + + + Text + + + + Journal Article + + + Bachelor Thesis + + + Book + + + Book Chapter + + + Conference Paper + + + Magazine Article + + + Diploma Thesis + + + Dissertation + + + Master Thesis + + + Periodical + + + Journal Issue + + + Report + + + Book Review + + + Study Thesis + + + Working Paper + + + Text + + + + + + + + + + + + + + + + + + + + + + de + + + https://dewey.info/ + + + dewey + + + + + + + + + + + + + + + + + + + + + KB + + + + + + + pages + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-contributorType-v4.xsd b/library/Opus/Doi/include/datacite-contributorType-v4.xsd new file mode 100644 index 000000000..4de481f7c --- /dev/null +++ b/library/Opus/Doi/include/datacite-contributorType-v4.xsd @@ -0,0 +1,35 @@ + + + + + + The type of contributor of the resource. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-dateType-v4.xsd b/library/Opus/Doi/include/datacite-dateType-v4.xsd new file mode 100644 index 000000000..dea58ba77 --- /dev/null +++ b/library/Opus/Doi/include/datacite-dateType-v4.xsd @@ -0,0 +1,21 @@ + + + + + + The type of date. Use RKMS‐ISO8601 standard for depicting date ranges.To indicate the end of an embargo period, use Available. To indicate the start of an embargo period, use Submitted or Accepted, as appropriate. + + + + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-descriptionType-v4.xsd b/library/Opus/Doi/include/datacite-descriptionType-v4.xsd new file mode 100644 index 000000000..bdbe47f5d --- /dev/null +++ b/library/Opus/Doi/include/datacite-descriptionType-v4.xsd @@ -0,0 +1,19 @@ + + + + + + The type of the description. + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-funderIdentifierType-v4.xsd b/library/Opus/Doi/include/datacite-funderIdentifierType-v4.xsd new file mode 100644 index 000000000..a23c9a7f9 --- /dev/null +++ b/library/Opus/Doi/include/datacite-funderIdentifierType-v4.xsd @@ -0,0 +1,15 @@ + + + + + + The type of the funderIdentifier. + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-relatedIdentifierType-v4.xsd b/library/Opus/Doi/include/datacite-relatedIdentifierType-v4.xsd new file mode 100644 index 000000000..8e842a530 --- /dev/null +++ b/library/Opus/Doi/include/datacite-relatedIdentifierType-v4.xsd @@ -0,0 +1,32 @@ + + + + + + The type of the RelatedIdentifier. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-relationType-v4.xsd b/library/Opus/Doi/include/datacite-relationType-v4.xsd new file mode 100644 index 000000000..4df4c6e9f --- /dev/null +++ b/library/Opus/Doi/include/datacite-relationType-v4.xsd @@ -0,0 +1,39 @@ + + + + + + Description of the relationship of the resource being registered (A) and the related resource (B). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-resourceType-v4.xsd b/library/Opus/Doi/include/datacite-resourceType-v4.xsd new file mode 100644 index 000000000..e51a551f3 --- /dev/null +++ b/library/Opus/Doi/include/datacite-resourceType-v4.xsd @@ -0,0 +1,26 @@ + + + + + + The general type of a resource. + + + + + + + + + + + + + + + + + + + diff --git a/library/Opus/Doi/include/datacite-titleType-v4.xsd b/library/Opus/Doi/include/datacite-titleType-v4.xsd new file mode 100644 index 000000000..007e1d013 --- /dev/null +++ b/library/Opus/Doi/include/datacite-titleType-v4.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/library/Opus/Doi/metadata.xsd b/library/Opus/Doi/metadata.xsd new file mode 100644 index 000000000..cafc0d4c2 --- /dev/null +++ b/library/Opus/Doi/metadata.xsd @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + Root element of a single record. This wrapper element is for XML implementation only and is not defined in the DataCite DOI standard. + Note: This is the case for all wrapper elements within this schema. + No content in this wrapper element. + + + + + + + A persistent identifier that identifies a resource. + Currently, only DOI is allowed. + + + + + + + + + + + + + + + The main researchers involved working on the data, or the authors of the publication in priority order. May be a corporate/institutional or personal name. + Format: Family, Given. + Personal names can be further specified using givenName and familyName. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A name or title by which a resource is known. + + + + + + + + + + + + + + + + The name of the entity that holds, archives, publishes prints, distributes, releases, issues, or produces the resource. This property will be used to formulate the citation, so consider the prominence of the role. + In the case of datasets, "publish" is understood to mean making the data available to the community of researchers. + + + + + + + + Year when the data is made publicly available. If an embargo period has been in effect, use the date when the embargo period ends. + In the case of datasets, "publish" is understood to mean making the data available on a specific date to the community of researchers. If there is no standard publication year value, use the date that would be preferred from a citation perspective. + YYYY + + + + + + + + The type of a resource. You may enter an additional free text description. + The format is open, but the preferred format is a single term of some detail so that a pair can be formed with the sub-property. + + + + + + + + + + + + + + + + Subject, keywords, classification codes, or key phrases describing the resource. + + + + + + + + + + + + + + + + + + + + + The institution or person responsible for collecting, creating, or otherwise contributing to the developement of the dataset. + The personal name format should be: Family, Given. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Different dates relevant to the work. + YYYY,YYYY-MM-DD, YYYY-MM-DDThh:mm:ssTZD or any other format or level of granularity described in W3CDTF. Use RKMS-ISO8601 standard for depicting date ranges. + + + + + + + + + + + + + + + Primary language of the resource. Allowed values are taken from IETF BCP 47, ISO 639-1 language codes. + + + + + + + + An identifier or identifiers other than the primary Identifier applied to the resource being registered. This may be any alphanumeric string which is unique within its domain of issue. May be used for local identifiers. AlternateIdentifier should be used for another identifier of the same instance (same location, same file). + + + + + + + + + + + + + + + + + + Identifiers of related resources. Use this property to indicate subsets of properties, as appropriate. + + + + + + + + + + + + + + + + + + + + + + Unstructures size information about the resource. + + + + + + + + + + + Technical format of the resource. + Use file extension or MIME type where possible. + + + + + + + + Version number of the resource. If the primary resource has changed the version number increases. + Register a new identifier for a major version change. Individual stewards need to determine which are major vs. minor versions. May be used in conjunction with properties 11 and 12 (AlternateIdentifier and RelatedIdentifier) to indicate various information updates. May be used in conjunction with property 17 (Description) to indicate the nature and file/record range of version. + + + + + + + + Any rights information for this resource. Provide a rights management statement for the resource or reference a service providing such information. Include embargo information if applicable. +Use the complete title of a license and include version information if applicable. + + + + + + + + + + + + + + + + + + All additional information that does not fit in any of the other categories. May be used for technical information. It is a best practice to supply a description. + + + + + + + + + + + + + + + + + + + + + + + + + + + Spatial region or named place where the data was gathered or about which the data is focused. + + + + + A point contains a single latitude-longitude pair, separated by whitespace. + + + + + A box contains two white space separated latitude-longitude pairs, with each pair separated by whitespace. The first pair is the lower corner, the second is the upper corner. + + + + + A drawn polygon area, defined by a set of points and lines connecting the points in a closed chain. + + + + + + + + + + + + + + + + + + + Information about financial support (funding) for the resource being registered. + + + + + + Name of the funding provider. + + + + + + + + Uniquely identifies a funding entity, according to various types. + + + + + + + + + + + + The code assigned by the funder to a sponsored award (grant). + + + + + + + + + + + + The human readable title of the award (grant). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/Opus/Enrichment.php b/library/Opus/Enrichment.php index 2908805d7..e180c450a 100644 --- a/library/Opus/Enrichment.php +++ b/library/Opus/Enrichment.php @@ -30,6 +30,7 @@ * @author Pascal-Nicolas Becker * @author Gunar Maiwald * @author Jens Schwidder + * @author Sascha Szott * @copyright Copyright (c) 2008-2018, OPUS 4 development team * @license http://www.gnu.org/licenses/gpl.html General Public License */ @@ -73,7 +74,10 @@ class Opus_Enrichment extends Opus_Model_Dependent_Abstract protected function _init() { $key = new Opus_Model_Field('KeyName'); - $key->setMandatory(true)->setValidator(new Zend_Validate_NotEmpty()); + $key->setMandatory(true) + ->setValidator(new Zend_Validate_NotEmpty()) + ->setSelection(true) + ->setDefault(Opus_EnrichmentKey::getAll()); $value = new Opus_Model_Field('Value'); $value->setMandatory(true) @@ -82,4 +86,5 @@ protected function _init() $this->addField($key); $this->addField($value); } + } diff --git a/library/Opus/Identifier.php b/library/Opus/Identifier.php index 893f20abc..e834a17c6 100644 --- a/library/Opus/Identifier.php +++ b/library/Opus/Identifier.php @@ -28,9 +28,8 @@ * @category Framework * @package Opus_Model * @author Felix Ostrowski - * @copyright Copyright (c) 2008, OPUS 4 development team + * @copyright Copyright (c) 2008-2018, OPUS 4 development team * @license http://www.gnu.org/licenses/gpl.html General Public License - * @version $Id$ */ /** @@ -39,6 +38,8 @@ * @category Framework * @package Opus_Model * @uses Opus_Model_Dependent_Abstract + * + * TODO find way to remove DOI and URN functions to separate classes */ class Opus_Identifier extends Opus_Model_Dependent_Abstract { /** @@ -93,30 +94,243 @@ protected function _init() { ) ); $this->addField($type); + + // zwei Felder, die ausschließlich für Identifier vom Typ DOI genutzt werden + $value = new Opus_Model_Field('Status'); + $value->setMandatory(false) + ->setDefault(array( + 'registered' => 'registered', + 'verified' => 'verified') + ); + $this->addField($value); + + $value = new Opus_Model_Field('RegistrationTs'); + $value->setMandatory(false); + $this->addField($value); } - protected function _preStore() { + protected function _preStore() + { $type = $this->getType(); $value = $this->getValue(); - if (isset($type) and isset($value) and $type === 'urn') { - $finder = new Opus_DocumentFinder(); - $docIds = $finder->setIdentifierTypeValue('urn', $value)->ids(); - - $errorMsg = "urn collision (documents " . implode(",", $docIds) . ")"; - if ($this->isNewRecord() and count($docIds) > 0) { - throw new Opus_Identifier_UrnAlreadyExistsException($errorMsg); + if (isset($type) and isset($value)) { + switch ($type) { + case 'urn': + $this->checkUrnCollision($value); + break; + case 'doi': + if ($this->checkDoiCollision()) { + throw new Opus_Identifier_DoiAlreadyExistsException('could not save DOI with value ' . $value . ' since it already exists in your instance'); + } + break; } + } - if (count($docIds) > 1) { - throw new Opus_Identifier_UrnAlreadyExistsException($errorMsg); + return parent::_preStore(); + } + + /** + * Prüfe, dass die in $value gespeicherte URN nicht bereits in der Datenbank existiert. + * + * Wird als zweites Argument eine ID eines OPUS-Dokuments übergeben, so wird das zugehörige Dokument bei der + * Eindeutigkeitsüberprüfung nicht berücksichtigt. + * + * @param $value + * @throws Opus_Identifier_UrnAlreadyExistsException + * @throws Opus_Model_Exception + */ + private function checkUrnCollision($value, $docId = null) + { + $log = Zend_Registry::get('Zend_Log'); + $log->debug('check URN collision for URN ' . $value); + + $finder = new Opus_DocumentFinder(); + $finder->setIdentifierTypeValue('urn', $value); + $docIds = $finder->ids(); + // remove $docId of current document from $docIds + + if (!is_null($docId)) { + if (($key = array_search($docId, $docIds)) !== false) { + unset($docIds[$key]); } + } + $this->checkIdCollision('urn', $docIds); + + $log->debug('no URN collision was found for URN ' . $value); + } + + /** + * Pürft, ob die vorliegende URN innerhalb der OPUS-Instanz nur einmal vorkommt. + * Optional kann eine ID eines OPUS-Dokuments übergeben werden. Das zugehörige Dokument wird dann bei der + * Eindeutigkeitsüberprüfung nicht betrachtet. + * + * @param $docId optionale ID eines OPUS-Dokuments + * @return bool + */ + public function isUrnUnique($docId = null) { + try { + $this->checkUrnCollision($this->getValue(), $docId); + return true; + } + catch (Opus_Identifier_UrnAlreadyExistsException $e) { + // ignore exception + } + return false; + } + + /** + * Prüfe, ob die vorliegende DOI nicht bereits in der Datenbank exitsiert. Die Prüfung erstreckt sich hierbei aber + * nur auf lokale DOIs. + * + * Im Falle einer DOI-Kollision (d.h. der DOI-Wert existiert bereits in der Datenbank) liefert die Methode true + * zurück; andernfalls false. + * + */ + private function checkDoiCollision() { + if (!$this->isLocalDoi()) { + return; + } - if (count($docIds) == 1 and !in_array($this->getParentId(), $docIds)) { - throw new Opus_Identifier_UrnAlreadyExistsException($errorMsg); + $log = Zend_Registry::get('Zend_Log'); + $log->debug('check collision for local DOI ' . $this->getValue()); + + if ($this->isDoiUnique()) { + $log->debug('no DOI collision was found for DOI ' . $this->getValue()); + return false; + } + + $log->debug('found a DOI collision for DOI ' . $this->getValue()); + return true; + } + + /** + * Prüft, ob die vorliegende DOI einmalig innerhalb der OPUS-Instanz ist. Es werden bei der Eindeutigkeitsprüfung + * alle DOIs (auch von Dokumenten, die nicht im Zustand published vorliegen) betrachtet. + * + * Im Falle der Eindeutigkeit der DOI innerhalb der OPUS-Instanz liefert die Methode true zurück, andernfalls false. + * + * Wird eine ID eines Opus-Dokuments übergeben, so wird das zugehörige Dokument bei der Eindeutigkeitsprüfung nicht + * betrachtet. + * + */ + public function isDoiUnique($docId = null) + { + $finder = new Opus_DocumentFinder(); + $finder->setIdentifierTypeValue('doi', $this->getValue()); + $docIds = $finder->ids(); + // remove $docId from $docIds + if (!is_null($docId)) { + if (($key = array_search($docId, $docIds)) !== false) { + unset($docIds[$key]); } + + $generator = Opus_Doi_Generator_DoiGeneratorFactory::create(); + $isLocalDoi = $generator->isLocal($this->getValue()); + return $isLocalDoi; } - return parent::_preStore(); + try { + $this->checkIdCollision('doi', $docIds); + } + catch (Exception $e) { + return false; + } + return true; } + /** + * Prüfe, ob es sich bei dem vorliegenden Identifier um lokale DOI handelt: eine lokale DOI hat das konfigurierte + * DOI-Präfix und das konfigurierte Lokal-Präfix, sofern es konfiguriert wurden. + * + * Wird eine DOI-Generierungsklasse verwendet, so wird auf die von der Klasse zu implementierende Prüfmethode + * zurückgegriffen. + * + * Im Erfolgsfall gibt die Methode true zurück; sonst false. + * + * @return bool + * @throws Zend_Exception + */ + public function isLocalDoi() + { + + $generator = null; + try { + $generator = Opus_Doi_Generator_DoiGeneratorFactory::create(); + } + catch (Opus_Doi_DoiException $e) { + // ignore exception + } + + // wenn DOI-Generierungsklasse in Konfiguration angegeben wurde, dann nutze die von der Klasse + // implementierte Methode isLocal für die Prüfung, ob eine lokale DOI vorliegt + if (!is_null($generator)) { + $isLocalDoi = $generator->isLocal($this->getValue()); + return $isLocalDoi; + } + + $config = Zend_Registry::get('Zend_Config'); + + // es wurde keine DOI-Generierungsklasse angegeben bzw. die Klasse kann nicht gefunden werden. + // wir prüfen lediglich, ob die DOI mit dem konfigurierten Präfix beginnt und nach dem Schrägstrich + // das ggf. konfigurierte lokale Präfix folgt + if (!isset($config->doi->prefix) || $config->doi->prefix == '') { + // DOI-Präfix für nicht definiert: daher können wir nicht entscheiden, ob eine DOI lokal ist + return false; + } + + $prefix = $config->doi->prefix; + if (substr($prefix, -1) != '/') { + $prefix .= '/'; + } + + if (isset($config->doi->localPrefix) && $config->doi->localPrefix != '') { + $prefix .= $config->doi->localPrefix; + } + + // beginnt die DOI mit $prefix + if (substr($this->getValue(), 0, strlen($prefix)) !== $prefix) { + return false; + } + + return true; // lokale DOI gefunden + } + + /** + * Prüft, dass in der DOI nur die von DataCite erlaubten Werte enthalten sind. + */ + public function isValidDoi() + { + $value = $this->getValue(); + $containsInvalidChar = preg_match('/[^0-9a-zA-Z\-\.\_\+\:\/]/', $value); + return $containsInvalidChar !== 1; + } + + private function checkIdCollision($type, $docIds) + { + $errorMsg = "$type collision (documents " . implode(",", $docIds) . ")"; + switch ($type) { + case 'urn': + $exception = new Opus_Identifier_UrnAlreadyExistsException($errorMsg); + break; + + case 'doi': + $exception = new Opus_Identifier_DoiAlreadyExistsException($errorMsg); + break; + + default: + $exception = new Opus_Model_Exception($errorMsg); + } + + if ($this->isNewRecord() and !empty($docIds)) { + throw $exception; + } + + if (count($docIds) > 1) { + throw $exception; + } + + if (count($docIds) == 1 and !is_null($this->getParentId()) and !in_array($this->getParentId(), $docIds)) { + throw $exception; + } + } } diff --git a/library/Opus/Identifier/DoiAlreadyExistsException.php b/library/Opus/Identifier/DoiAlreadyExistsException.php new file mode 100644 index 000000000..bb18cc785 --- /dev/null +++ b/library/Opus/Identifier/DoiAlreadyExistsException.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Identifier_DoiAlreadyExistsException extends Opus_Model_Exception +{ +} diff --git a/library/Opus/Mail/SendMail.php b/library/Opus/Mail/SendMail.php index 4a1dba41b..22680c899 100644 --- a/library/Opus/Mail/SendMail.php +++ b/library/Opus/Mail/SendMail.php @@ -53,6 +53,21 @@ class Opus_Mail_SendMail { public function __construct() { $config = Zend_Registry::get('Zend_Config'); if (isset($config, $config->mail->opus)) { + + if (isset($config->mail->opus->transport) && $config->mail->opus->transport == 'file') { + // erlaubt das Speichern von E-Mails in Dateien, die im Verzeichnis mail.opus.file abgelegt werden + $options = array(); + if (isset($config->mail->opus->file)) { + $options['path'] = $config->mail->opus->file; + } + $callback = function () { + return 'opus-mail_' . time() . '_' . mt_rand() . '.tmp'; + }; + $options['callback'] = $callback; + $this->_transport = new Zend_Mail_Transport_File($options); + return; + } + $this->_transport = new Opus_Mail_Transport($config->mail->opus); return; } diff --git a/library/Opus/Search/Config.php b/library/Opus/Search/Config.php index feeab88a4..3095f540e 100644 --- a/library/Opus/Search/Config.php +++ b/library/Opus/Search/Config.php @@ -385,15 +385,13 @@ public static function getFacetLimits( $facetSetName = null, $serviceDomain = nu } } - // if facet-name is 'year_inverted', the facet values have to be sorted vice versa // however, the facet-name should be 'year' (reset in framework ResponseRenderer::getFacets()) if ( array_key_exists( 'year_inverted', $set ) ) { $set['year'] = $set['year_inverted']; - unset( $set['year_inverted'] ); + $set['year_inverted']; // leave set for query to solr 'year_inverted' facet } - return $set; } diff --git a/library/Opus/Util/ClassLoaderHelper.php b/library/Opus/Util/ClassLoaderHelper.php new file mode 100644 index 000000000..5292dcc25 --- /dev/null +++ b/library/Opus/Util/ClassLoaderHelper.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Util_ClassLoaderHelper { + + public static function classExists($className) { + // Anpassung des Zend-Autoloaders erforderlich, damit keine PHP Warning erzeugt wird, wenn Generator-Klasse + // nicht existiert: PHPUnit erzeugt sonst aus PHP Warning (wenn Klasse nicht gefunden wird) eine Exception, weil + // in Konfiguration convertWarningsToExceptions="true gesetzt -> das führt zu verändertem Exception-Verhalten + $autoloader = Zend_Loader_Autoloader::getInstance(); + + // Default-Wert für späteres Zurücksetzen speichern + $suppressNotFoundWarnings = $autoloader->suppressNotFoundWarnings(); + + $autoloader->suppressNotFoundWarnings(true); + $classExists = class_exists($className); + + // Wiederherstellen des Default-Wertes + $autoloader->suppressNotFoundWarnings($suppressNotFoundWarnings); + + return $classExists; + } +} diff --git a/library/Opus/Version.php b/library/Opus/Version.php index 912f3cfd7..5cbfdd040 100644 --- a/library/Opus/Version.php +++ b/library/Opus/Version.php @@ -45,12 +45,12 @@ class Opus_Version /** * Opus Framework version identification - see compareVersion() */ - const VERSION = '4.5'; + const VERSION = '4.6.3'; /** * Version of database schema. */ - const SCHEMA_VERSION = '8'; + const SCHEMA_VERSION = '9'; /** * Compare the specified Opus Framework version string $version diff --git a/tests/Opus/Db/Adapter/Pdo/Mysqlutf8Test.php b/tests/Opus/Db/Adapter/Pdo/Mysqlutf8Test.php index 446bc5b8a..53ec87c6d 100644 --- a/tests/Opus/Db/Adapter/Pdo/Mysqlutf8Test.php +++ b/tests/Opus/Db/Adapter/Pdo/Mysqlutf8Test.php @@ -81,7 +81,9 @@ public function setUp() { public function tearDown() { // Close connection for clean transaction state. $dba = Zend_Db_Table::getDefaultAdapter(); - $dba->closeConnection(); + if (!is_null($dba)) { + $dba->closeConnection(); + } // Restore existing adapter Zend_Db_Table::setDefaultAdapter($this->dba_backup); diff --git a/tests/Opus/Document/Plugin/IdentifierDoiTest.php b/tests/Opus/Document/Plugin/IdentifierDoiTest.php new file mode 100644 index 000000000..a06066314 --- /dev/null +++ b/tests/Opus/Document/Plugin/IdentifierDoiTest.php @@ -0,0 +1,325 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Document_Plugin_IdentifierDoiTest extends TestCase { + + const ENRICHMENT_KEY_NAME = 'opus.doi.autoCreate'; + + private function setupEnrichmentKey() { + $enrichmentKey = new Opus_EnrichmentKey(); + $enrichmentKey->setName(self::ENRICHMENT_KEY_NAME); + $enrichmentKey->store(); + } + + private function adaptDoiConfiguration($doiConfig) { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge(new Zend_Config(array('doi' => $doiConfig)))); + } + + private function createMinimalDocument($enrichmentValue = null) { + $model = new Opus_Document(); + $model->setServerState('published'); + + if (!is_null($enrichmentValue)) { + $this->setupEnrichmentKey(); + + $enrichment = new Opus_Enrichment(); + $enrichment->setKeyName(self::ENRICHMENT_KEY_NAME); + $enrichment->setValue($enrichmentValue); + + $enrichments = array(); + $enrichments[] = $enrichment; + $model->setEnrichment($enrichments); + } + + $docId = $model->store(); + return $docId; + } + + /** + * Überprüft, dass Konfigurationseinstellung doi.autoCreate korrekt angewendet wird. + */ + public function testDisabledAutoCreationOfDoiInConfig() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => 0 + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument(); + + $this->assertNoGeneratedDoi($docId); + } + + /** + * Überprüft, dass Konfigurationseinstellung doi.autoCreate korrekt angewendet wird. + */ + public function testDisabledAutoCreationOfDoiInConfigAlt() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => false + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument(); + + $this->assertNoGeneratedDoi($docId); + } + + /** + * Überprüft, dass Enrichment opus.doi.autoCreate die Konfigurationseinstellung überschreibt. + */ + public function testDisabledAutoCreationOfDoi() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => 1 + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument('false'); + + $this->assertNoGeneratedDoi($docId, 1); + } + + /** + * Überprüft, dass Konfigurationseinstellung doi.autoCreate korrekt angewendet wird. + */ + public function testEnabledAutoCreationOfDoiInConfig() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => 1 + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument(); + + $this->assertGeneratedDoi($docId); + } + + /** + * Überprüft, dass Konfigurationseinstellung doi.autoCreate korrekt angewendet wird. + */ + public function testEnabledAutoCreationOfDoiInConfigAlt() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => true + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument(); + + $this->assertGeneratedDoi($docId); + } + + /** + * Überprüft, dass Enrichment opus.doi.autoCreate die Konfigurationseinstellung überschreibt. + */ + public function testEnabledAutoCreationOfDoi() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => 0 + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument('true'); + + $this->assertGeneratedDoi($docId, 1); + } + + private function assertNoGeneratedDoi($docId, $numOfEnrichments = 0) { + $model = new Opus_Document($docId); + $this->assertEquals(0, count($model->getIdentifier())); + $this->assertEquals(0, count($model->getIdentifierDoi())); + $this->assertEquals($numOfEnrichments, count($model->getEnrichment())); + + $dois = $model->getIdentifier(); + $this->assertEmpty($dois); + } + + private function assertGeneratedDoi($docId, $numOfEnrichments = 0) { + $model = new Opus_Document($docId); + $this->assertEquals(1, count($model->getIdentifier())); + $this->assertEquals(1, count($model->getIdentifierDoi())); + $this->assertEquals($numOfEnrichments, count($model->getEnrichment())); + + $dois = $model->getIdentifier(); + $this->assertCount(1, $dois); + + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.000/opustest-' . $docId, $doi->getValue()); + } + + public function testSkipGenerationIfDoiAlreadyExists() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'registerAtPublish' => 0, + 'autoCreate' => 1 + ); + $this->adaptDoiConfiguration($doiConfig); + + $doc = new Opus_Document(); + $doc->setServerState('unpublished'); + + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue('1234'); + $dois = array(); + $dois[] = $doi; + $doc->setIdentifier($dois); + + $docId = $doc->store(); + + $doc = new Opus_Document($docId); + $doc->setServerState('published'); + $doc->store(); + + $doc = new Opus_Document($docId); + $this->assertCount(1, $doc->getIdentifier()); + $this->assertCount(1, $doc->getIdentifierDoi()); + + $dois = $doc->getIdentifier(); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('1234', $doi->getValue()); + $this->assertNull($doi->getStatus()); + } + + /** + * Aktuell wird beim Löschen eines Dokuments mit einer lokalen DOI + * lediglich der Metadatensatz bei DataCite als "inactive" markiert. + * Der Status der lokalen DOI wird nicht verändert. + * + */ + public function testHandleDeleteEvent() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'autoCreate' => 1, + 'doi.registration.datacite.serviceUrl' => 'localhost' + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createMinimalDocument(); + + $doc = new Opus_Document($docId); + // simuliere eine erfolgreiche DOI-Registrierung durch Setzen des Status auf registered + $dois = $doc->getIdentifier(); + $doi = $dois[0]; + $doi->setStatus('registered'); + $doc->setIdentifier($dois); + $doc->store(); + + $doc = new Opus_Document($docId); + $doc->delete(); + + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $this->assertCount(1, $dois); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.000/opustest-' . $docId , $doi->getValue()); + $this->assertEquals('registered', $doi->getStatus()); + } + + public function testDoiGenerationWithMissingGenerationClass() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_MissingGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'autoCreate' => 1, + ); + $this->adaptDoiConfiguration($doiConfig); + + $docId = $this->createMinimalDocument(); + + $doc = new Opus_Document($docId); + $identifiers = $doc->getIdentifier(); + $this->assertEmpty($identifiers); + } + + public function testDoiRegistration() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'autoCreate' => 1, + 'registerAtPublish' => 1, + 'doi.registration.datacite.serviceUrl' => 'localhost' + ); + $this->adaptDoiConfiguration($doiConfig); + + $docId = $this->createMinimalDocument(); + + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $this->assertCount(1, $dois); + + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.000/opustest-' . $docId, $doi->getValue()); + $this->assertNull($doi->getStatus()); + } + + public function testDoiRegistrationWithMissingLocalDoi() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.000/', + 'localPrefix' => 'opustest', + 'autoCreate' => 0, + 'registerAtPublish' => 1, + 'doi.registration.datacite.serviceUrl' => 'localhost' + ); + $this->adaptDoiConfiguration($doiConfig); + + $docId = $this->createMinimalDocument(); + + $doc = new Opus_Document($docId); + $this->assertEmpty($doc->getIdentifier()); + } + +} diff --git a/tests/Opus/DocumentTest.php b/tests/Opus/DocumentTest.php index 8d2f5e15e..ed0f75cd2 100644 --- a/tests/Opus/DocumentTest.php +++ b/tests/Opus/DocumentTest.php @@ -2582,6 +2582,8 @@ public function testRemoveAllPersons() */ public function testGetIdentifierDoiProducesDifferentResultThanGetIdentifier() { + $this->markTestSkipped('TODO OPUSVIER-3860 Regression test - not fixed yet'); + $doc = new Opus_Document(); $doc->store(); $id = new Opus_Identifier(); diff --git a/tests/Opus/Doi/DataCiteXmlGeneratorTest.php b/tests/Opus/Doi/DataCiteXmlGeneratorTest.php new file mode 100644 index 000000000..b4ba2f59f --- /dev/null +++ b/tests/Opus/Doi/DataCiteXmlGeneratorTest.php @@ -0,0 +1,83 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DataCiteXmlGeneratorTest extends TestCase +{ + + public function testGenerateMissingFields() + { + $doc = new Opus_Document(); + $doc->store(); + + $generator = new Opus_Doi_DataCiteXmlGenerator(); + $this->setExpectedException('Opus_Doi_DataCiteXmlGenerationException'); + $generator->getXml($doc); + } + + public function testGenerateRequiredFields() + { + $doc = new Opus_Document(); + $this->addRequiredPropsToDoc($doc); + + $generator = new Opus_Doi_DataCiteXmlGenerator(); + $result = $generator->getXml($doc); + + $this->assertTrue(is_string($result)); + } + + private function addRequiredPropsToDoc($doc) + { + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue('10.2345/opustest-' . $doc->getId()); + $doc->setIdentifier(array($doi)); + + $doc->setCompletedYear(2018); + $doc->setServerState('unpublished'); + $doc->setType('book'); + $doc->setPublisherName('ACME corp'); + + $author = new Opus_Person(); + $author->setLastName('Doe'); + $author->setFirstName('John'); + $doc->addPersonAuthor($author); + + $title = new Opus_Title(); + $title->setType('main'); + $title->setValue('Document without meaningful title'); + $title->setLanguage('deu'); + $doc->addTitleMain($title); + + $doc->store(); + } +} diff --git a/tests/Opus/Doi/DoiMailNotificationTest.php b/tests/Opus/Doi/DoiMailNotificationTest.php new file mode 100644 index 000000000..5c56127f0 --- /dev/null +++ b/tests/Opus/Doi/DoiMailNotificationTest.php @@ -0,0 +1,171 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiMailNotificationTest extends TestCase { + + public function testConstructMissingConfig() { + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertFalse($notification->isEnabled()); + } + + public function testConstructPartialConfig1() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => false, + 'notificationEmail' => array('doe@localhost') + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertFalse($notification->isEnabled()); + } + + public function testConstructPartialConfig2() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => '0', + 'notificationEmail' => array('doe@localhost') + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertFalse($notification->isEnabled()); + } + + public function testConstructPartialConfig3() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => true, + 'notificationEmail' => array('doe@localhost') + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertTrue($notification->isEnabled()); + } + + public function testConstructPartialConfig4() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => '1', + 'notificationEmail' => array('doe@localhost') + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertTrue($notification->isEnabled()); + } + + public function testConstructPartialConfig5() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => true + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertFalse($notification->isEnabled()); + } + + public function testConstructPartialConfig6() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => '1' + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $this->assertFalse($notification->isEnabled()); + } + + public function testSendMailEmpty() { + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => true, + 'notificationEmail' => array('doe@localhost') + ) + ); + $notification = new Opus_Doi_DoiMailNotification(); + $notification->sendRegistrationEmail(); + } + + public function testSendMailSingle() { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4')))); + + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => true, + 'notificationEmail' => array('doe@localhost') + ) + ); + + $docId = $this->createTestDocWithDoi('10.2345/opustest-999'); + + $notification = new Opus_Doi_DoiMailNotification(); + $notification->addNotification($docId, $this->getDoi($docId), 'error'); + $notification->sendRegistrationEmail(); + } + + public function testSendMailMultiple() { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4')))); + + $this->adaptDoiConfiguration(array( + 'notificationEmailEnabled' => true, + 'notificationEmail' => array('doe@localhost') + ) + ); + + $doc1Id = $this->createTestDocWithDoi('10.2345/opustest-888'); + $doc2Id = $this->createTestDocWithDoi('10.2345/opustest-999'); + + $notification = new Opus_Doi_DoiMailNotification(); + $notification->addNotification('888', $this->getDoi($doc1Id), 'error'); + $notification->addNotification('999', $this->getDoi($doc2Id)); + $notification->sendRegistrationEmail(); + } + + private function adaptDoiConfiguration($doiConfig) { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge(new Zend_Config(array('doi' => $doiConfig)))); + } + + private function createTestDocWithDoi($doiValue) { + $doc = new Opus_Document(); + + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue($doiValue); + $doc->setIdentifier(array($doi)); + + $docId = $doc->store(); + + return $docId; + } + + private function getDoi($docId) { + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + return $dois[0]; + } + +} \ No newline at end of file diff --git a/tests/Opus/Doi/DoiManagerTest.php b/tests/Opus/Doi/DoiManagerTest.php new file mode 100644 index 000000000..8c300a59a --- /dev/null +++ b/tests/Opus/Doi/DoiManagerTest.php @@ -0,0 +1,774 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_DoiManagerTest extends TestCase +{ + + public function testConstructor() { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4')))); + $this->adaptDoiConfiguration(array('prefix' => '')); + $doiManager = new Opus_Doi_DoiManager(); + $this->assertNotNull($doiManager); + } + + public function testConstructorAlt() { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4/')))); + $this->adaptDoiConfiguration(array('prefix' => '')); + $doiManager = new Opus_Doi_DoiManager(); + $this->assertNotNull($doiManager); + } + + public function testRegisterMissingArg() { + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->register(null); + } + + public function testRegisterInvalidArg() { + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->register('999'); + } + + public function testRegisterDocIdAsdArg() { + $doc = new Opus_Document(); + $docId = $doc->store(); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register($docId); + $this->assertNull($doi); + } + + public function testRegisterDocWithoutDoi() { + $doc = new Opus_Document(); + $docId = $doc->store(); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId)); + $this->assertNull($doi); + } + + public function testRegisterDocWithExternalDoi() { + $this->adaptDoiConfiguration(array('prefix' => '10.3456/')); + $docId = $this->createTestDocWithDoi('23.4567/'); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId)); + $this->assertNull($doi); + } + + public function testRegisterDocWithExternalDoiAndMissingConfig() { + $docId = $this->createTestDocWithDoi('23.4567/'); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId)); + $this->assertNull($doi); + } + + public function testRegisterDocWithLocalRegisteredDoi() { + $this->adaptDoiConfiguration(array('prefix' => '10.3456/')); + $docId = $this->createTestDocWithDoi('10.3456/', 'registered'); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId)); + $this->assertNull($doi); + } + + public function testRegisterDocWithLocalNonUniqueDoi() { + $this->adaptDoiConfiguration(array('prefix' => '10.3456/')); + $doc1Id = $this->createTestDocWithDoi('10.3456/'); + + $doc2Id = $this->createTestDocWithDoi('10.3456/'); + $doc2 = new Opus_Document($doc2Id); + $identifiers = $doc2->getIdentifier(); + $doi = $identifiers[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.3456/' . $doc2Id, $doi->getValue()); + + // change value to create a DOI conflict + $doi->setValue('10.3456/' . $doc1Id); + $doc2->setIdentifier(array($doi)); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register($doc2); + $this->assertNull($doi); + } + + public function testRegisterDocWithMissingProps() { + $this->adaptDoiConfiguration(array('prefix' => '10.3456/')); + $docId = $this->createTestDocWithDoi('10.3456/'); + + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_RegistrationException'); + $doi = $doiManager->register(new Opus_Document($docId)); + } + + public function testRegisterDocWithRequiredPropsButMissingConfig() { + $this->adaptDoiConfiguration(array('prefix' => '10.3456/')); + $docId = $this->createTestDocWithDoi('10.3456/'); + + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_RegistrationException'); + $doi = $doiManager->register(new Opus_Document($docId)); + } + + public function testRegisterDocWithRequiredPropsButCompleteConfig() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.3456/', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost' + ) + ))); + $docId = $this->createTestDocWithDoi('10.3456/'); + + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_RegistrationException'); + $doi = $doiManager->register(new Opus_Document($docId)); + } + + public function testRegisterAndVerifyDocSuccessfully() { + $this->markTestSkipped('kann nur für manuellen Test verwendet werden, da DataCite-Testumgebung erforderlich (Username und Password werden in config.ini gesetzt)'); + + // add url to config to allow creation of frontdoor URLs + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4/')))); + + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'serviceUrl' => 'https://mds.test.datacite.org' + ) + ))); + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId), true); + $this->assertNotNull($doi); + + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $doi->getValue()); + $this->assertEquals('registered', $doi->getStatus()); + $this->assertNotNull($doi->getRegistrationTs()); + + $status = $doiManager->verifyRegistered(); + $this->assertFalse($status->isNoDocsToProcess()); + $statusOfDoc = $status->getDocsWithDoiStatus()[$docId]; + $this->assertNotNull($statusOfDoc); + $this->assertFalse($statusOfDoc['error']); + } + + public function testRegisterPendingWithoutDocs() { + $doiManager = new Opus_Doi_DoiManager(); + $status = $doiManager->registerPending(); + $this->assertTrue($status->isNoDocsToProcess()); + } + + public function testRegisterPendingWithDocWithWrongServerState() { + $this->createTestDocWithDoi('10.5072/OPUS4-'); + $doiManager = new Opus_Doi_DoiManager(); + $status = $doiManager->registerPending(); + $this->assertTrue($status->isNoDocsToProcess()); + } + + public function testRegisterPendingWithDoc() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + $doiManager = new Opus_Doi_DoiManager(); + $status = $doiManager->registerPending(null); + $this->assertFalse($status->isNoDocsToProcess()); + + $statusOfDoc = $status->getDocsWithDoiStatus()[$docId]; + $this->assertNotNull($statusOfDoc); + $this->assertTrue($statusOfDoc['error']); + } + + public function testVerifyRegistered() { + $doiManager = new Opus_Doi_DoiManager(); + $status = $doiManager->verifyRegistered(); + + $this->assertTrue($status->isNoDocsToProcess()); + } + + public function testVerifyRegisteredBefore() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered'); + $doiManager = new Opus_Doi_DoiManager(); + $status = $doiManager->verifyRegisteredBefore(); + + $this->assertFalse($status->isNoDocsToProcess()); + $statusOfDoc = $status->getDocsWithDoiStatus()[$docId]; + $this->assertNotNull($statusOfDoc); + $this->assertTrue($statusOfDoc['error']); + } + + public function testVerifyWithUnknownDocId() { + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify('999'); + $this->assertNull($result); + } + + public function testVerifyWithDocWithoutDoi() { + $doc = new Opus_Document(); + $docId = $doc->store(); + + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify($docId); + $this->assertNull($result); + } + + public function testVerifyWithUnregisteredDoi() { + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify($docId); + $this->isNull($result); + } + + public function testVerifyWithVerifiedDoiWithoutReverification() { + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'verified'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify($docId, false); + $this->assertNull($result); + } + + public function testVerifyWithVerifiedDoiWithReverification() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost' + ) + ))); + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'verified'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify($docId, true); + + $this->assertNotNull($result); + $this->assertEquals('doi', $result->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $result->getValue()); + // Status-Downgrade prüfen + $this->assertEquals('registered', $result->getStatus()); + } + + public function testVerifyWithRegisteredDoiAndMissingConfig() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->verify($docId); + + $this->assertNotNull($result); + $this->assertEquals('doi', $result->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $result->getValue()); + $this->assertEquals('registered', $result->getStatus()); + } + + public function testVerifyBeforeFilterPositive() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost' + ) + ))); + + $dateTimeZone = new DateTimeZone(date_default_timezone_get()); + $dateTime = new DateTime('now', $dateTimeZone); + $currentDate = $dateTime->format('Y-m-d H:i:s'); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered', $currentDate); + + $doiManager = new Opus_Doi_DoiManager(); + $dateTime = $dateTime->add(new DateInterval('PT1H')); + $oneHourAfterCurrentDate = $dateTime->format('Y-m-d H:i:s'); + $result = $doiManager->verify($docId, true, $oneHourAfterCurrentDate); + + $this->assertNotNull($result); + $this->assertEquals('doi', $result->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $result->getValue()); + $this->assertEquals('registered', $result->getStatus()); + } + + public function testVerifyBeforeFilterNegative() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost' + ) + ))); + + $dateTimeZone = new DateTimeZone(date_default_timezone_get()); + $dateTime = new DateTime('now', $dateTimeZone); + $currentDate = $dateTime->format('Y-m-d H:i:s'); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered', $currentDate); + + $doiManager = new Opus_Doi_DoiManager(); + $dateTime = $dateTime->sub(new DateInterval('PT1H')); + $oneHourBeforeCurrentDate = $dateTime->format('Y-m-d H:i:s'); + $result = $doiManager->verify($docId, true, $oneHourBeforeCurrentDate); + + $this->assertNull($result); + } + + public function testVerifySuccessfully() { + $this->markTestSkipped('kann nur für manuellen Test verwendet werden, da DataCite-Testumgebung erforderlich (Username und Password werden in config.ini gesetzt)'); + + // add url to config to allow creation of frontdoor URLs + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4/')))); + + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'serviceUrl' => 'https://mds.test.datacite.org' + ) + ))); + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register(new Opus_Document($docId), true); + $this->assertNotNull($doi); + + $doi = $doiManager->verify($docId); + $this->assertNotNull($doi); + + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $doi->getValue()); + $this->assertEquals('verified', $doi->getStatus()); + $this->assertNotNull($doi->getRegistrationTs()); + } + + public function testVerifyFailed() { + $this->markTestSkipped('kann nur für manuellen Test verwendet werden, da DataCite-Testumgebung erforderlich (Username und Password werden in config.ini gesetzt)'); + + // add url to config to allow creation of frontdoor URLs + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge( + new Zend_Config(array('url' => 'http://localhost/opus4/')))); + + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'serviceUrl' => 'https://mds.test.datacite.org' + ) + ))); + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'verified'); + + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->verify($docId); + + $this->assertNotNull($doi); + + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('10.5072/OPUS4-' . $docId, $doi->getValue()); + $this->assertEquals('verified', $doi->getStatus()); + } + + public function testGetAllEmptyResult() { + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->getAll(); + $this->assertEmpty($result); + } + + public function testGetAllOneExternalDoi() { + $this->createTestDocWithDoi('10.5072'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->getAll(); + $this->assertEmpty($result); + } + + public function testGetAll() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + $doiManager = new Opus_Doi_DoiManager(); + $result = $doiManager->getAll(); + $this->assertCount(1, $result); + } + + public function testGetAllStatusFiltered() { + $this->adaptDoiConfiguration(array( + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered'); + + $doiManager = new Opus_Doi_DoiManager(); + + $result = $doiManager->getAll('registered'); + $this->assertCount(1, $result); + + $result = $doiManager->getAll(); + $this->assertCount(1, $result); + + $result = $doiManager->getAll('verified'); + $this->assertEmpty($result); + + $result = $doiManager->getAll('unregistered'); + $this->assertEmpty($result); + + $this->createTestDocWithDoi('10.5072/OPUS4-'); + $result = $doiManager->getAll('unregistered'); + $this->assertCount(1, $result); + } + + public function testGenerateNewDoiMissingConfig() { + $doc = new Opus_Document(); + $doc->store(); + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->generateNewDoi($doc); + } + + public function testGenerateNewDoiMissingGeneratorClass() { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_MissingGenerator') + ); + + $doc = new Opus_Document(); + $doc->store(); + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->generateNewDoi($doc); + } + + public function testGenerateNewDoiInvalidDocId() { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator') + ); + + $doc = new Opus_Document(); + $doc->store(); + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->generateNewDoi('999'); + } + + public function testGenerateNewDoiWithDocId() { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $doc = new Opus_Document(); + $docId = $doc->store(); + $doiManager = new Opus_Doi_DoiManager(); + $doiValue = $doiManager->generateNewDoi($docId); + + $this->assertEquals('10.5072/OPUS4-' . $docId, $doiValue); + } + + public function testGenerateNewDoiWithDoc() { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $doc = new Opus_Document(); + $docId = $doc->store(); + $doiManager = new Opus_Doi_DoiManager(); + $doiValue = $doiManager->generateNewDoi(new Opus_Document($docId)); + + $this->assertEquals('10.5072/OPUS4-' . $docId, $doiValue); + } + + public function testDeleteMetadataForDoiDocWithoutDoi() + { + $doc = new Opus_Document(); + $doc->store(); + + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi($doc); + } + + public function testDeleteMetadataForDoiDocWithExternalDoi() + { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $docId = $this->createTestDocWithDoi('10.9999/system-'); + + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi(new Opus_Document($docId)); + } + + public function testDeleteMetadataForDoiDocWithLocalDoi() + { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4') + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi(new Opus_Document($docId)); + } + + public function testDeleteMetadataForDoiDocWithLocalRegisteredDoi() + { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost') + ) + ) + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'registered'); + + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi(new Opus_Document($docId)); + } + + public function testDeleteMetadataForDoiDocWithLocalVerifiedDoi() + { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost') + ) + ) + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-', 'verified'); + + $doiManager = new Opus_Doi_DoiManager(); + $doiManager->deleteMetadataForDoi(new Opus_Document($docId)); + } + + public function testUpdateLandingPageUrlOfDoiWithMissingConfig() + { + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->updateLandingPageUrlOfDoi('10.5072/OPUS4-999', 'http://localhost/frontdoor/999'); + } + + public function testUpdateLandingPageUrlOfDoi() + { + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'username' => 'test', + 'password' => 'secret', + 'serviceUrl' => 'http://localhost') + ) + ) + ); + + $doiManager = new Opus_Doi_DoiManager(); + $this->setExpectedException('Opus_Doi_DoiException'); + $doiManager->updateLandingPageUrlOfDoi('10.5072/OPUS4-999', 'http://localhost/frontdoor/999'); + } + + public function testUpdateLandingPageUrlOfDoiWithExistingDoi() + { + $this->markTestSkipped('kann nur für manuellen Test verwendet werden, da DataCite-Testumgebung erforderlich (Username und Password werden in config.ini gesetzt)'); + + $config = Zend_Registry::get('Zend_Config'); + + Zend_Registry::set('Zend_Config', + $config->merge( + new Zend_Config(array('url' => 'http://localhost/opus4')))); + + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'serviceUrl' => 'https://mds.test.datacite.org') + ) + ) + ); + + $docId = $this->createTestDocWithDoi('10.5072/OPUS4-'); + $this->addRequiredPropsToDoc(new Opus_Document($docId)); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->register($docId, true); + $this->assertEquals('registered', $doi->getStatus()); + + $doi = $doiManager->verify($docId); + $this->assertEquals('verified', $doi->getStatus()); + + $doiManager->updateLandingPageUrlOfDoi('10.5072/OPUS4-' . $docId, 'http://localhost/opus5/frontdoor/index/index/' . $docId); + + $doi = $doiManager->verify($docId); + $this->assertEquals('registered', $doi->getStatus()); + + Zend_Registry::set('Zend_Config', + $config->merge( + new Zend_Config(array('url' => 'http://localhost/opus5')))); + + $this->adaptDoiConfiguration(array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '10.5072/', + 'localPrefix' => 'OPUS4', + 'registration' => array( + 'datacite' => array( + 'serviceUrl' => 'https://mds.test.datacite.org') + ) + ) + ); + + $doiManager = new Opus_Doi_DoiManager(); + $doi = $doiManager->verify($docId); + $this->assertEquals('verified', $doi->getStatus()); + } + + private function adaptDoiConfiguration($doiConfig) + { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge(new Zend_Config(array('doi' => $doiConfig)))); + } + + private function addRequiredPropsToDoc($doc) + { + $doc->setCompletedYear(2018); + $doc->setServerState('unpublished'); + $doc->setType('book'); + $doc->setPublisherName('ACME corp'); + + $author = new Opus_Person(); + $author->setLastName('Doe'); + $author->setFirstName('John'); + $doc->addPersonAuthor($author); + + $title = new Opus_Title(); + $title->setType('main'); + $title->setValue('Document without meaningful title'); + $title->setLanguage('deu'); + $doc->addTitleMain($title); + + $doc->store(); + } + + private function createTestDocWithDoi($doiPrefix, $status = null, $registrationTs = null) + { + $doc = new Opus_Document(); + $docId = $doc->store(); + + $doc = new Opus_Document($docId); + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue($doiPrefix . $docId); + if (!is_null($status)) { + $doi->setStatus($status); + } + if (!is_null($registrationTs)) { + $doi->setRegistrationTs($registrationTs); + } + $doc->setIdentifier(array($doi)); + $doc->store(); + + return $docId; + } +} diff --git a/tests/Opus/Doi/Generator/DefaultGeneratorTest.php b/tests/Opus/Doi/Generator/DefaultGeneratorTest.php new file mode 100644 index 000000000..d0ff81605 --- /dev/null +++ b/tests/Opus/Doi/Generator/DefaultGeneratorTest.php @@ -0,0 +1,198 @@ + + * @copyright Copyright (c) 2018, OPUS 4 development team + * @license http://www.gnu.org/licenses/gpl.html General Public License + */ + +class Opus_Doi_Generator_DefaultGeneratorTest extends TestCase { + + public function testGenerateWithMissingConfig() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $doc->store(); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $exception = null; + try { + $generator->generate($doc); + } + catch (Opus_Doi_Generator_DoiGeneratorException $e) { + $exception = $e; + } + + $this->assertTrue($exception instanceof Opus_Doi_Generator_DoiGeneratorException); + + } + + public function testGenerateWithPartialConfig() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $doc->store(); + + $this->adaptDoiConfiguration(array('prefix' => '')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $exception = null; + try { + $generator->generate($doc); + } + catch (Opus_Doi_Generator_DoiGeneratorException $e) { + $exception = $e; + } + + $this->assertTrue($exception instanceof Opus_Doi_Generator_DoiGeneratorException); + } + + public function testGenerateWithPrefixConfig() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $docId = $doc->store(); + + $this->adaptDoiConfiguration(array('prefix' => '12.3456')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $doi = $generator->generate($doc); + $this->assertEquals('12.3456/' . $docId, $doi); + } + + public function testGenerateWithPrefixConfigAlt() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $docId = $doc->store(); + + $this->adaptDoiConfiguration(array('prefix' => '12.3456/')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $doi = $generator->generate($doc); + $this->assertEquals('12.3456/' . $docId, $doi); + } + + public function testGenerateWithCompleteConfig() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $docId = $doc->store(); + + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $doi = $generator->generate($doc); + $this->assertEquals('12.3456/opustest-' . $docId, $doi); + } + + public function testGenerateWithCompleteConfigAlt() { + // create minimal test document to provide document ID + $doc = new Opus_Document(); + $docId = $doc->store(); + + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest-')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $doi = $generator->generate($doc); + $this->assertEquals('12.3456/opustest-' . $docId, $doi); + } + + public function testIsLocalWithMissingConfig() { + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertFalse($generator->isLocal('doiValue')); + } + + public function testIsLocalWithPartialConfigNegative() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertFalse($generator->isLocal('doiValue')); + + $this->assertFalse($generator->isLocal('12.3456')); + } + + public function testIsLocalWithPartialConfigPositive() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertTrue($generator->isLocal('12.3456/')); + } + + public function testIsLocalWithPartialAltConfigNegative() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertFalse($generator->isLocal('doiValue')); + + $this->assertFalse($generator->isLocal('12.3456')); + } + + public function testIsLocalWithPartialAltConfigPositive() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertTrue($generator->isLocal('12.3456/')); + } + + public function testIsLocalWithCompleteConfigNegative() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertFalse($generator->isLocal('doiValue')); + + $this->assertFalse($generator->isLocal('12.3456/opustest')); + } + + public function testIsLocalWithCompleteConfigPositive() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertTrue($generator->isLocal('12.3456/opustest-')); + + $this->assertTrue($generator->isLocal('12.3456/opustest-789')); + } + + public function testIsLocalWithCompleteAltConfigNegative() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest-')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertFalse($generator->isLocal('doiValue')); + + $this->assertFalse($generator->isLocal('12.3456/opustest')); + } + + public function testIsLocalWithCompleteAltConfigPositive() { + $this->adaptDoiConfiguration(array('prefix' => '12.3456/', 'localPrefix' => 'opustest-')); + + $generator = new Opus_Doi_Generator_DefaultGenerator(); + $this->assertTrue($generator->isLocal('12.3456/opustest-')); + + $this->assertTrue($generator->isLocal('12.3456/opustest-789')); + } + + private function adaptDoiConfiguration($doiConfig) { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge(new Zend_Config(array('doi' => $doiConfig)))); + } +} \ No newline at end of file diff --git a/tests/Opus/IdentifierTest.php b/tests/Opus/IdentifierTest.php index b3993a610..33a6f6d53 100644 --- a/tests/Opus/IdentifierTest.php +++ b/tests/Opus/IdentifierTest.php @@ -1,5 +1,4 @@ - * @copyright Copyright (c) 2010, OPUS 4 development team + * @author Sascha Szott + * @copyright Copyright (c) 2010-2018, OPUS 4 development team * @license http://www.gnu.org/licenses/gpl.html General Public License - * @version $Id$ */ -class Opus_IdentifierTest extends TestCase { +class Opus_IdentifierTest extends TestCase +{ private function createDocumentWithIdentifierUrn($urn) { $document = new Opus_Document(); @@ -43,7 +43,7 @@ private function createDocumentWithIdentifierUrn($urn) { ->setValue($urn); return $document; } - + /** * Check if exactly one document with testUrn exists * @@ -71,7 +71,7 @@ function testCreateDocumentWithUrn() { $this->assertEquals('urn', $identifiers[0]->getType()); $this->assertEquals($testUrn, $identifiers[0]->getValue()); } - + /** * Regression test for OPUSVIER-2289 */ @@ -161,5 +161,293 @@ function testCreateUrnCollisionUsingAddIdentifierUrn() { } } -} + public function testIsValidDoiPositive() { + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue('12.3456/opustest-789'); + $this->assertTrue($doi->isValidDoi()); + } + + public function testIsValidDoiNegative() { + $doiValuesToProbe = array( + '10.000/äöüß-987', + '10.000/opus~987', + '10.000/opus*987', + '10.000/opus#987'); + foreach ($doiValuesToProbe as $value) { + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue($value); + $this->assertFalse($doi->isValidDoi(), 'expected ' . $value . ' to be an invalid DOI value'); + } + } + + public function testIsLocalDoiPositiveWithGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/opustest-987'); + + $this->assertDoi($docId, '12.3456/opustest-987', true); + } + + public function testIsLocalDoiPositiveWithGeneratorClassAndMissingPrefixShlash() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/opustest-987'); + + $this->assertDoi($docId, '12.3456/opustest-987', true); + } + + public function testIsLocalDoiPositiveWithoutGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => '', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/opustest-987'); + + $this->assertDoi($docId, '12.3456/opustest-987', true); + } + + public function testIsLocalDoiPositiveWithoutGeneratorClassAndMissingPrefixSlash() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => '', + 'prefix' => '12.3456', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/opustest-987'); + + $this->assertDoi($docId, '12.3456/opustest-987', true); + } + + public function testIsLocalDoiNegativeWithGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/anothersystem-987'); + + $this->assertDoi($docId, '12.3456/anothersystem-987', false); + } + + public function testIsLocalDoiNegativeWithGeneratorClassAlt() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.6543/opustest-987'); + + $this->assertDoi($docId, '12.6543/opustest-987', false); + } + + public function testIsLocalDoiNegativeWithMissingGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_MissingGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/opustest-987'); + + $this->assertDoi($docId, '12.3456/opustest-987', false); + } + + public function testIsLocalDoiNegativeWithoutGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => '', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('12.3456/anothersystem-987'); + + $this->assertDoi($docId, '12.3456/anothersystem-987', false); + } + + public function testIsLocalDoiNegativeWithoutPrefixAndWithoutGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => '', + 'prefix' => '', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('opustest-987'); + + $this->assertDoi($docId, 'opustest-987', false); + } + + public function testIsLocalDoiNegativeWithoutPrefixAndWithGeneratorClass() { + // adapt configuration to allow detection local DOIs + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + $docId = $this->createTestDocumentWithDoi('opustest-987'); + + $this->assertDoi($docId, 'opustest-987', false); + } + + public function testIsDoiUniquePositive() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + + $doc1 = $this->createTestDocumentWithDoi('12.3456/opustest-789', false); + $doc1Id = $doc1->store(); + + $doc2 = $this->createTestDocumentWithDoi('12.3456/opustest-890', false); + $dois = $doc2->getIdentifier(); + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('12.3456/opustest-890', $doi->getValue()); + $this->assertTrue($doi->isDoiUnique()); + $this->assertTrue($doi->isDoiUnique($doc1Id)); + + $doc2Id = $doc2->store(); + $this->assertNotNull($doc2Id); + } + + public function testIsDoiUniqueNegative() { + $doiConfig = array( + 'generatorClass' => 'Opus_Doi_Generator_DefaultGenerator', + 'prefix' => '12.3456/', + 'localPrefix' => 'opustest', + ); + $this->adaptDoiConfiguration($doiConfig); + + $doc1 = $this->createTestDocumentWithDoi('12.3456/opustest-789', false); + $doc1Id = $doc1->store(); + + $doc2 = $this->createTestDocumentWithDoi('12.3456/opustest-890', false); + $doc2Id = $doc2->store(); + + $doc3 = $this->createTestDocumentWithDoi('12.3456/opustest-789', false); + $dois = $doc3->getIdentifier(); + $this->assertCount(1, $dois); + + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals('12.3456/opustest-789', $doi->getValue()); + + $this->assertFalse($doi->isDoiUnique()); + $this->assertTrue($doi->isDoiUnique($doc1Id)); + $this->assertFalse($doi->isDoiUnique($doc2Id)); + + $exceptionToCheck = null; + try { + $doc3->store(); + } + catch (Exception $e) { + $exceptionToCheck = $e; + } + $this->assertTrue($exceptionToCheck instanceof Opus_Identifier_DoiAlreadyExistsException); + } + + public function testIsUrnUniquePositive() { + $doc1 = $this->createDocumentWithIdentifierUrn('urn:987654321'); + $doc1Id = $doc1->store(); + + $doc2 = $this->createDocumentWithIdentifierUrn('urn:123456789'); + + $urns = $doc2->getIdentifier(); + $this->assertCount(1, $urns); + $urn = $urns[0]; + $this->assertEquals('urn', $urn->getType()); + $this->assertEquals('urn:123456789', $urn->getValue()); + + $this->assertTrue($urn->isUrnUnique()); + $this->assertTrue($urn->isUrnUnique($doc1Id)); + + $doc2Id = $doc2->store(); + $this->assertNotNull($doc2Id); + } + + public function testIsUrnUniqueNegative() { + $doc1 = $this->createDocumentWithIdentifierUrn('urn:987654321'); + $doc1Id = $doc1->store(); + + $doc2 = $this->createDocumentWithIdentifierUrn('urn:123456789'); + $doc2Id = $doc2->store(); + + $doc3 = $this->createDocumentWithIdentifierUrn('urn:987654321'); + $urns = $doc3->getIdentifier(); + $this->assertCount(1, $urns); + $urn = $urns[0]; + $this->assertEquals('urn', $urn->getType()); + $this->assertEquals('urn:987654321', $urn->getValue()); + + $this->assertFalse($urn->isUrnUnique()); + $this->assertTrue($urn->isUrnUnique($doc1Id)); + $this->assertFalse($urn->isUrnUnique($doc2Id)); + + $exceptionToCheck = null; + try { + $doc3->store(); + } + catch (Exception $e) { + $exceptionToCheck = $e; + } + $this->assertTrue($exceptionToCheck instanceof Opus_Identifier_UrnAlreadyExistsException); + } + + private function assertDoi($docId, $value, $isLocal) { + $doc = new Opus_Document($docId); + $dois = $doc->getIdentifier(); + $this->assertCount(1, $dois); + + $doi = $dois[0]; + $this->assertEquals('doi', $doi->getType()); + $this->assertEquals($value, $doi->getValue()); + $this->assertEquals($isLocal, $doi->isLocalDoi()); + } + + private function createTestDocumentWithDoi($value, $store = true) { + $doc = new Opus_Document(); + + $doi = new Opus_Identifier(); + $doi->setType('doi'); + $doi->setValue($value); + $doc->setIdentifier(array($doi)); + + if (!$store) { + return $doc; + } + + $docId = $doc->store(); + return $docId; + } + + private function adaptDoiConfiguration($doiConfig) { + Zend_Registry::set('Zend_Config', + Zend_Registry::get('Zend_Config')->merge(new Zend_Config(array('doi' => $doiConfig)))); + } +} diff --git a/tests/Opus/Validate/IssnTest.php b/tests/Opus/Validate/IssnTest.php index a6c838182..2862ab36c 100644 --- a/tests/Opus/Validate/IssnTest.php +++ b/tests/Opus/Validate/IssnTest.php @@ -72,15 +72,15 @@ public function invalidIssnProvider() { */ public function messageIssnProvider() { return array( - array('12345478', 'form', "'12345478' is malformed"), - array('12456-65478', 'form', "'12456-65478' is malformed"), - array('123456789', 'form', "'123456789' is malformed"), - array('1050 124X', 'form', "'1050 124X' is malformed"), - array('1050_124X', 'form', "'1050_124X' is malformed"), - array('1050-1242', 'checkdigit', "The check digit of '1050-1242' is not valid"), - array('1062-512X', 'checkdigit', "The check digit of '1062-512X' is not valid"), - array('0025-5856', 'checkdigit', "The check digit of '0025-5856' is not valid"), - array('0001-3211', 'checkdigit', "The check digit of '0001-3211' is not valid"), + array('12345478', 'form', "'12345478' is malformed."), + array('12456-65478', 'form', "'12456-65478' is malformed."), + array('123456789', 'form', "'123456789' is malformed."), + array('1050 124X', 'form', "'1050 124X' is malformed."), + array('1050_124X', 'form', "'1050_124X' is malformed."), + array('1050-1242', 'checkdigit', "The check digit of '1050-1242' is not valid."), + array('1062-512X', 'checkdigit', "The check digit of '1062-512X' is not valid."), + array('0025-5856', 'checkdigit', "The check digit of '0025-5856' is not valid."), + array('0001-3211', 'checkdigit', "The check digit of '0001-3211' is not valid."), ); }