More refactoring

This commit is contained in:
Daniel Neis Araujo 2013-05-14 18:24:59 -03:00
parent 1a2e5be242
commit cef72091cf
6 changed files with 202 additions and 314 deletions

View File

@ -9,33 +9,33 @@
* the support of on-the-fly output compression which may significantly * the support of on-the-fly output compression which may significantly
* reduce the amount of data being transfered. * reduce the amount of data being transfered.
* *
* This package has been inspired by PHP OAI Data Provider developed by Heinrich Stamerjohanns at University of Oldenburg. * This package has been inspired by PHP OAI Data Provider developed by Heinrich Stamerjohanns at University of Oldenburg.
* Some of the functions and algorithms used in this code were transplanted from his implementation at http://physnet.uni-oldenburg.de/oai/. * Some of the functions and algorithms used in this code were transplanted from his implementation at http://physnet.uni-oldenburg.de/oai/.
* *
* Database support is supported through PDO (PHP Data Objects included in * Database support is supported through PDO (PHP Data Objects included in
* the PHP distribution), so almost any popular SQL-database can be * the PHP distribution), so almost any popular SQL-database can be
* used without any change in the code. Only thing need to do is to configure * used without any change in the code. Only thing need to do is to configure
* database connection and define a suitable data structure. * database connection and define a suitable data structure.
* *
* The repository can be quite easily configured by just editing * The repository can be quite easily configured by just editing
* oaidp-config.php, most possible values and options are explained. * oaidp-config.php, most possible values and options are explained.
* *
* \section req_sec Requirements * \section req_sec Requirements
* - A running web server + PHP version 5.0 or above. * - A running web server + PHP version 5.0 or above.
* - A databse can be connected by PDO. * - A databse can be connected by PDO.
* *
* \section install_sec Installation * \section install_sec Installation
* *
*- Copy the the files in source package to a location under your *- Copy the the files in source package to a location under your
* document root of your web server. The directory structure should be * document root of your web server. The directory structure should be
* preserved. * preserved.
*- Change to that directory (e.g. cd /var/www/html/oai). *- Change to that directory (e.g. cd /var/www/html/oai).
*- Allow your webserver to write to the token directory. *- Allow your webserver to write to the token directory.
* The default token directory is /tmp which does not need any attention. * The default token directory is /tmp which does not need any attention.
*- Edit oaidp-config.php. Almost all possible options are *- Edit oaidp-config.php. Almost all possible options are
* explained. It is assumed that basic elements of a record are stored in * explained. It is assumed that basic elements of a record are stored in
* one simple table. You can find sql examples of table definition in doc folder. * one simple table. You can find sql examples of table definition in doc folder.
* If your data is organized differently, you have to adjust the <i>Query</i> functions * If your data is organized differently, you have to adjust the <i>Query</i> functions
* to reflect it and even develop your own code. * to reflect it and even develop your own code.
*- Check your oai site through a web browser. e.g. : \code http://localhost/oai/ \endcode *- Check your oai site through a web browser. e.g. : \code http://localhost/oai/ \endcode
*- SELinux needs special treatments for database connection and other permission. *- SELinux needs special treatments for database connection and other permission.
@ -91,14 +91,14 @@ $MY_URI = substr($MY_URI, 0, $pos). '/oai2.php';
<p>This implementation completely complies to <a href="http://www.openarchives.org/OAI/openarchivesprotocol.html" target="_blank">OAI-PMH 2.0</a>, including the support of on-the-fly output compression which may significantly <p>This implementation completely complies to <a href="http://www.openarchives.org/OAI/openarchivesprotocol.html" target="_blank">OAI-PMH 2.0</a>, including the support of on-the-fly output compression which may significantly
reduce the amount of data being transfered.</p> reduce the amount of data being transfered.</p>
<p> This package has been inspired by <a href='http://physnet.uni-oldenburg.de/oai/' target="_blank">PHP OAI Data Provider</a> developed by Heinrich Stamerjohanns at University of Oldenburg. <p> This package has been inspired by <a href='http://physnet.uni-oldenburg.de/oai/' target="_blank">PHP OAI Data Provider</a> developed by Heinrich Stamerjohanns at University of Oldenburg.
Some of the functions and algorithms used in this code were transplanted from his implementation.<p> Some of the functions and algorithms used in this code were transplanted from his implementation.<p>
<p>Database is supported through <a href="http://www.php.net/manual/en/book.pdo.php" target="_blank"><dfn><abbr title="PHP Data Objects">PDO</abbr></dfn></a>, so almost any popular SQL-database which has PDO driver can be used without any change in the code.</p> <p>Database is supported through <a href="http://www.php.net/manual/en/book.pdo.php" target="_blank"><dfn><abbr title="PHP Data Objects">PDO</abbr></dfn></a>, so almost any popular SQL-database which has PDO driver can be used without any change in the code.</p>
<p> It uses <dfn>DOM extension</dfn>,an extension included in every PHP installation since version 5, for generating XML files. With PHP 5 or above no extra extension is needed.</p> <p> It uses <dfn>DOM extension</dfn>,an extension included in every PHP installation since version 5, for generating XML files. With PHP 5 or above no extra extension is needed.</p>
<p>The repository can be quite easily configured by just editing oai2/oaidp-config.php, most possible values and options are explained. <p>The repository can be quite easily configured by just editing oai2/oaidp-config.php, most possible values and options are explained.
For requirements and instructions to install and configure, please reference <a href="doc/index.html">the documentation</a>.</p> For requirements and instructions to install and configure, please reference <a href="doc/index.html">the documentation</a>.</p>
<p>Once you have setup your Data Provider, you can the easiliy check the generated answers (it will be XML) of your Data Provider <p>Once you have setup your Data Provider, you can the easiliy check the generated answers (it will be XML) of your Data Provider
@ -106,7 +106,7 @@ by clicking on the <a href="#tests">test links below</a>. </p>
<p>For simple visual tests set <em>$SHOW_QUERY_ERROR</em> to <em>TRUE</em> and <em>$CONTENT_TYPE</em> to <em>text/plain</em>, so you can easily read the generated XML responses in your browser. </p> <p>For simple visual tests set <em>$SHOW_QUERY_ERROR</em> to <em>TRUE</em> and <em>$CONTENT_TYPE</em> to <em>text/plain</em>, so you can easily read the generated XML responses in your browser. </p>
<p><strong>Remember</strong>, <em>GetRecord</em> needs <b><i>identifier</i></b> to work. <p><strong>Remember</strong>, <em>GetRecord</em> needs <b><i>identifier</i></b> to work.
So please change it use your own or you should see a response with error message.</p> So please change it use your own or you should see a response with error message.</p>
<p> <p>

View File

@ -1,18 +1,5 @@
<?php <?php
/**
* OAI Data Provider command processor
*
* OAI Data Provider is not designed for human to retrieve data.
*
* This is an implementation of OAI Data Provider version 2.0.
* @see http://www.openarchives.org/OAI/2.0/openarchivesprotocol.htm
*
* It needs other files:
* - oaidp-util.php : Utility functions
*
*/
require_once('oaidp-util.php');
require_once('oai2server.php'); require_once('oai2server.php');
/** /**

View File

@ -12,7 +12,6 @@ class OAI2Exception extends Exception {
'text' => "The value '{$value}' of attribute '{$argument}' on element 'request' is not valid with respect to its type, 'UTCdatetimeType'.", 'text' => "The value '{$value}' of attribute '{$argument}' on element 'request' is not valid with respect to its type, 'UTCdatetimeType'.",
'code' => 'badArgument', 'code' => 'badArgument',
), ),
'badResumptionToken' => array( 'badResumptionToken' => array(
'text' => "The resumptionToken '{$value}' does not exist or has already expired.", 'text' => "The resumptionToken '{$value}' does not exist or has already expired.",
), ),
@ -32,12 +31,6 @@ class OAI2Exception extends Exception {
), ),
'idDoesNotExist' => array( 'idDoesNotExist' => array(
'text' => "The value '{$value}' of the identifier does not exist in this repository.", 'text' => "The value '{$value}' of the identifier does not exist in this repository.",
/*
if (!is_valid_uri($value)) {
'code' = 'badArgument',
'text' .= ' Invalidated URI has been detected.',
}
*/
), ),
'missingArgument' => array( 'missingArgument' => array(
'text' => "The required argument '{$argument}' is missing in the request.", 'text' => "The required argument '{$argument}' is missing in the request.",

View File

@ -1,195 +1,141 @@
<?php <?php
require_once('oai2exception.php'); require_once('oai2exception.php');
require_once('oai2xml.php'); require_once('oai2xml.php');
/** /**
* The content-type the WWW-server delivers back. For debug-puposes, "text/plain" * This is an implementation of OAI Data Provider version 2.0.
* is easier to view. On a production site you should use "text/xml". * @see http://www.openarchives.org/OAI/2.0/openarchivesprotocol.htm
*/ */
define('CONTENT_TYPE', 'Content-Type: text/xml');
/** After 24 hours resumptionTokens become invalid. Unit is second. */
define('TOKEN_VALID',24*3600);
/** Where token is saved and path is included */
define('TOKEN_PREFIX','/tmp/oai_pmh-');
class OAI2Server { class OAI2Server {
public $errors = array(); public $errors = array();
private $args = array(); private $args = array();
private $verb = ''; private $verb = '';
private $token_prefix = '/tmp/oai_pmh-';
private $token_valid = 86400;
function __construct($uri, $args, $identifyResponse, $callbacks) { function __construct($uri, $args, $identifyResponse, $callbacks) {
if (!isset($args['verb']) || empty($args['verb'])) { if (!isset($args['verb']) || empty($args['verb'])) {
$this->errors[] = new OAI2Exception('noVerb'); $this->errors[] = new OAI2Exception('noVerb');
$this->errorResponse(); } else {
} $verbs = array('Identify', 'ListMetadataFormats', 'ListSets', 'ListIdentifiers', 'ListRecords', 'GetRecord');
if (in_array($args['verb'], $verbs)) {
$this->verb = $args['verb']; $this->verb = $args['verb'];
unset($args['verb']);
$this->args = $args;
$this->uri = $uri; unset($args['verb']);
$this->identifyResponse = $identifyResponse; $this->args = $args;
$this->listMetadataFormatsCallback = $callbacks['ListMetadataFormats']; $this->uri = $uri;
$this->listSetsCallback = $callbacks['ListSets'];
$this->listRecordsCallback = $callbacks['ListRecords'];
$this->getRecordCallback = $callbacks['GetRecord'];
$this->response = new OAI2XMLResponse($this->uri, $this->verb, $this->args); $this->identifyResponse = $identifyResponse;
$this->respond(); $this->listMetadataFormatsCallback = $callbacks['ListMetadataFormats'];
} $this->listSetsCallback = $callbacks['ListSets'];
$this->listRecordsCallback = $callbacks['ListRecords'];
$this->getRecordCallback = $callbacks['GetRecord'];
private function respond() { $this->response = new OAI2XMLResponse($this->uri, $this->verb, $this->args);
switch ($this->verb) { call_user_func(array($this, $this->verb));
case 'Identify': $this->identify(); break; } else {
$this->errors[] = new OAI2Exception('badVerb', $args['verb']);
case 'ListMetadataFormats': $this->listMetadataFormats(); break; }
case 'ListSets': $this->listSets(); break;
case 'ListIdentifiers':
case 'ListRecords': $this->listRecords(); break;
case 'GetRecord': $this->getRecord(); break;
default: $this->errors[] = new OAI2Exception('badVerb', $this->args['verb']);
} }
if (empty($this->errors)) { if (empty($this->errors)) {
header(CONTENT_TYPE);
$this->response->display(); $this->response->display();
} else { } else {
$this->errorResponse(); $errorResponse = new OAI2XMLResponse($this->uri, $this->verb, $this->args);
$oai_node = $errorResponse->doc->documentElement;
foreach($this->errors as $e) {
$node = $errorResponse->addChild($oai_node,"error",$e->getMessage());
$node->setAttribute("code",$e->getOAI2Code());
}
$errorResponse->display();
} }
} }
private function errorResponse() { public function Identify() {
$errorResponse = new OAI2XMLResponse($this->uri, $this->verb, $this->args);
$oai_node = $errorResponse->doc->documentElement;
foreach($this->errors as $e) {
$node = $errorResponse->addChild($oai_node,"error",$e->getMessage());
$node->setAttribute("code",$e->getOAI2Code());
}
header(CONTENT_TYPE);
$errorResponse->display();
exit();
}
/**
* Response to Verb Identify
*
* Tell the world what the data provider is. Usually it is static once the provider has been set up.
*
* http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm for details
*/
public function identify() {
if (count($this->args) > 0) { if (count($this->args) > 0) {
foreach($args as $key => $val) { foreach($this->args as $key => $val) {
$this->errors[] = new OAI2Exception('badArgument', $key, $val); $this->errors[] = new OAI2Exception('badArgument', $key, $val);
} }
$this->errorResponse(); } else {
} foreach($this->identifyResponse as $key => $val) {
$this->response->addToVerbNode($key, $val);
foreach($this->identifyResponse as $key => $val) { }
$this->response->addToVerbNode($key, $val);
} }
} }
/** public function ListMetadataFormats() {
* Response to Verb ListMetadataFormats
*
* The information of supported metadata formats
*/
public function listMetadataFormats() {
foreach ($this->args as $argument => $value) { foreach ($this->args as $argument => $value) {
if ($argument != 'identifier') { if ($argument != 'identifier') {
$this->errors[] = new OAI2Exception('badArgument', $argument, $value); $this->errors[] = new OAI2Exception('badArgument', $argument, $value);
} }
} }
if (!empty($this->errors)) { if (empty($this->errors)) {
$this->errorResponse(); try {
} if ($formats = call_user_func($this->listMetadataFormatsCallback, $this->args['identifier'])) {
foreach($formats as $key => $val) {
try { $cmf = $this->response->addToVerbNode("metadataFormat");
if ($formats = call_user_func($this->listMetadataFormatsCallback, $this->args['identifier'])) { $this->response->addChild($cmf,'metadataPrefix',$key);
foreach($formats as $key => $val) { $this->response->addChild($cmf,'schema',$val['schema']);
$cmf = $this->response->addToVerbNode("metadataFormat"); $this->response->addChild($cmf,'metadataNamespace',$val['metadataNamespace']);
$this->response->addChild($cmf,'metadataPrefix',$key); }
$this->response->addChild($cmf,'schema',$val['schema']); } else {
$this->response->addChild($cmf,'metadataNamespace',$val['metadataNamespace']); $this->errors[] = new OAI2Exception('noMetadataFormats');
} }
} else { } catch (OAI2Exception $e) {
$this->errors[] = new OAI2Exception('noMetadataFormats'); $this->errors[] = $e;
} }
} catch (OAI2Exception $e) {
$this->errors[] = $e;
} }
} }
/** public function ListSets() {
* Response to Verb ListSets
*
* Lists what sets are available to records in the system.
* This variable is filled in config-sets.php
*/
public function listSets() {
if (isset($this->args['resumptionToken'])) { if (isset($this->args['resumptionToken'])) {
if (count($this->args) > 1) { if (count($this->args) > 1) {
$this->errors[] = new OAI2Exception('exclusiveArgument'); $this->errors[] = new OAI2Exception('exclusiveArgument');
} else { } else {
if ((int)$val+TOKEN_VALID < time()) { if ((int)$val+$this->token_valid < time()) {
$this->errors[] = new OAI2Exception('badResumptionToken'); $this->errors[] = new OAI2Exception('badResumptionToken');
} }
} }
$resumptionToken = $this->args['resumptionToken'];
} else {
$resumptionToken = null;
} }
if (!empty($this->errors)) { if (!empty($this->errors)) {
$this->errorResponse(); if ($sets = call_user_func($this->listSetsCallback, $resumptionToken)) {
}
if ($sets = call_user_func($this->listSetsCallback)) { foreach($sets as $set) {
foreach($sets as $set) { $setNode = $this->response->addToVerbNode("set");
$setNode = $this->response->addToVerbNode("set"); foreach($set as $key => $val) {
if($key=='setDescription') {
foreach($set as $key => $val) { $desNode = $this->response->addChild($setNode,$key);
if($key=='setDescription') { $des = $this->response->doc->createDocumentFragment();
$desNode = $this->response->addChild($setNode,$key); $des->appendXML($val);
$des = $this->response->doc->createDocumentFragment(); $desNode->appendChild($des);
$des->appendXML($val); } else {
$desNode->appendChild($des); $this->response->addChild($setNode,$key,$val);
} else { }
$this->response->addChild($setNode,$key,$val);
} }
} }
} else {
$this->errors[] = new OAI2Exception('noSetHierarchy');
} }
} else {
$this->errors[] = new OAI2Exception('noSetHierarchy');
} }
} }
/** public function GetRecord() {
* Response to Verb GetRecord
*
* Retrieve a record based its identifier.
*
* Local variables <B>$metadataPrefix</B> and <B>$identifier</B> need to be provided through global array variable <B>$args</B>
* by their indexes 'metadataPrefix' and 'identifier'.
* The reset of information will be extracted from database based those two parameters.
*/
public function getRecord() {
if (!isset($this->args['metadataPrefix'])) { if (!isset($this->args['metadataPrefix'])) {
$this->errors[] = new OAI2Exception('missingArgument', 'metadataPrefix'); $this->errors[] = new OAI2Exception('missingArgument', 'metadataPrefix');
@ -202,52 +148,66 @@ class OAI2Server {
if (!isset($this->args['identifier'])) { if (!isset($this->args['identifier'])) {
$this->errors[] = new OAI2Exception('missingArgument', 'identifier'); $this->errors[] = new OAI2Exception('missingArgument', 'identifier');
} }
if (!empty($this->errors)) {
$this->errorResponse();
}
try { if (empty($this->errors)) {
if ($record = call_user_func($this->getRecordCallback, $this->args['identifier'], $this->args['metadataPrefix'])) { try {
if ($record = call_user_func($this->getRecordCallback, $this->args['identifier'], $this->args['metadataPrefix'])) {
$identifier = $record['identifier']; $identifier = $record['identifier'];
$datestamp = formatDatestamp($record['datestamp']); $datestamp = $this->formatDatestamp($record['datestamp']);
$set = $record['set']; $set = $record['set'];
$status_deleted = (isset($record['deleted']) && ($record['deleted'] == 'true') && $status_deleted = (isset($record['deleted']) && ($record['deleted'] == 'true') &&
(($this->identifyResponse['deletedRecord'] == 'transient') || (($this->identifyResponse['deletedRecord'] == 'transient') ||
($this->identifyResponse['deletedRecord'] == 'persistent'))); ($this->identifyResponse['deletedRecord'] == 'persistent')));
$cur_record = $this->response->addToVerbNode('record'); $cur_record = $this->response->addToVerbNode('record');
$cur_header = $this->response->createHeader($identifier, $datestamp, $set, $cur_record); $cur_header = $this->response->createHeader($identifier, $datestamp, $set, $cur_record);
if ($status_deleted) { if ($status_deleted) {
$cur_header->setAttribute("status","deleted"); $cur_header->setAttribute("status","deleted");
} else {
$this->add_metadata($cur_record, $record);
}
} else { } else {
$this->add_metadata($cur_record, $record); $this->errors[] = new OAI2Exception('idDoesNotExist', 'identifier', $identifier);
} }
} catch (OAI2Exception $e) {
$this->errors[] = $e;
} }
} catch (OAI2Exception $e) {
$this->errors[] = $e;
} }
} }
/** public function ListIdentifiers() {
* Response to Verb ListRecords return $this->ListRecords();
* }
* Lists records according to conditions. If there are too many, a resumptionToken is generated.
* - If a request comes with a resumptionToken and is still valid, read it and send back records. public function ListRecords() {
* - Otherwise, set up a query with conditions such as: 'metadataPrefix', 'from', 'until', 'set'.
* Only 'metadataPrefix' is compulsory. All conditions are accessible through global array variable <B>$args</B> by keywords. $maxItems = 1000;
*/ $deliveredRecords = 0;
public function listRecords() { $metadataPrefix = $this->args['metadataPrefix'];
$from = isset($this->args['from']) ? $this->args['from'] : '';
$until = isset($this->args['until']) ? $this->args['until'] : '';
$set = isset($this->args['set']) ? $this->args['set'] : '';
if (isset($this->args['resumptionToken'])) { if (isset($this->args['resumptionToken'])) {
if (count($this->args) > 1) { if (count($this->args) > 1) {
$this->errors[] = new OAI2Exception('exclusiveArgument'); $this->errors[] = new OAI2Exception('exclusiveArgument');
} else { } else {
if ((int)$val+TOKEN_VALID < time()) { if ((int)$val+$this->token_valid < time()) {
$this->errors[] = new OAI2Exception('badResumptionToken'); $this->errors[] = new OAI2Exception('badResumptionToken');
} else {
if (!file_exists($this->token_prefix.$this->args['resumptionToken'])) {
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']);
} else {
if ($readings = $this->readResumptionToken($this->token_prefix.$this->args['resumptionToken'])) {
list($deliveredRecords, $metadataPrefix, $from, $until, $set) = $readings;
} else {
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']);
}
}
} }
} }
} else { } else {
@ -260,99 +220,69 @@ class OAI2Server {
} }
} }
if (isset($this->args['from'])) { if (isset($this->args['from'])) {
if(!checkDateFormat($this->args['from'])) { if(!$this->checkDateFormat($this->args['from'])) {
$this->errors[] = new OAI2Exception('badGranularity', 'from', $this->args['from']); $this->errors[] = new OAI2Exception('badGranularity', 'from', $this->args['from']);
} }
} }
if (isset($this->args['until'])) { if (isset($this->args['until'])) {
if(!checkDateFormat($this->args['until'])) { if(!$this->checkDateFormat($this->args['until'])) {
$this->errors[] = new OAI2Exception('badGranularity', 'until', $this->args['until']); $this->errors[] = new OAI2Exception('badGranularity', 'until', $this->args['until']);
} }
} }
} }
if (!empty($this->errors)) { if (!empty($this->errors)) {
$this->errorResponse(); try {
}
// Resume previous session? $records_count = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, true);
if (isset($this->args['resumptionToken'])) {
if (!file_exists(TOKEN_PREFIX.$this->args['resumptionToken'])) { $records = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, false, $deliveredRecords, $maxItems);
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']);
} else {
if ($readings = $this->readResumptionToken(TOKEN_PREFIX.$this->args['resumptionToken'])) { foreach ($records as $record) {
list($deliveredRecords, $metadataPrefix, $from, $until, $set) = $readings;
} else { $identifier = $record['identifier'];
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']); $datestamp = $this->formatDatestamp($record['datestamp']);
$setspec = $record['set'];
$status_deleted = (isset($record['deleted']) && ($record['deleted'] === true) &&
(($this->identifyResponse['deletedRecord'] == 'transient') ||
($this->identifyResponse['deletedRecord'] == 'persistent')));
if($this->args['verb'] == 'ListRecords') {
$cur_record = $this->response->createToVerNode('record');
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec,$cur_record);
if (!$status_deleted) {
$this->add_metadata($cur_record, $record);
}
} else { // for ListIdentifiers, only identifiers will be returned.
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec);
}
if ($status_deleted) {
$cur_header->setAttribute("status","deleted");
}
} }
} // Will we need a new ResumptionToken?
if ($records_count - $deliveredRecords > $maxItems) {
if (!empty($this->errors)) { $deliveredRecords += $maxItems;
$this->errorResponse(); $restoken = $this->createResumptionToken($deliveredRecords);
}
} else { $expirationDatetime = gmstrftime('%Y-%m-%dT%TZ', time()+$this->token_valid);
$deliveredRecords = 0;
$metadataPrefix = $this->args['metadataPrefix'];
$from = isset($this->args['from']) ? $this->args['from'] : '';
$until = isset($this->args['until']) ? $this->args['until'] : '';
$set = isset($this->args['set']) ? $this->args['set'] : '';
}
$maxItems = 1000; } elseif (isset($args['resumptionToken'])) {
try { // Last delivery, return empty ResumptionToken
$restoken = null;
$records_count = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, true); $expirationDatetime = null;
$records = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, false, $deliveredRecords, $maxItems);
foreach ($records as $record) {
$identifier = $record['identifier'];
$datestamp = formatDatestamp($record['datestamp']);
$setspec = $record['set'];
$status_deleted = (isset($record['deleted']) && ($record['deleted'] === true) &&
(($this->identifyResponse['deletedRecord'] == 'transient') ||
($this->identifyResponse['deletedRecord'] == 'persistent')));
if($this->args['verb'] == 'ListRecords') {
$cur_record = $this->response->createToVerNode('record');
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec,$cur_record);
if (!$status_deleted) {
$this->add_metadata($cur_record, $record);
}
} else { // for ListIdentifiers, only identifiers will be returned.
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec);
} }
if ($status_deleted) {
$cur_header->setAttribute("status","deleted"); if (isset($restoken)) {
$this->response->createResumptionToken($restoken,$expirationDatetime,$records_count,$deliveredRecords);
} }
} catch (OAI2Exception $e) {
$this->errors[] = $e;
} }
// Will we need a new ResumptionToken?
if ($records_count - $deliveredRecords > $maxItems) {
$deliveredRecords += $maxItems;
$restoken = $this->createResumptionToken($deliveredRecords);
$expirationDatetime = gmstrftime('%Y-%m-%dT%TZ', time()+TOKEN_VALID);
} elseif (isset($args['resumptionToken'])) {
// Last delivery, return empty ResumptionToken
$restoken = null;
$expirationDatetime = null;
}
if (isset($restoken)) {
$this->response->createResumptionToken($restoken,$expirationDatetime,$records_count,$deliveredRecords);
}
} catch (OAI2Exception $e) {
$this->errors[] = $e;
} }
} }
@ -374,17 +304,17 @@ class OAI2Server {
list($usec, $sec) = explode(" ", microtime()); list($usec, $sec) = explode(" ", microtime());
$token = ((int)($usec*1000) + (int)($sec*1000)); $token = ((int)($usec*1000) + (int)($sec*1000));
$fp = fopen (TOKEN_PREFIX.$token, 'w'); $fp = fopen ($this->token_prefix.$token, 'w');
if($fp==false) { if($fp==false) {
exit("Cannot write. Writer permission needs to be changed."); exit("Cannot write. Writer permission needs to be changed.");
} }
fputs($fp, "$delivered_records#"); fputs($fp, "$delivered_records#");
fputs($fp, "$metadataPrefix#"); fputs($fp, "$metadataPrefix#");
fputs($fp, "{$this->args['from']}#"); fputs($fp, "{$this->args['from']}#");
fputs($fp, "{$this->args['until']}#"); fputs($fp, "{$this->args['until']}#");
fputs($fp, "{$this->args['set']}#"); fputs($fp, "{$this->args['set']}#");
fclose($fp); fclose($fp);
return $token; return $token;
} }
private function readResumptionToken($resumptionToken) { private function readResumptionToken($resumptionToken) {
@ -393,10 +323,33 @@ class OAI2Server {
if ($fp != false) { if ($fp != false) {
$filetext = fgets($fp, 255); $filetext = fgets($fp, 255);
$textparts = explode('#', $filetext); $textparts = explode('#', $filetext);
fclose($fp); fclose($fp);
unlink($resumptionToken); unlink($resumptionToken);
$rtVal = array_values($textparts); $rtVal = array_values($textparts);
} }
return $rtVal; return $rtVal;
}
/**
* All datestamps used in this system are GMT even
* return value from database has no TZ information
*/
private function formatDatestamp($datestamp) {
return date("Y-m-d\TH:i:s\Z",strtotime($datestamp));
}
/**
* The database uses datastamp without time-zone information.
* It needs to clean all time-zone informaion from time string and reformat it
*/
private function checkDateFormat($date) {
$date = str_replace(array("T","Z")," ",$date);
$time_val = strtotime($date);
if(!$time_val) return false;
if(strstr($date,":")) {
return date("Y-m-d H:i:s",$time_val);
} else {
return date("Y-m-d",$time_val);
}
} }
} }

View File

@ -27,6 +27,7 @@ class OAI2XMLResponse {
function display() { function display() {
$this->doc->formatOutput = true; $this->doc->formatOutput = true;
$this->doc->preserveWhiteSpace = false; $this->doc->preserveWhiteSpace = false;
header('Content-Type: text/xml');
echo $this->doc->saveXML(); echo $this->doc->saveXML();
} }

View File

@ -1,46 +0,0 @@
<?php
/**
* \file
* \brief Utilities for the OAI Data Provider
*
* A collection of functions used.
*/
/** Validates an identifier. The pattern is: '/^[-a-z\.0-9]+$/i' which means
* it accepts -, letters and numbers.
* Used only by function <B>oai_error</B> code idDoesNotExist.
* \param $url Type: string
*/
function is_valid_uri($url) {
return((bool)preg_match('/^[-a-z\.0-9]+$/i', $url));
}
/** Validates attributes come with the query.
* It accepts letters, numbers, ':', '_', '.' and -.
* Here there are few more match patterns than is_valid_uri(): ':_'.
* \param $attrb Type: string
*/
function is_valid_attrb($attrb) {
return preg_match("/^[_a-zA-Z0-9\-\:\.]+$/",$attrb);
}
/** All datestamps used in this system are GMT even
* return value from database has no TZ information
*/
function formatDatestamp($datestamp) {
return date("Y-m-d\TH:i:s\Z",strtotime($datestamp));
}
/** The database uses datastamp without time-zone information.
* It needs to clean all time-zone informaion from time string and reformat it
*/
function checkDateFormat($date) {
$date = str_replace(array("T","Z")," ",$date);
$time_val = strtotime($date);
if(!$time_val) return false;
if(strstr($date,":")) {
return date("Y-m-d H:i:s",$time_val);
} else {
return date("Y-m-d",$time_val);
}
}