diff --git a/app/Exceptions/OaiModelException.php b/app/Exceptions/OaiModelException.php new file mode 100644 index 0000000..a60f020 --- /dev/null +++ b/app/Exceptions/OaiModelException.php @@ -0,0 +1,12 @@ +all(); $safeRemoveParameters = array('module', 'controller', 'action', 'role'); foreach ($safeRemoveParameters as $parameter) { unset($oaiRequest[$parameter]); } - return $this->__handleRequest($oaiRequest); + try { + $this->__handleRequest($oaiRequest); + } catch (OaiModelException $e) { + $errorCode = OaiModelError::mapCode($e->getCode()); + //$this->getLogger()->err($errorCode); + $this->_proc->setParameter('', 'oai_error_code', $errorCode); + //$this->getLogger()->err($e->getMessage()); + $this->_proc->setParameter('', 'oai_error_message', htmlentities($e->getMessage())); + } catch (Exception $e) { + //$this->getLogger()->err($e); + $this->_proc->setParameter('', 'oai_error_code', 'unknown'); + $this->_proc->setParameter('', 'oai_error_message', 'An internal error occured.'); + //$this->getResponse()->setHttpResponseCode(500); + } + // $xml = $this->_xml->saveXML(); + $xml = $this->_proc->transformToXML($this->_xml); + + //$xml = $this->doc->asXML(); + return response($xml)//->view('rss', array('rss'=>$this->rss)) + ->header('Content-Type', 'application/xml') + ->header('charset', 'utf-8'); } @@ -95,6 +119,8 @@ class RequestController extends Controller $this->handleListMetadataFormats(); } elseif ($oaiRequest['verb'] == 'ListRecords') { $this->handleListRecords($oaiRequest); + } elseif ($oaiRequest['verb'] == 'GetRecord') { + $this->handleGetRecord($oaiRequest); } elseif ($oaiRequest['verb'] == 'ListIdentifiers') { $this->handleListIdentifiers($oaiRequest); } elseif ($oaiRequest['verb'] == 'ListSets') { @@ -107,14 +133,6 @@ class RequestController extends Controller $this->_proc->setParameter('', 'oai_verb', $oaiRequest['verb']); $this->doc = $this->handleIdentify(); } - - //$xml = $this->_xml->saveXML(); - $xml = $this->_proc->transformToXML($this->_xml); - - //$xml = $this->doc->asXML(); - return response($xml)//->view('rss', array('rss'=>$this->rss)) - ->header('Content-Type', 'application/xml') - ->header('charset', 'utf-8'); } /** @@ -139,6 +157,89 @@ class RequestController extends Controller $this->_xml->appendChild($this->_xml->createElement('Documents')); } + /** + * Implements response for OAI-PMH verb 'GetRecord'. + * + * @param array &$oaiRequest Contains full request information + * @return void + */ + private function handleGetRecord(array &$oaiRequest) + { + // Identifier references metadata Urn, not plain Id! + // Currently implemented as 'oai:foo.bar.de:{docId}' or 'urn:nbn...-123' + $dataId = $this->getDocumentIdByIdentifier($oaiRequest['identifier']); + + $dataset = null; + try { + //$dataset = new Opus_Document($docId); + $dataset = Dataset::findOrFail($dataId); + } catch (ModelNotFoundException $ex) { + throw new OaiModelException( + 'The value of the identifier argument is unknown or illegal in this repository.', + OaiModelError::IDDOESNOTEXIST + ); + } + + $metadataPrefix = $oaiRequest['metadataPrefix']; + + // do not deliver datasets which are restricted by document state + if (is_null($dataset) + //or (false === in_array($dataset->getServerState(), $this->_deliveringDocumentStates)) + or (false === $dataset->whereIn('server_state', $this->deliveringDocumentStates)) + + + or (false === $dataset->hasEmbargoPassed())) { + throw new OaiModelException('Document is not available for OAI export!', OaiModelError::NORECORDSMATCH); + } + + $this->_xml->appendChild($this->_xml->createElement('Documents')); + $this->createXmlRecord($dataset); + } + + /** + * Retrieve a document id by an oai identifier. + * + * @param string $oaiIdentifier + * @result int + */ + private function getDocumentIdByIdentifier($oaiIdentifier) + { + $identifierParts = explode(":", $oaiIdentifier); + + $dataId = null; + switch ($identifierParts[0]) { + // case 'urn': + // //$finder = new Opus_DocumentFinder(); + // $finder = Dataset::query(); + // // $finder->setIdentifierTypeValue('urn', $oaiIdentifier); + // // $finder->setServerStateInList($this->_deliveringDocumentStates); + // $finder->whereIn('server_state', $this->deliveringDocumentStates); + // $docIds = $finder->ids(); + // $docId = $docIds[0]; + // break; + case 'oai': + if (isset($identifierParts[2])) { + $dataId = $identifierParts[2]; + } + break; + default: + throw new OaiModelException( + 'The prefix of the identifier argument is unknown.', + OaiModelError::BADARGUMENT + ); + break; + } + + if (empty($dataId) or !preg_match('/^\d+$/', $dataId)) { + throw new Oai_Model_Exception( + 'The value of the identifier argument is unknown or illegal in this repository.', + Oai_Model_Error::IDDOESNOTEXIST + ); + } + + return $dataId; + } + diff --git a/app/Models/Dataset.php b/app/Models/Dataset.php index 21fb13c..ac57892 100644 --- a/app/Models/Dataset.php +++ b/app/Models/Dataset.php @@ -11,6 +11,7 @@ use App\Models\Person; use App\Models\XmlCache; use App\Models\File; use Illuminate\Database\Eloquent\Model; +use Carbon\Carbon; class Dataset extends Model { @@ -40,6 +41,7 @@ class Dataset extends Model 'server_date_created', 'server_date_modified', 'server_date_published', + 'embargo_date', ]; //protected $dateFormat = 'Y-m-d'; @@ -209,4 +211,31 @@ class Dataset extends Model { return $this->project()->exists(); } + + + public function hasEmbargoPassed($now = null) + { + $embargoDate = $this->embargo_date; + if (is_null($embargoDate)) { + return true; + } + + if (is_null($now)) { + $now = $dt = Carbon::now(); + } + // Embargo has passed on the day after the specified date + // $embargoDate->setHour(23); + // $embargoDate->setMinute(59); + // $embargoDate->setSecond(59); + // $embargoDate->setTimezone('Z'); + + // $dt->year = 2015; + // $dt->month = 04; + // $dt->day = 21; + $embargoDate->hour = 23; + $embargoDate->minute = 59; + $embargoDate->second = 59; + + return ($embargoDate->gt($now) == true); + } } diff --git a/app/Models/OaiModelError.php b/app/Models/OaiModelError.php new file mode 100644 index 0000000..784d613 --- /dev/null +++ b/app/Models/OaiModelError.php @@ -0,0 +1,42 @@ + 'badVerb', + self::BADARGUMENT => 'badArgument', + self::NORECORDSMATCH => 'noRecordsMatch', + self::CANNOTDISSEMINATEFORMAT => 'cannotDisseminateFormat', + self::BADRESUMPTIONTOKEN => 'badResumptionToken', + self::IDDOESNOTEXIST => 'idDoesNotExist', + ); + /** + * Map internal error codes to OAI error codes. + * + * @param int $code Internal error code. + * @return string OAI error code. + */ + public static function mapCode($code) + { + if (false === array_key_exists($code, self::$oaiErrorCodes)) { + throw new Oai_Model_Exception("Unknown oai error code $code"); + } + return self::$oaiErrorCodes[$code]; + } +} diff --git a/config/laravellocalization.php b/config/laravellocalization.php index 0d12ded..064e999 100644 --- a/config/laravellocalization.php +++ b/config/laravellocalization.php @@ -323,6 +323,6 @@ return [ // URLs which should not be processed, e.g. '/nova', '/nova/*', '/nova-api/*' or specific application URLs // Defaults to [] - 'urlsIgnored' => ['/skipped', '/settings', '/settings/*'], + 'urlsIgnored' => ['/skipped', '/settings', '/settings/*', '/oai'], ]; diff --git a/public/oai-pmh.xslt b/public/oai-pmh.xslt index 3250276..d096f38 100644 --- a/public/oai-pmh.xslt +++ b/public/oai-pmh.xslt @@ -214,6 +214,12 @@ + + + + + +