commit 2aa9b58b076c5c6a6e9cbd9b6926be30312ab297
Author: Daniel Neis Araujo
Date: Tue May 7 14:48:37 2013 -0300
As downloaded from https://code.google.com/p/oai-pmh-2/
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
new file mode 100644
index 0000000..79e709e
--- /dev/null
+++ b/COPYRIGHT.txt
@@ -0,0 +1,14 @@
+Copyright (c) 2011 Jianfeng Li
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
diff --git a/ands_rifcs.php b/ands_rifcs.php
new file mode 100644
index 0000000..41625bd
--- /dev/null
+++ b/ands_rifcs.php
@@ -0,0 +1,260 @@
+oai_pmh = $ands_response_doc;
+ $this->working_node = $metadata_node;
+ $this->create_regObjects();
+ }
+
+ /**
+ * A worker function for easily adding a newly created node to current XML Doc.
+ * @param $mom_node Type: DOMElement. Node the new child will be attached to.
+ * @param $name Type: sting. The name of the child node is being added.
+ * @param $value Type: sting. The text content of the child node is being added. The default is ''.
+ * @return DOMElement. The added child node
+ */
+ protected function addChild($mom_node,$name, $value='') {
+ return $this->oai_pmh->addChild($mom_node,$name, $value);
+ }
+
+ /** Create a registryObjects node to hold individual registryObject's.
+ * This is only a holder node.
+ */
+ protected function create_regObjects() {
+ $this->working_node = $this->oai_pmh->addChild($this->working_node,'registryObjects');
+ $this->working_node->setAttribute('xmlns',"http://ands.org.au/standards/rif-cs/registryObjects");
+ $this->working_node->setAttribute('xmlns:xsi',"http://www.w3.org/2001/XMLSchema-instance");
+ $this->working_node->setAttribute('xsi:schemaLocation','http://ands.org.au/standards/rif-cs/registryObjects http://services.ands.org.au/documentation/rifcs/1.2.0/schema/registryObjects.xsd');
+ }
+
+ /** Create a single registryObject node. Each set has its own structure but they all have an attribute of group, a key node and an originatingSource node. The newly created node will be used as the working node.
+ *
+ * \param $group string, group attribute of the new registryObject node .
+ * \param $key string, key node, used as an identifier.
+ * \param $originatingSource string, an url of the data provider.
+ */
+ protected function create_regObject($group, $key, $originatingSource) {
+ $regObj_node = $this->addChild($this->working_node,'registryObject');
+ $regObj_node->setAttribute('group',$group);
+ $this->addChild($regObj_node,'key',$key);
+ $this->addChild($regObj_node,'originatingSource',$originatingSource);
+ $this->working_node = $regObj_node;
+ }
+
+ /** RIF-CS node is the content node of RIF-CS metadata node which starts from regObjects.
+ * Each set supportted in RIF-CS has its own content model. The created node will be used as the
+ * root node of this record for following nodes will be created.
+ *
+ * \param $set_name string, the name of set. For ANDS, they are Activity, Party and Collection
+ * \param $set_type string, the type of set. For example, Activity can have project as a type.
+ */
+ protected function create_rifcs_node($set_name, $set_type) {
+ $this->working_node = $this->addChild($this->working_node, $set_name);
+ $this->working_node->setAttribute('type', $set_type);
+ }
+
+ /**
+ * Create a top level name node.
+ * @param $name_type string. Text for the types, can be either primary or abbreviated. Default: primary
+ *
+ * @return DOMElement $added_name_node.
+ * The newly created node, it will be used for further expansion by adding namePart.
+ */
+ protected function create_name_node($name_type = 'primary') {
+ $c = $this->addChild($this->working_node, 'name');
+ $c->setAttribute('type', $name_type);
+ return $c;
+ }
+
+ /**
+ * Create a namePart of a name node.
+ * @param $name_node
+ * Type: DOMElement. Node of name_node created previously
+ *
+ * @param $value
+ * Type: string. Text fror this namePart
+ *
+ * @param $part_type Type: string, used for group:person record. Types can be: titile, given, family
+ *
+ */
+ protected function create_namePart($name_node, $value, $part_type = '') {
+ $c = $this->addChild($name_node, 'namePart', $value);
+ if (!empty($part_type)) {
+ $c->setAttribute('type', $part_type);
+ }
+ }
+
+ /** Create related object. One RIF-CS can have more than one related object nodes,
+ * each object is described by one node.
+ * \param $key
+ * Type: string. The identifier of the related object.
+ * \param $relation_type
+ * Type: string. Type of relationship.
+ *
+ */
+ protected function create_relatedObject($key,$relation_type) {
+ $c = $this->addChild($this->working_node, 'relatedObject');
+ $this->addChild($c,'key',$key);
+ $c = $this->addChild($c, 'relation');
+ // Mimick ANDS with enpty value to get both tags for relation. Only for better display
+ // $c = $this->addChild($c, 'relation',' ');
+ $c->setAttribute('type', $relation_type);
+ }
+
+ /** Create description node. One RIF-CS can have more than one description nodes.
+ * Each description node has only one description.
+ * \param $value Type: string. The content of the description.
+ * \param $des_type Type: string. Type of the description. Types can be brief, full, acessRights and note. Default is 'brief'.
+ */
+ protected function create_description_node($value, $des_type='brief') {
+ $c = $this->addChild($this->working_node, 'description', $value);
+ $c->setAttribute('type', $des_type);
+ }
+
+ /** Create local or other type of identifier inside of RIF-CS metadata node
+ * \param $key
+ * Type string. The indentifier itself.
+ * \param $i_type
+ * Type string. Type of identifier. Can be abn, uri, local, etc.. Default is local.
+ */
+ protected function create_identifier_node($key, $i_type='local') {
+ $c = $this->addChild($this->working_node, 'identifier',$key);
+ $c->setAttribute('type', $i_type);
+ }
+
+ /** Location node is a holder node for either address or spatial nodes
+ * \return DOMElement node, for adding address or spatial nodes.
+ */
+ protected function create_location_node() {
+ return $this->addChild($this->working_node, 'location');
+ }
+
+ /** Address node is a holder node for phiscal or electrical nodes.
+ * \param $location_node Type: DOMElement. Location node created previously.
+ * \return DOMElement
+ */
+ protected function create_address_node($location_node) {
+ return $this->addChild($location_node, 'address');
+ }
+
+ /** Electrical address node. Used for email, url, etc
+ * \param $addr_node Type: DOMElement. Previously created address node.
+ * \param $e_node Type: string. The content of the adding node.
+ * \param $e_type Type: string. Default is email.
+ */
+ protected function create_e_node($addr_node, $e_node, $e_type = 'email') {
+ $c = $this->addChild($addr_node, 'electronic');
+ $c->setAttribute('type', $e_type);
+ $this->addChild($c,'value',$e_node);
+ }
+
+ /** Physical node is a holder node for phone or fax nodes.
+ * \param $addr_node Type: DOMelement. Address node created before to which the new phiscial->addressPart will be attached.
+ * \param $number Type: string. Telephone or fax number as a string.
+ * \param $fone_fax Type: string. Either telehoneNumber or faxNumber.
+ */
+ protected function create_physcial_fone_fax($addr_node, $number,$fone_fax='telephoneNumber') {
+ $c = $this->addChild($addr_node, 'physical');
+ $c = $this->addChild($c, 'addressPart', $number);
+ $c->setAttribute('type', $fone_fax);
+ }
+
+ /** create address node under location node, either streetAddress or postalAddress.
+ * But they are in text (one block) format.
+ * \param $addr_node Type: DOMelement. Address node created before to which the new phiscial->addressPart will be attached.
+ * \param $txt_addr string, full street address in text block format
+ * \param $phys_type string, default is 'streetAddress', can be 'postalAddress'
+ */
+ protected function create_physcial_addr_txt($addr_node, $txt_addr,$phys_type='streetAddress') {
+ $c = $this->addChild($addr_node, 'physical');
+ $c->setAttribute('type', $phys_type);
+ $c = $this->addChild($c, 'addressPart', $txt_addr);
+ $c->setAttribute('type', 'text');
+ }
+
+ /** Create spatial node under a location node.
+ * \param $location_node Type: DOMElement. Location node where spatial node is being added to.
+ * \param $value Type: string. The value of spatial information. Default is local latitude and longitude.
+ * \param $sp_type Type: string. Type of spaitial informaion. Default is kmlPolyCoords.
+ */
+ protected function create_spatial_node($location_node, $value = '138.6396,-34.97063', $sp_type = 'kmlPolyCoords') {
+ $c = $this->addChild($location_node, 'spatial',$value);
+ $c->setAttribute('type',$sp_type);
+ }
+
+ /** Create temporal coverage node for collection or activity records.
+ * \param $values Type: 2-D array. The values of temporal coverage. It can has maximal two elements: one from 'dateFrom' and another for 'dateTo'.
+ * Either can be ommited according to RIF-CS schema. Each element of $values is an array and has keys: date, type and format.
+ * ['date'] is a string represents date. It has to be in W3CDTF or UTC format.
+ * ['type'] has to be either 'dateFrom' or 'dateTo'.
+ * ['format'] is optional and its default is 'W3CDTF'. UTC format requires date has to be in UTC: dateTtimeZ.
+ * It throws an exception if the input parameter is not an array.
+ */
+ protected function create_coverage_tempo($values) {
+ // Non array is not acceptable.
+ if (!is_array($values)) { throw new Exception('The input of temporal coverage has to be an array of arraies with keys.');}
+ $c = $this->addChild($this->working_node,'coverage');
+ $t = $this->addChild($c,'temporal');
+ foreach($values as $value) $this->create_coverage_tempo_date($t, $value);
+ }
+
+ /** Create temporal coverage node for collection or activity records.
+ * \param $t Type: DOMElement. The \\\ node to which \ nodes will be attached to.
+ * \param $value Type: array. The value of temporal coverage. It has maxmimal three elements with keys: type, date and format.
+ * It throws an exception if the input parameter is not an array.
+ * \see create_coverage_tempo
+ */
+ private function create_coverage_tempo_date($t, $value) {
+ if (!is_array($value)) { throw new Exception('The input of temporal coverage has to be an array with keys.');}
+ $d = $this->addChild($t,'date',$value['date']);
+ $d->setAttribute('type',$value['type']);
+ if (isset($value['format'])) $d->setAttribute('dateFormat',$value['format']);
+ else $d->setAttribute('dateFormat','W3CDTF');
+ }
+
+ /** Create a subject node for a researcher, project, project, etc
+ * \param $value Type: string. A string representing the new namePart.
+ * \param $subject_type Type: string. A string representing the type of subject. The default value is anzsrc-for.
+ */
+ protected function create_subject_node($value, $subject_type = 'anzsrc-for') {
+ if (empty($value)) return;
+ $c = $this->addChild($this->working_node,'subject',$value);
+ $c->setAttribute('type',$subject_type);
+ }
+} // end of class ANDS_RIFCS
+
diff --git a/ands_tpa.php b/ands_tpa.php
new file mode 100644
index 0000000..e86c031
--- /dev/null
+++ b/ands_tpa.php
@@ -0,0 +1,349 @@
+create_metadata($cur_record);
+ * $obj_node = new ANDS_TPA($outputObj, $metadata_node, $db);
+ * try {
+ * $obj_node->create_obj_node($record[$SQL['set']], $identifier);
+ * } catch (Exception $e) {
+ * echo 'Caught exception: ', $e->getMessage(), " when adding $identifier\n";
+ * }
+ * \endcode
+ * \see Code in action can be seen in record_rif.php
+ */
+
+class ANDS_TPA extends ANDS_RIFCS {
+ //! Type: PDO. The database connection of the data source.
+ //! \see __construct.
+ private $db;
+
+ /**
+ * Constructor
+ * The first two parameters are used by its parent class ANDS_RIFCS. The third is its own private property.
+ *
+ * \param $ands_response_doc ANDS_Response_XML. A XML Doc acts as the parent node.
+ * \param $metadata_node DOMElement. The meta node which all subsequent nodes will be added to.
+ * \param $db Type: PDO. The database connection of the data source.
+ */
+ function __construct($ands_response_doc, $metadata_node, $db) {
+ parent::__construct($ands_response_doc, $metadata_node);
+ $this->db = $db;
+ }
+
+
+ /**
+ * This is the general entrence of creating actual content. It calls different functions for different type of RIF-CS model.
+ * When anything goes wrong, e.g. found no record, or $set_name is not recognised, an exception will be thrown.
+ * And for this implementation, data are stored in a database therefore a PDO is needed. But the source can be any.
+ *
+ * \param $set_name Type: string. The name of set is going to be created. Can be one of activity, collection or party.
+ * \param $key Type: string. The main identifier used in ANDS system. There can be other identifier.
+ *
+ * \see create_activity, create_collection, create_party
+ */
+ function create_obj_node($set_name, $key) {
+ $db = $this->db;
+ $set_name = strtolower($set_name);
+ if (in_array($set_name,prepare_set_names())) {
+ try {
+ // Get ori_id and which the original table is:
+ $query = "select ori_table_name, ori_id from oai_headers where oai_identifier = '".$key."'";
+ $res = exec_pdo_query($db, $query);
+ $record = $res->fetch(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ echo "$key returned no record.\n";
+ echo $e->getMessage();
+ }
+
+ $processor = 'create_'.substr($set_name,6);
+ $this->create_regObject(REG_OBJ_GROUP, $key, MY_URI);
+ $this->$processor($record['ori_table_name'],$record['ori_id']);
+ $this->create_identifier_node($key);
+ $this->create_identifier_node('table='.$record['ori_table_name'].'+id='.$record['ori_id']);
+ } else {
+ throw new Exception('Wrong set name was used: '.$set_name);
+ }
+ }
+
+ /** The processor for creating metadata node of Activity. Called from create_obj_node.
+ * \param $table_name Type: string. The table name will be used to retrieve data from.
+ * \param $id_project Type: integer. Internal project id associated to this activity-project.
+ * \see Function create_obj_node.
+ */
+ private function create_activity($table_name, $id_project) {
+ $db = $this->db;
+# // Get ori_id and which the original table is:
+# $query = "select ori_table_name, ori_id from oai_headers where oai_identifier = '".$key."'";
+# $res = exec_pdo_query($db, $query);
+# $record = $res->fetch(PDO::FETCH_ASSOC);
+# // $id_project will e used later, so save it:
+# $id_project = $record['ori_id'];
+ // Get the content using the previously obtained infor:
+ $query = sprintf("select inter_no,start_date, end_date,pub_descrip from %s where id_project = %s",$table_name,$id_project);
+
+ try {
+ $res = exec_pdo_query($db,$query);
+ $record = $res->fetch(PDO::FETCH_ASSOC);
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ }
+
+ $this->create_rifcs_node('activity','project');
+ $c = $this->create_name_node();
+ $this->create_namePart($c,'The Plant Accelerator Project '.$record['inter_no']);
+// Test codes for rich format.
+# // \n works
+# $this->create_description_node(sprintf("Line one:%s,\nLine two:%s.\nThird",'a','b'));
+ $this->create_description_node(str_replace("\r\n","\n",$record['pub_descrip']));
+
+ $this->create_description_node('The experiment was carried out between '.$record['start_date'].' and '.$record['end_date'],'note');
+ $query = sprintf("select idr,stype from list_prj_ids_v2(%d) where stype in ('dataset','person')",$id_project);
+ // echo $query;
+ try {
+ $res = $db->query($query,PDO::FETCH_ASSOC);
+ if ($res==false) {
+ throw new Exception($query."\nIt found nothing.\n");
+ }
+ foreach ($res as $record) {
+ switch ($record['stype']) {
+ case 'dataset':
+ $this->create_relatedObject($record['idr'],'hasOutput');
+ break;
+ case 'person':
+ $this->create_relatedObject($record['idr'],'isManagedBy');
+ break;
+ }
+ }
+ // The Plant Accelerator always participates in Activity
+ $this->create_relatedObject('0874ad60-ab4d-11df-aebd-0002a5d5c51b','hasParticipant');
+ } catch (PDOException $e) {
+ process_pdo_error($query, $e);
+ }// end of try-catch block
+ } // end of function create_activity($key, $id_project)
+
+ /** The processor for creating metadata node of Collection. Called from create_obj_node.
+ * \param $table_name Type: string. The table name will be used to retrieve data from.
+ * \param $id_collect Type: integer. Internal collection id associated to this collection-dataset.
+ * \see Function create_obj_node.
+ */
+ private function create_collection($table_name, $id_collect) {
+ $db = $this->db;
+ try {
+ $query = sprintf("select plant,variety,start_date,end_date,img_freq,anzsrc from %s where id_collect = %s",$table_name,$id_collect);
+ $res = exec_pdo_query($db, $query);
+ $dataset = $res->fetch(PDO::FETCH_ASSOC);
+
+ $res = exec_pdo_query($db, $query);
+ $record = $res->fetch(PDO::FETCH_ASSOC);
+
+ $query = 'select id_rep, inter_no, id_project from tpa_project_ids where id_collect = '.$id_collect;
+ $res = exec_pdo_query($db, $query);
+ $prj_info = $res->fetch(PDO::FETCH_ASSOC);
+
+ $query = 'select email from tpa_person where id_rep = '.$prj_info['id_rep'];
+ $res = exec_pdo_query($db, $query);
+ $email = $res->fetch(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ echo $query.' was failed\n';
+ echo $e->getMessage();
+ }
+
+ $this->create_rifcs_node('collection','dataset');
+ // Get the project inter_no as the name of this dataset
+ $c = $this->create_name_node();
+ $this->create_namePart($c,'Data set of Plant Accelerator Project '.$prj_info['inter_no']);
+
+ // locatin node: contact person
+ $l_node = $this->create_location_node();
+ $a_node = $this->create_address_node($l_node);
+ $this->create_e_node($a_node, $email['email']);
+ // location node: TPA's physical address
+ $l_node = $this->create_location_node();
+ $a_node = $this->create_address_node($l_node);
+ $this->create_physcial_addr_txt($a_node, 'The Plant Accelerator, Hartley Grove, Urrbrae, SA 5064') ;
+ // Temporal coverage of colletion
+ $dates = array(array('date'=>$dataset['start_date'],'type'=>'dateFrom'),array('date'=>$dataset['end_date'],'type'=>'dateTo'));
+ $this->create_coverage_tempo($dates);
+ // subject
+ $this->create_subject_node($dataset['aznsrc']);
+ // relatedOjbects
+ $query = sprintf("select idr,stype from list_prj_ids_v2(%d) where stype in ('project','person')",$prj_info['id_project']);
+ try {
+ $res = $db->query($query,PDO::FETCH_ASSOC);
+ if ($res==false) {
+ throw new Exception($query."\nIt found nothing.\n");
+ }
+ foreach ($res as $record) {
+ switch ($record['stype']) {
+ case 'project':
+ $this->create_relatedObject($record['idr'],'isOutputOf');
+ break;
+ case 'person':
+ $this->create_relatedObject($record['idr'],'isOwnedBy');
+ break;
+ }
+ }
+ } catch (PDOException $e) {
+ process_pdo_error($query, $e);
+ }// end of try-catch block
+
+ // right of accessing
+ $this->create_description_node('For information on rights and access to this dataset, please contact the owner.','accessRights');
+
+ // image data:
+ $imgs = ''; $ex_conf = '';
+ $dic = array('im_type_rgb'=>'RGB','im_type_nir'=>'NIR','im_type_fir'=>'FIR','im_type_nir_roots'=>'NIR Roots','im_type_fluo'=>'Fluorescence');
+ $query = 'select im_type_rgb,im_type_nir,im_type_fir,im_type_nir_roots,im_type_fluo, lines, treatments, replicates, total from ands_collection where id_collect = '. $id_collect;
+ $res = $db->query($query,PDO::FETCH_ASSOC);
+ if ($res==false) {
+ throw new Exception($query."\nIt found nothing.\n");
+ }
+ $info = $res->fetch();
+ foreach ($info as $item => $v) {
+ switch ($item) {
+ case 'im_type_rgb':
+ case 'im_type_nir':
+ case 'im_type_fir':
+ case 'im_type_nir_roots':
+ case 'im_type_fluo':
+ if (!empty($v)) { $imgs .= $dic[$item].', '; }
+ break;
+ default:
+ if (!empty($v)) { $ex_conf .= ' '.$item.' = '.$v.', '; }
+ break;
+ }
+ }
+ if (empty($imgs)) $imgs = "Images data of RGB, FIR, NIR, NIR Roots and Fluorescence cameras., ";
+ $imgs = substr($imgs,0,-2);
+ if (!empty($ex_conf)) $imgs = $imgs."\n".substr($ex_conf,0,-2);
+ $this->create_description_node($imgs);
+ // imaging frequency
+ $this->create_description_node('Imaging frequency: '.$dataset['img_freq'],'note');
+ } // end of function create_collection($key,$id_collect)
+
+ /** The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+ * \param $table_name Type: string. The table name will be used to retrieve data from.
+ * \param $id_party Type: integer. Internal party id associated to this party.
+ * \see Function create_obj_node.
+ */
+ private function create_party($table_name, $id_party) {
+ $db = $this->db;
+ $query = sprintf("SELECT set_type FROM oai_headers WHERE ori_table_name = '%s' AND ori_id = %s",$table_name,$id_party);
+ $res = exec_pdo_query($db, $query);
+ $party_type = $res->fetch(PDO::FETCH_ASSOC);
+
+ if (in_array($party_type['set_type'],array('person','group'))) {
+ $this->create_rifcs_node('party',$party_type['set_type']);
+
+ if ($party_type['set_type']=='person') {
+ $this->create_person($table_name, $id_party);
+ } elseif ($party_type['set_type']=='group') {
+ $this->create_group($table_name, $id_party); }
+ } else {
+ throw new Exception('Unsupported set_type: '.$party_type['set_type']);
+ }
+ } // end of function create_part($key,$id_party)
+
+ /** The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+ * \param $table_name Type: string. The table name will be used to retrieve data from.
+ * \param $id_party Type: integer. Internal party id associated to this party-person.
+ * \see Function create_party.
+ */
+ private function create_person($table_name, $id_party) {
+ $db = $this->db;
+ $query = sprintf("SELECT id_org, title, first_name, family_name, tel, fax, email, www, address, post_code, city,state,country,duty FROM %s WHERE id_rep = %s",$table_name, $id_party);
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_ASSOC);
+ $c = $this->create_name_node();
+ if (!empty($info['title'])) $this->create_namePart($c,$info['title'],'title');
+ $this->create_namePart($c,$info['family_name'],'family');
+ $this->create_namePart($c,$info['first_name'],'given');
+
+ // locatin node: contact person
+ $l_node = $this->create_location_node();
+ $a_node = $this->create_address_node($l_node);
+ $this->create_e_node($a_node, $info['email']);
+ if (!empty($info['www'])) $this->create_e_node($a_node, $info['www'],'url');
+ $this->create_physcial_fone_fax($a_node, $info['tel'],'telephoneNumber');
+ if (!empty($info['fax'])) $this->create_physcial_fone_fax($a_node, $info['fax'],'faxNumber');
+ $add_txt = trim($info['address']).', '.$info['city'].' '.$info['state'].' '.$info['post_code'].', '.$info['country'];
+ // the strlength of ', , ' is 6
+ if (strlen($add_txt)>6) $this->create_physcial_addr_txt($a_node,$add_txt);
+
+ // related objects:
+ // their group: id_customer is a foreign key of tpa_organisation
+ $query = sprintf("SELECT get_identifier('tpa_organisation',%s)",$info['id_org']);
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_NUM);
+ $this->create_relatedObject($info[0],'isMemberOf');
+
+ // their activities
+ $query = "SELECT list_persons_objs($id_party,'project')";
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_NUM);
+ foreach ($info as $item) {
+ $this->create_relatedObject($item,'isManagerOf');
+ }
+ // their collections
+ $query = "SELECT list_persons_objs($id_party,'dataset')";
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_NUM);
+ foreach ($info as $item) {
+ $this->create_relatedObject($item,'isOwnerOf');
+ }
+ }
+
+ /** The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+ * \param $table_name Type: string. The table name will be used to retrieve data from.
+ * \param $id_party Type: integer. Internal party id associated to this party-group.
+ * \see Function create_party.
+ */
+ private function create_group($table_name, $id_party) {
+ $db = $this->db;
+ // echo 'table: ',$table_name,' party: ',$id_party,"\n";
+ $query = sprintf("SELECT customer_name, abn, post_code, address, city, state, country, tel, fax, email, www, description FROM %s WHERE id_org = %s",$table_name, $id_party);
+ //echo $query;
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_ASSOC);
+ $c = $this->create_name_node();
+ $this->create_namePart($c,$info['customer_name']);
+ if (!empty($info['abn'])) $this->create_identifier_node($info['abn'],'abn');
+
+ if (!empty($info['description'])) $this->create_description_node($info['description']);
+
+ $l_node = $this->create_location_node();
+ $a_node = $this->create_address_node($l_node);
+ $this->create_physcial_fone_fax($a_node, $info['tel'],'telephoneNumber');
+ $this->create_physcial_fone_fax($a_node, $info['fax'],'faxNumber');
+ $add_txt = trim($info['address']).', '.$info['city'].' '.$info['state'].' '.$info['post_code'].', '.$info['country'];
+ $this->create_physcial_addr_txt($a_node,$add_txt);
+
+ // related objects:
+ // their members:
+ $query = "SELECT list_pub_members($id_party)";
+ $res = exec_pdo_query($db, $query);
+ $info = $res->fetch(PDO::FETCH_NUM);
+ foreach ($info as $item) {
+ $this->create_relatedObject($item,'hasMember');
+ }
+ }
+} // end of class ANDS_TPA
+
diff --git a/doc/ands__rifcs_8php.html b/doc/ands__rifcs_8php.html
new file mode 100644
index 0000000..8ba6b63
--- /dev/null
+++ b/doc/ands__rifcs_8php.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+OAI PHP: ands_rifcs.php File Reference
+
+
+
+
+
+
+
+
+
Classes related to generating RIF-CS XML response file for ANDS from repository. It also serves as an exmaple how class ANDS_RIFCS can be used in a particular case.
+More...
Class ANDS_RIFCS provides all essential functionalities for creating ANDS RIF-CS records. The protected member functions are the backbone functions which can be used for creating any ANDS RIF-CS records.
DOMElement. The meta node which all subsequent nodes will be added to.
+
+
+
+
+
+
+
Member Function Documentation
+
+
+
+
+
+
ANDS_RIFCS::addChild
+
(
+
$
+
mom_node,
+
+
+
+
+
$
+
name,
+
+
+
+
+
$
+
value = ''
+
+
+
+
)
+
[protected]
+
+
+
+
+
A worker function for easily adding a newly created node to current XML Doc.
+
Parameters:
+
+
$mom_node
Type: DOMElement. Node the new child will be attached to.
+
$name
Type: sting. The name of the child node is being added.
+
$value
Type: sting. The text content of the child node is being added. The default is ''.
+
+
+
+
Returns:
DOMElement. The added child node
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_regObjects
+
(
+
+
)
+
[protected]
+
+
+
+
+
Create a registryObjects node to hold individual registryObject's. This is only a holder node.
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_regObject
+
(
+
$
+
group,
+
+
+
+
+
$
+
key,
+
+
+
+
+
$
+
originatingSource
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create a single registryObject node. Each set has its own structure but they all have an attribute of group, a key node and an originatingSource node. The newly created node will be used as the working node.
+
Parameters:
+
+
$group
string, group attribute of the new registryObject node .
+
$key
string, key node, used as an identifier.
+
$originatingSource
string, an url of the data provider.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_rifcs_node
+
(
+
$
+
set_name,
+
+
+
+
+
$
+
set_type
+
+
+
+
)
+
[protected]
+
+
+
+
+
RIF-CS node is the content node of RIF-CS metadata node which starts from regObjects. Each set supportted in RIF-CS has its own content model. The created node will be used as the root node of this record for following nodes will be created.
+
Parameters:
+
+
$set_name
string, the name of set. For ANDS, they are Activity, Party and Collection
+
$set_type
string, the type of set. For example, Activity can have project as a type.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_name_node
+
(
+
$
+
name_type = 'primary'
+
)
+
[protected]
+
+
+
+
+
Create a top level name node.
+
Parameters:
+
+
$name_type
string. Text for the types, can be either primary or abbreviated. Default: primary
+
+
+
+
Returns:
DOMElement $added_name_node. The newly created node, it will be used for further expansion by adding namePart.
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_namePart
+
(
+
$
+
name_node,
+
+
+
+
+
$
+
value,
+
+
+
+
+
$
+
part_type = ''
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create a namePart of a name node.
+
Parameters:
+
+
$name_node
Type: DOMElement. Node of name_node created previously
+
$value
Type: string. Text fror this namePart
+
$part_type
Type: string, used for group:person record. Types can be: titile, given, family
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_relatedObject
+
(
+
$
+
key,
+
+
+
+
+
$
+
relation_type
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create related object. One RIF-CS can have more than one related object nodes, each object is described by one node.
+
Parameters:
+
+
$key
Type: string. The identifier of the related object.
+
$relation_type
Type: string. Type of relationship.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_description_node
+
(
+
$
+
value,
+
+
+
+
+
$
+
des_type = 'brief'
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create description node. One RIF-CS can have more than one description nodes. Each description node has only one description.
+
Parameters:
+
+
$value
Type: string. The content of the description.
+
$des_type
Type: string. Type of the description. Types can be brief, full, acessRights and note. Default is 'brief'.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_identifier_node
+
(
+
$
+
key,
+
+
+
+
+
$
+
i_type = 'local'
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create local or other type of identifier inside of RIF-CS metadata node
+
Parameters:
+
+
$key
Type string. The indentifier itself.
+
$i_type
Type string. Type of identifier. Can be abn, uri, local, etc.. Default is local.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_location_node
+
(
+
+
)
+
[protected]
+
+
+
+
+
Location node is a holder node for either address or spatial nodes
+
Returns:
DOMElement node, for adding address or spatial nodes.
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_address_node
+
(
+
$
+
location_node
+
)
+
[protected]
+
+
+
+
+
Address node is a holder node for phiscal or electrical nodes.
+
Parameters:
+
+
$location_node
Type: DOMElement. Location node created previously.
+
+
+
+
Returns:
DOMElement
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_e_node
+
(
+
$
+
addr_node,
+
+
+
+
+
$
+
e_node,
+
+
+
+
+
$
+
e_type = 'email'
+
+
+
+
)
+
[protected]
+
+
+
+
+
Electrical address node. Used for email, url, etc
+
Parameters:
+
+
$addr_node
Type: DOMElement. Previously created address node.
+
$e_node
Type: string. The content of the adding node.
+
$e_type
Type: string. Default is email.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_physcial_fone_fax
+
(
+
$
+
addr_node,
+
+
+
+
+
$
+
number,
+
+
+
+
+
$
+
fone_fax = 'telephoneNumber'
+
+
+
+
)
+
[protected]
+
+
+
+
+
Physical node is a holder node for phone or fax nodes.
+
Parameters:
+
+
$addr_node
Type: DOMelement. Address node created before to which the new phiscial->addressPart will be attached.
+
$number
Type: string. Telephone or fax number as a string.
+
$fone_fax
Type: string. Either telehoneNumber or faxNumber.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_physcial_addr_txt
+
(
+
$
+
addr_node,
+
+
+
+
+
$
+
txt_addr,
+
+
+
+
+
$
+
phys_type = 'streetAddress'
+
+
+
+
)
+
[protected]
+
+
+
+
+
create address node under location node, either streetAddress or postalAddress. But they are in text (one block) format.
+
Parameters:
+
+
$addr_node
Type: DOMelement. Address node created before to which the new phiscial->addressPart will be attached.
+
$txt_addr
string, full street address in text block format
+
$phys_type
string, default is 'streetAddress', can be 'postalAddress'
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_spatial_node
+
(
+
$
+
location_node,
+
+
+
+
+
$
+
value = '138.6396,
+
+
+
+
+
-34.97063'
+
,
+
+
+
+
+
$
+
sp_type = 'kmlPolyCoords'
+
+
+
+
)
+
[protected]
+
+
+
+
+
Create spatial node under a location node.
+
Parameters:
+
+
$location_node
Type: DOMElement. Location node where spatial node is being added to.
+
$value
Type: string. The value of spatial information. Default is local latitude and longitude.
+
$sp_type
Type: string. Type of spaitial informaion. Default is kmlPolyCoords.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_coverage_tempo
+
(
+
$
+
values
+
)
+
[protected]
+
+
+
+
+
Create temporal coverage node for collection or activity records.
+
Parameters:
+
+
$values
Type: 2-D array. The values of temporal coverage. It can has maximal two elements: one from 'dateFrom' and another for 'dateTo'. Either can be ommited according to RIF-CS schema. Each element of $values is an array and has keys: date, type and format. ['date'] is a string represents date. It has to be in W3CDTF or UTC format. ['type'] has to be either 'dateFrom' or 'dateTo'. ['format'] is optional and its default is 'W3CDTF'. UTC format requires date has to be in UTC: dateTtimeZ. It throws an exception if the input parameter is not an array.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_RIFCS::create_coverage_tempo_date
+
(
+
$
+
t,
+
+
+
+
+
$
+
value
+
+
+
+
)
+
[private]
+
+
+
+
+
Create temporal coverage node for collection or activity records.
+
Parameters:
+
+
$t
Type: DOMElement. The <coverage><temporal/></coverage> node to which <date> nodes will be attached to.
+
$value
Type: array. The value of temporal coverage. It has maxmimal three elements with keys: type, date and format. It throws an exception if the input parameter is not an array.
Create an empty <record> node. Other nodes will be appended to it later.
+
+
+
+
+
+
+
+
+
ANDS_Response_XML::create_header
+
(
+
$
+
identifier,
+
+
+
+
+
$
+
timestamp,
+
+
+
+
+
$
+
ands_class,
+
+
+
+
+
$
+
add_to_node = null
+
+
+
+
)
+
+
+
+
+
+
Headers are enclosed inside of <record> to the query of ListRecords, ListIdentifiers and etc.
+
Parameters:
+
+
$identifier
Type: string. The identifier string for node <identifier>.
+
$timestamp
Type: timestamp. Timestapme in UTC format for node <datastamp>.
+
$ands_class
Type: mix. Can be an array or just a string. Content of <setSpec>.
+
$add_to_node
Type: DOMElement. Default value is null. In normal cases, $add_to_node is the <record> node created previously. When it is null, the newly created header node is attatched to $this->verbNode. Otherwise it will be attatched to the desired node defined in $add_to_node.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_Response_XML::create_metadata
+
(
+
$
+
mom_record_node
+
)
+
+
+
+
+
+
Create metadata node for holding metadata. This is always added to <record> node.
+
Parameters:
+
+
$mom_record_node
DOMElement. A node acts as the parent node.
+
+
+
+
Returns:
$meta_node Type: DOMElement. The newly created registryObject node which will be used for further expansion. metadata node itself is maintained by internally by the Class.
+
+
+
+
+
+
+
+
+
ANDS_Response_XML::create_resumpToken
+
(
+
$
+
token,
+
+
+
+
+
$
+
expirationdatetime,
+
+
+
+
+
$
+
num_rows,
+
+
+
+
+
$
+
cursor = null
+
+
+
+
)
+
+
+
+
+
+
If there are too many records request could not finished a resumpToken is generated to let harvester know
+
Parameters:
+
+
$token
Type: string. A random number created somewhere?
+
$expirationdatetime
Type: string. A string representing time.
+
$num_rows
Type: integer. Number of records retrieved.
+
$cursor
Type: string. Cursor can be used for database to retrieve next time.
+
+
+
+
+
+
+
Member Data Documentation
+
+
+
+
+
+
ANDS_Response_XML::$verbNode
+
+
+
+
+
Type: DOMElement. Verb node itself.
+
+
+
+
+
+
+
+
+
ANDS_Response_XML::$verb [protected]
+
+
+
+
+
Type: string. The verb in the request
+
+
+
+The documentation for this class was generated from the following file:
Class ANDS_RIFCS provides all essential functionalities for creating ANDS RIF-CS records. The protected member functions are the backbone functions which can be used for creating any ANDS RIF-CS records. At the time of design only data source is database and there is only one set of outputs. Therefore there is only one class has been designed. Ideally, there should be a separated class for creating actual records which reflect data source and data models.
+
Example usage: publish records meet ANDS RIF-CS requirements
DOMElement. The meta node which all subsequent nodes will be added to.
+
$db
Type: PDO. The database connection of the data source.
+
+
+
+
+
+
+
Member Function Documentation
+
+
+
+
+
+
ANDS_TPA::create_obj_node
+
(
+
$
+
set_name,
+
+
+
+
+
$
+
key
+
+
+
+
)
+
+
+
+
+
+
This is the general entrence of creating actual content. It calls different functions for different type of RIF-CS model. When anything goes wrong, e.g. found no record, or $set_name is not recognised, an exception will be thrown. And for this implementation, data are stored in a database therefore a PDO is needed. But the source can be any.
+
Parameters:
+
+
$set_name
Type: string. The name of set is going to be created. Can be one of activity, collection or party.
+
$key
Type: string. The main identifier used in ANDS system. There can be other identifier.
The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+
Parameters:
+
+
$table_name
Type: string. The table name will be used to retrieve data from.
+
$id_party
Type: integer. Internal party id associated to this party.
The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+
Parameters:
+
+
$table_name
Type: string. The table name will be used to retrieve data from.
+
$id_party
Type: integer. Internal party id associated to this party-person.
The processor for creating metadata node of Party. Called from create_obj_node. As party-person is different to party-group, there are two sub-functions are called accordingly.
+
Parameters:
+
+
$table_name
Type: string. The table name will be used to retrieve data from.
+
$id_party
Type: integer. Internal party id associated to this party-group.
Add a child node to a parent node on a XML Doc: a worker function.
+
Parameters:
+
+
$mom_node
Type: DOMNode. The target node.
+
$name
Type: string. The name of child nade is being added
+
$value
Type: string. Text for the adding node if it is a text node.
+
+
+
+
Returns:
DOMElement $added_node The newly created node, can be used for further expansion. If no further expansion is expected, return value can be igored.
+
+
+
+
+
+
+
+
+
ANDS_XML::create_request
+
(
+
$
+
par_array
+
)
+
+
+
+
+
+
Create an OAI request node.
+
Parameters:
+
+
$par_array
Type: array The attributes of a request node. They describe the verb of the request and other associated parameters used in the request. Keys of the array define attributes, and values are their content.
+
+
+
+
+
+
+
+
+
+
+
+
ANDS_XML::display
+
(
+
+
)
+
+
+
+
+
+
Display a doc in a readable, well-formatted way for display or saving
+
+
+
+
Member Data Documentation
+
+
+
+
+
+
ANDS_XML::$doc
+
+
+
+
+
Type: DOMDocument. Handle of current XML Document object
+
+
+
+The documentation for this class was generated from the following file:
This class is used to construct database connection to a PostgreSQL database. It also includes some basic operation to database. This can be a template for future development of connecting to other databases
+
Constructor & Destructor Documentation
+
+
+
+
+
+
DB_OP::__construct
+
(
+
$
+
params
+
)
+
+
+
+
+
+
Constructor. Accepts one parameter containing the database connection settings.
+
Parameters:
+
+
array
+
+
+
+
+
+
+
Member Function Documentation
+
+
+
+
+
+
DB_OP::initialize
+
(
+
+
)
+
[private]
+
+
+
+
+
Initialize Database Settings. Called by the constructor.
+
Parameters:
+
+
void
+
+
+
+
Returns:
boolean. TRUE for successful initialisation. FALSE otherwise.
+
+
+
+
+
+
+
+
+
DB_OP::_connect_string
+
(
+
+
)
+
[private]
+
+
+
+
+
Construct connection string
+
Returns:
string
+
+
+
+
+
+
+
+
+
DB_OP::db_connect
+
(
+
+
)
+
[private]
+
+
+
+
+
Non-persistent database connection
+
Returns:
resource
+
+
+
+
+
+
+
+
+
DB_OP::db_pconnect
+
(
+
+
)
+
[private]
+
+
+
+
+
Persistent database connection
+
Returns:
resource
+
+
+
+
+
+
+
+
+
DB_OP::reconnect
+
(
+
+
)
+
+
+
+
+
+
Reconnect
+
Keep / reestablish the db connection if no queries have been sent for a length of time exceeding the server's idle timeout
+
Returns:
void
+
+
+
+
+
+
+
+
+
DB_OP::affected_rows
+
(
+
+
)
+
+
+
+
+
+
Affected Rows
+
Returns:
integer
+
+
+
+
+
+
+
+
+
DB_OP::select
+
(
+
$
+
table,
+
+
+
+
+
$
+
select = array('*')
+
+
+
+
)
+
+
+
+
+
+
Generates the SELECT portion of the query when multiple fileds are needed
+
Parameters:
+
+
$table
Type: string.
+
$select
Type: array.
+
+
+
+
Returns:
string
+
+
+
+
+
+
+
+
+
DB_OP::simple_query
+
(
+
$
+
sql
+
)
+
+
+
+
+
+
Simple Query This function is used for client script will process result. Or running transaction commands since they do not require all the features of the main query() function.
+
Parameters:
+
+
string
the sql query
+
+
+
+
Returns:
resource
+
+
+
+
+
+
+
+
+
DB_OP::query
+
(
+
$
+
sql,
+
+
+
+
+
$
+
return_object = FALSE
+
+
+
+
)
+
+
+
+
+
+
Execute the query
+
Accepts an SQL string as input and returns a result object upon successful execution of a "read" type query. Returns boolean TRUE upon successful execution of a "write" type query. Returns boolean FALSE upon failure.
+
Parameters:
+
+
string
An SQL query string
+
array
An array of binding data
+
+
+
+
Returns:
mixed
+
+
+
+
+
+
+
+
+
DB_OP::load_rdriver
+
(
+
+
)
+
+
+
+
+
+
Load the result drivers
+
Returns:
string the name of the result class
+
+
+
+
+
+
+
+
+
DB_OP::close
+
(
+
+
)
+
+
+
+
+
+
Close database connection
+
Returns:
void
+
+
+
+
+
+
+
+
+
DB_OP::_error_message
+
(
+
+
)
+
[private]
+
+
+
+
+
The error message string
+
Returns:
string
+
+
+
+
+
+
+
+
+
DB_OP::display_error
+
(
+
$
+
error = '',
+
+
+
+
+
$
+
swap = '',
+
+
+
+
+
$
+
native = FALSE
+
+
+
+
)
+
+
+
+
+
+
Display an error message in some format
+
Parameters:
+
+
string
the error message
+
string
any "swap" values
+
boolean
whether to localize the message
+
+
+
+
Returns:
string sends the application/error_db.php template
+
+
+
+The documentation for this class was generated from the following file:
Support to different metadataformats in your own systems. Two examples provided with the package are: record_dc.php and record_rif.php. They are helpers and need information from the real records. They need to be devloped for a particular system.
Support to different metadataformats in your own systems. Two examples provided with the package are: record_dc.php and record_rif.php. They are helpers and need information from the real records. They need to be devloped for a particular system.
Classes related to generating RIF-CS XML response file for ANDS from repository. It also serves as an exmaple how class ANDS_RIFCS can be used in a particular case
Local variables $metadataPrefix and $identifier need to be provided through global array variable $args by their indexes 'metadataPrefix' and 'identifier'. The reset of information will be extracted from database based those two parameters.
This is an implementation for an OAI-PMH 2.0 Data Provider (sometimes, repository is used exchangeablly) written in PHP.
+
This implementation completely complies to OAI-PMH 2.0, including the support of on-the-fly output compression which may significantly 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. 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 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 database connection and define a suitable data structure.
+
The repository can be quite easily configured by just editing oaidp-config.php, most possible values and options are explained.
Copy the the files in source package to a location under your document root of your web server. The directory structure should be preserved.
+
Change to that directory (e.g. cd /var/www/html/oai).
+
Allow your webserver to write to the token directory. The default token directory is /tmp which does not need any attention.
+
Edit oaidp-config.php. Almost all possible options are 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. If your data is organized differently, you have to adjust the Query functions to reflect it and even develop your own code.
+
Check your oai site through a web browser. e.g. :
http://localhost/oai/
+
+
SELinux needs special treatments for database connection and other permission.
Support to different metadataformats in your own systems. Two examples provided with the package are: record_dc.php and record_rif.php. They are helpers and need information from the real records. They need to be devloped for your particular system.
Default starting point of OAI Data Provider for a human to check.
+More...
+
+
+
Detailed Description
+
OAI Data Provider is not designed for human to retrieve data but it is possible to use this page to test and check the functionality of current implementation. This page provides a summary of the OAI-PMH and the implementation.
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.
+
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 $args by keywords.
Remember: to define your own classess for generating metadata records. In common cases, you have to implement your own code to act fully and correctly. For generic usage, you can try the ANDS_Response_XML defined in xml_creater.php.
+
Variable Documentation
+
+
+
+
+
+
$errors = array()
+
+
+
+
+
An array for collecting erros which can be reported later. It will be checked before a new action is taken.
Support to different metadataformats in your own systems. Two examples provided with the package are: record_dc.php and record_rif.php. They are helpers and need information from the real records. They need to be devloped for a particular system.
This is the configuration file for the PHP OAI Data Provider. Please read through the WHOLE file, there are several things, that need to be adjusted.
+
The response may may be compressed for better performace:
+
+
Compression : a compression encoding supported by the repository. The recommended values are those defined for the Content-Encoding header in Section 14.11 of RFC 2616 describing HTTP 1.1. A compression element should not be included for the identity encoding, which is implied.
+
+
Some other used variables:
+
+
$adminEmail: the e-mail addresses of administrators of the repository.
+
+
+
$repositoryIdentifier : For a data provider there is only one. For repositories to comply with the oai format it has to be unique identifiers for items records. Basically using domainname will be fine. See: http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm.
+
+
+
$SETS: An array with key words . List of supported SETs.
+
+
+
$METADATAFORMATS: List of supported metadata formats. It is a two-dimensional array with keys. Each supported format is one element of this array at the first dimension. The key is the name of a metadata format. The exact number of items within each format associated array depends on the nature of a metadata format. Most definitions are done here but handlers themselves are defined in separated files because only the names of PHP script are listed here.
+
metadataPrefix
+
schema
+
metadataNamespace
+
myhandler
+
other optional items: record_prefix, record_namespace and etc.
+
+
+
+
+
$SQL: Settings for database and queries from database
+
+
+
$DSN: DSN for connecting your database. Reference PDO for details.
+
+
The rest of settings will not normally need to be adjusted. Read source code for details.
Whether to show error message for dubug. For installation, testing and debuging set SHOW_QUERY_ERROR to TRUE If set to TRUE, application will die and display query and database error message as soon as there is a problem. Do not set this to TRUE on a production site, since it will show error messages to everybody. If set FALSE, will create XML-output, no matter what happens.
Identifier settings. It needs to have proper values to reflect the settings of the data provider.
+
+
$identifyResponse['repositoryName'] : compulsory. A human readable name for the repository;
+
$identifyResponse['baseURL'] : compulsory. The base URL of the repository;
+
$identifyResponse['protocolVersion'] : compulsory. The version of the OAI-PMH supported by the repository;
+
$identifyResponse['earliestDatestamp'] : compulsory. A UTCdatetime that is the guaranteed lower limit of all datestamps recording changes, modifications, or deletions in the repository. A repository must not use datestamps lower than the one specified by the content of the earliestDatestamp element. earliestDatestamp must be expressed at the finest granularity supported by the repository.
+
$identifyResponse['deletedRecord'] : the manner in which the repository supports the notion of deleted records. Legitimate values are no ; transient ; persistent with meanings defined in the section on deletion.
+
$identifyResponse['granularity'] : the finest harvesting granularity supported by the repository. The legitimate values are YYYY-MM-DD and YYYY-MM-DDThh:mm:ssZ with meanings as defined in ISO8601.
Currently only gzip is supported (you need output buffering turned on, and php compiled with libgz). The client MUST send "Accept-Encoding: gzip" to actually receive
Prints a message for debugging, only works when SHOW_QUERY_ERROR is true. PHP function print_r can be used to construct message with return parameter sets to true.
Check if provided correct arguments for a request.
+
Only number of parameters is checked. metadataPrefix has to be checked before it is used. set has to be checked before it is used. resumptionToken has to be checked before it is used. from and until can easily checked here because no extra information is needed.
+
+
+
+
+
+
+
+
+
is_valid_uri
+
(
+
$
+
url
+
)
+
+
+
+
+
+
Validates an identifier. The pattern is: '/^[-a-z\.0-9]+$/i' which means it accepts -, letters and numbers. Used only by function oai_error code idDoesNotExist.
+
Parameters:
+
+
$url
Type: string
+
+
+
+
+
+
+
+
+
+
+
+
is_valid_attrb
+
(
+
$
+
attrb
+
)
+
+
+
+
+
+
Validates attributes come with the query. It accepts letters, numbers, ':', '_', '.' and -. Here there are few more match patterns than is_valid_uri(): ':_'.
+
Parameters:
+
+
$attrb
Type: string
+
+
+
+
+
+
+
+
+
+
+
+
formatDatestamp
+
(
+
$
+
datestamp
+
)
+
+
+
+
+
+
All datestamps used in this system are GMT even return value from database has no TZ information
+
+
+
+
+
+
+
+
+
checkDateFormat
+
(
+
$
+
date
+
)
+
+
+
+
+
+
The database uses datastamp without time-zone information. It needs to clean all time-zone informaion from time string and reformat it
Finish a request when there is an error: send back errors.
+
+
+
+
+
+
+
+
+
get_token
+
(
+
+
)
+
+
+
+
+
+
Generate a string based on the current Unix timestamp in microseconds for creating resumToken file name.
+
+
+
+
+
+
+
+
+
createResumToken
+
(
+
$
+
cursor,
+
+
+
+
+
$
+
extquery,
+
+
+
+
+
$
+
metadataPrefix
+
+
+
+
)
+
+
+
+
+
+
Create a token file. It has three parts which is separated by '#': cursor, extension of query, metadataPrefix. Called by listrecords.php.
+
+
+
+
+
+
+
+
+
readResumToken
+
(
+
$
+
resumptionToken
+
)
+
+
+
+
+
+
Read a saved ResumToken
+
+
+
+
+
+
+
+
+
selectallQuery
+
(
+
$
+
metadPrefix = "rif",
+
+
+
+
+
$
+
id = ''
+
+
+
+
)
+
+
+
+
+
+
this function should generate a query which will return all records the useless condition id_column = id_column is just there to ease further extensions to the query, please leave it as it is.
+
+
+
+
+
+
+
+
+
idFormatQuery
+
(
+
$
+
id
+
)
+
+
+
+
+
+
this function will return metadataFormat of a record
+
+
+
+
+
+
+
+
+
idQuery
+
(
+
$
+
metadPrefix = "rif",
+
+
+
+
+
$
+
id = ''
+
+
+
+
)
+
+
+
+
+
+
this function will return identifier and datestamp for all records not very useful
+
+
+
+
+
+
+
+
+
untilQuery
+
(
+
$
+
until
+
)
+
+
+
+
+
+
filter for until, appends to the end of SQL query
+
+
+
+
+
+
+
+
+
fromQuery
+
(
+
$
+
from
+
)
+
+
+
+
+
+
filter for from , appends to the end of SQL query
+
+
+
+
+
+
+
+
+
setQuery
+
(
+
$
+
set
+
)
+
+
+
+
+
+
filter for sets, appends to the end of SQL query
+
+
+
+
+
+
+
+
+
rowCount
+
(
+
$
+
metadataPrefix,
+
+
+
+
+
$
+
extQuery,
+
+
+
+
+
$
+
db
+
+
+
+
)
+
+
+
+
+
+
for accurately to assess how many records satisfy conditions for all DBs
+
+
+
+
+
+
+
+
+
process_pdo_error
+
(
+
$
+
query,
+
+
+
+
+
$
+
e
+
+
+
+
)
+
+
+
+
+
+
A worker function for processing an error when a query was executed
+
Parameters:
+
+
$query
string, original query
+
$e
PDOException, the PDOException object
+
+
+
+
+
+
+
+
+
+
+
+
exec_pdo_query
+
(
+
$
+
db,
+
+
+
+
+
$
+
query
+
+
+
+
)
+
+
+
+
+
+
When query return no result, throw an Exception of Not found.
Project information includes 1. General project informaion 2. List of plants: studied objects 3. Imaging regime 4. Outcomes 5. Image data download options 6. Copyright statement 6.1. Contact person 6.2 Organisations involved
+
Function Documentation
+
+
+
+
+
+
format_bytes
+
(
+
$
+
bytes
+
)
+
+
+
+
+
+
Format files size to a in bytes, KB, MB, GB and TB
+
Parameters:
+
+
$bytes
Type: integer. Value of size will be formatted.
+
+
+
+
Returns:
string. Round to a format of xxx.xx unit.
+
+
+
+
+
+
+
+
+
disp_ul_sec
+
(
+
$
+
result,
+
+
+
+
+
$
+
lbls
+
+
+
+
)
+
+
+
+
+
+
Organise a query result in an unordered list
+
Parameters:
+
+
$result
Type: resource. The query result to be displayed.
+
$lbls
Type: array. The string array of labels.
+
+
+
+
+
+
+
+
+
+
+
+
disp_dl_group
+
(
+
$
+
lbls,
+
+
+
+
+
$
+
row
+
+
+
+
)
+
+
+
+
+
+
Organise a lable and content pair in <dt> <dd> pair.
Project information includes 1. General project informaion 2. List of plants: studied objects 3. Imaging regime 4. Outcomes 5. Image data download options 6. Copyright statement 6.1. Contact person 6.2 Organisations involved
Project information includes 1. General project informaion 2. List of plants: studied objects 3. Imaging regime 4. Outcomes 5. Image data download options 6. Copyright statement 6.1. Contact person 6.2 Organisations involved
+
Function Documentation
+
+
+
+
+
+
format_bytes
+
(
+
$
+
bytes
+
)
+
+
+
+
+
+
Format files size to a in bytes, KB, MB, GB and TB
+
Parameters:
+
+
$bytes
Type: integer. Value of size will be formatted.
+
+
+
+
Returns:
string. Round to a format of xxx.xx unit.
+
+
+
+
+
+
+
+
+
disp_ul_sec
+
(
+
$
+
result,
+
+
+
+
+
$
+
lbls
+
+
+
+
)
+
+
+
+
+
+
Display results in an unordered list setction Within each list, there is a description list
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
disp_dl_group
+
(
+
$
+
lbls,
+
+
+
+
+
$
+
row
+
+
+
+
)
+
+
+
+
+
+
Organise a lable and content pair in <dt> <dd> pair.
It is not working as it does not provide any content to the metadata node. It only included to demonstrate how a new metadata can be supported. For a working example, please see record_rif.php.
It is a plug-in helper function which will be called from where a metadata in rif format is being generated. The name of function defined here cannot be changed. This can also be used as an example for your own metadata strucutre:
+
+
create a metetadata node
+
append contents of the record to the metedata node
+
+
In this example, every time when a new record is being generated, a new instance of ANDS_TPA is created. As XML output document and the database connection are the same, it is possible to design otherwise.
+For other tests on your own provider or other providers, please use the Repository Explorer.
+
+
+Jianfeng Li
+The Plant Accelerator
+University of Adelaide
+
+
+
+
+
+
diff --git a/listmetadataformats.php b/listmetadataformats.php
new file mode 100644
index 0000000..7c1bf3c
--- /dev/null
+++ b/listmetadataformats.php
@@ -0,0 +1,67 @@
+idFormatQuery.
+ * \sa idFormatQuery
+ */
+
+/**
+ * Add a metadata format node to an ANDS_Response_XML
+ * \param &$outputObj
+ * type: ANDS_Response_XML. The ANDS_Response_XML object for output.
+ * \param $key
+ * type string. The name of new node.
+ * \param $val
+ * type: array. Values accessable through keywords 'schema' and 'metadataNamespace'.
+ *
+ */
+function addMetedataFormat(&$outputObj,$key,$val) {
+ $cmf = $outputObj->add2_verbNode("metadataFormat");
+ $outputObj->addChild($cmf,'metadataPrefix',$key);
+ $outputObj->addChild($cmf,'schema',$val['schema']);
+ $outputObj->addChild($cmf,'metadataNamespace',$val['metadataNamespace']);
+}
+
+if (isset($args['identifier'])) {
+ $identifier = $args['identifier'];
+ $query = idFormatQuery($identifier);
+ $res = $db->query($query);
+ if ($res==false) {
+ if (SHOW_QUERY_ERROR) {
+ echo __FILE__.','.__LINE__." ";
+ echo "Query: $query \n";
+ die($db->errorInfo());
+ } else {
+ $errors[] = oai_error('idDoesNotExist','', $identifier);
+ }
+ } else {
+ $record = $res->fetch();
+ if($record===false) {
+ $errors[] = oai_error('idDoesNotExist', '', $identifier);
+ } else {
+ $mf = explode(",",$record[$SQL['metadataPrefix']]);
+ }
+ }
+}
+
+//break and clean up on error
+if (!empty($errors)) oai_exit();
+
+$outputObj = new ANDS_Response_XML($args);
+if (isset($mf)) {
+ foreach($mf as $key) {
+ $val = $METADATAFORMATS[$key];
+ addMetedataFormat($outputObj,$key, $val);
+ }
+} elseif (is_array($METADATAFORMATS)) {
+ foreach($METADATAFORMATS as $key=>$val) {
+ addMetedataFormat($outputObj,$key, $val);
+ }
+}
+else { // a very unlikely event
+ $errors[] = oai_error('noMetadataFormats');
+ oai_exit();
+}
+?>
diff --git a/listrecords.php b/listrecords.php
new file mode 100644
index 0000000..01c8e25
--- /dev/null
+++ b/listrecords.php
@@ -0,0 +1,182 @@
+$args by keywords.
+ */
+
+debug_message("\nI am debuging". __FILE__) ;
+
+// Resume previous session?
+if (isset($args['resumptionToken'])) {
+ if (!file_exists(TOKEN_PREFIX.$args['resumptionToken'])) {
+ $errors[] = oai_error('badResumptionToken', '', $args['resumptionToken']);
+ } else {
+ $readings = readResumToken(TOKEN_PREFIX.$args['resumptionToken']);
+ if ($readings == false) {
+ $errors[] = oai_error('badResumptionToken', '', $args['resumptionToken']);
+ } else {
+ debug_var_dump('readings',$readings);
+ list($deliveredrecords, $extquery, $metadataPrefix) = $readings;
+ }
+ }
+} else { // no, we start a new session
+ $deliveredrecords = 0;
+ $extquery = '';
+
+ $metadataPrefix = $args['metadataPrefix'];
+
+ if (isset($args['from'])) {
+ $from = checkDateFormat($args['from']);
+ $extquery .= fromQuery($from);
+ }
+
+ if (isset($args['until'])) {
+ $until = checkDateFormat($args['until']);
+ $extquery .= untilQuery($until);
+ }
+
+ if (isset($args['set'])) {
+ if (is_array($SETS)) {
+ $extquery .= setQuery($args['set']);
+ } else {
+ $errors[] = oai_error('noSetHierarchy');
+ }
+ }
+}
+
+if (!empty($errors)) {
+ oai_exit();
+}
+
+// Load the handler
+if (is_array($METADATAFORMATS[$metadataPrefix])
+ && isset($METADATAFORMATS[$metadataPrefix]['myhandler'])) {
+ $inc_record = $METADATAFORMATS[$metadataPrefix]['myhandler'];
+ include($inc_record);
+} else {
+ $errors[] = oai_error('cannotDisseminateFormat', 'metadataPrefix', $metadataPrefix);
+}
+
+if (!empty($errors)) {
+ oai_exit();
+}
+
+if (empty($errors)) {
+ $query = selectallQuery($metadataPrefix) . $extquery;
+ debug_message("Query: $query") ;
+
+ $res = $db->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
+ $r = $res->execute();
+ if ($r===false) {
+ if (SHOW_QUERY_ERROR) {
+ echo __FILE__.','.__LINE__." ";
+ echo "Query: $query \n";
+ print_r($db->errorInfo());
+ exit();
+ } else {
+ $errors[] = oai_error('noRecordsMatch');
+ }
+ } else {
+ $r = $res->setFetchMode(PDO::FETCH_ASSOC);
+ if ($r===false) {
+ exit("FetchMode is not supported");
+ }
+ $num_rows = rowCount($metadataPrefix, $extquery, $db);
+ if ($num_rows==0) {
+ echo "Cannot find records: $query\n";
+ $errors[] = oai_error('noRecordsMatch');
+ }
+ }
+}
+
+if (!empty($errors)) {
+ oai_exit();
+}
+
+// Will we need a new ResumptionToken?
+if($args['verb']=='ListRecords') {
+ $maxItems = MAXRECORDS;
+} elseif($args['verb']=='ListIdentifiers') {
+ $maxItems = MAXIDS;
+} else {
+ exit("Check ".__FILE__." ".__LINE__.", there is something wrong.");
+}
+$maxrec = min($num_rows - $deliveredrecords, $maxItems);
+
+if ($num_rows - $deliveredrecords > $maxItems) {
+ $cursor = (int)$deliveredrecords + $maxItems;
+ $restoken = createResumToken($cursor, $extquery, $metadataPrefix);
+ $expirationdatetime = gmstrftime('%Y-%m-%dT%TZ', time()+TOKEN_VALID);
+}
+// Last delivery, return empty ResumptionToken
+elseif (isset($args['resumptionToken'])) {
+ $restoken = $args['resumptionToken']; // just used as an indicator
+ unset($expirationdatetime);
+}
+
+
+if (isset($args['resumptionToken'])) {
+ debug_message("Try to resume because a resumptionToken supplied.") ;
+ $record = $res->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, $deliveredrecords);
+}
+// Record counter
+$countrec = 0;
+
+// Publish a batch to $maxrec number of records
+$outputObj = new ANDS_Response_XML($args);
+while ($countrec++ < $maxrec) {
+ $record = $res->fetch(PDO::FETCH_ASSOC);
+ if ($record===false) {
+ if (SHOW_QUERY_ERROR) {
+ echo __FILE__.",". __LINE__." ";
+ print_r($db->errorInfo());
+ exit();
+ }
+ }
+
+ $identifier = $oaiprefix.$record[$SQL['identifier']];
+ $datestamp = formatDatestamp($record[$SQL['datestamp']]);
+ $setspec = $record[$SQL['set']];
+
+ // debug_var_dump('record', $record);
+ if (isset($record[$SQL['deleted']]) && ($record[$SQL['deleted']] === true) &&
+ ($deletedRecord == 'transient' || $deletedRecord == 'persistent')) {
+ $status_deleted = TRUE;
+ } else {
+ $status_deleted = FALSE;
+ }
+
+ //debug_var_dump('status_deleted', $status_deleted);
+ if($args['verb']=='ListRecords') {
+ $cur_record = $outputObj->create_record();
+ $cur_header = $outputObj->create_header($identifier, $datestamp,$setspec,$cur_record);
+ // return the metadata record itself
+ if (!$status_deleted) {
+ debug_var_dump('inc_record',$inc_record);
+ create_metadata($outputObj, $cur_record, $identifier, $setspec, $db);
+ }
+ } else { // for ListIdentifiers, only identifiers will be returned.
+ $cur_header = $outputObj->create_header($identifier, $datestamp,$setspec);
+ }
+ if ($status_deleted) {
+ $cur_header->setAttribute("status","deleted");
+ }
+}
+
+// ResumptionToken
+if (isset($restoken)) {
+ if(isset($expirationdatetime)) {
+ $outputObj->create_resumpToken($restoken,$expirationdatetime,$num_rows,$cursor);
+ } else {
+ $outputObj->create_resumpToken('',null,$num_rows,$deliveredrecords);
+ }
+}
+
+// end ListRecords
+if (SHOW_QUERY_ERROR) {echo "Debug listrecord.php reached to the end.\n\n";}
+?>
diff --git a/listsets.php b/listsets.php
new file mode 100644
index 0000000..8c47918
--- /dev/null
+++ b/listsets.php
@@ -0,0 +1,30 @@
+add2_verbNode("set");
+ foreach($set as $key => $val) {
+ if($key=='setDescription') {
+ $desNode = $outputObj->addChild($setNode,$key);
+ $des = $outputObj->doc->createDocumentFragment();
+ $des->appendXML($val);
+ $desNode->appendChild($des);
+ } else {
+ $outputObj->addChild($setNode,$key,$val);
+ }
+ }
+ }
+} else {
+ $errors[] = oai_error('noSetHierarchy');
+ oai_exit();
+}
+
+?>
diff --git a/oai2.php b/oai2.php
new file mode 100644
index 0000000..e426fb2
--- /dev/null
+++ b/oai2.php
@@ -0,0 +1,173 @@
+Remember: to define your own classess for generating metadata records.
+ * In common cases, you have to implement your own code to act fully and correctly.
+ * For generic usage, you can try the ANDS_Response_XML defined in xml_creater.php.
+ */
+
+// Report all errors except E_NOTICE
+// This is the default value set in php.ini
+// If anything else, try them.
+// error_reporting (E_ALL ^ E_NOTICE);
+
+/**
+ * An array for collecting erros which can be reported later. It will be checked before a new action is taken.
+ */
+$errors = array();
+
+/**
+ * Supported attributes associate to verbs.
+ */
+$attribs = array ('from', 'identifier', 'metadataPrefix', 'set', 'resumptionToken', 'until');
+
+if (in_array($_SERVER['REQUEST_METHOD'],array('GET','POST'))) {
+ $args = $_REQUEST;
+} else {
+ $errors[] = oai_error('badRequestMethod', $_SERVER['REQUEST_METHOD']);
+}
+
+require_once('oaidp-util.php');
+// Always using htmlentities() function to encodes the HTML entities submitted by others.
+// No one can be trusted.
+foreach ($args as $key => $val) {
+ $checking = htmlspecialchars(stripslashes($val));
+ if (!is_valid_attrb($checking)) {
+ $errors[] = oai_error('badArgument', $checking);
+ } else {$args[$key] = $checking; }
+}
+if (!empty($errors)) { oai_exit(); }
+
+foreach($attribs as $val) {
+ unset($$val);
+}
+
+require_once('oaidp-config.php');
+
+// Create a PDO object
+try {
+ $db = new PDO($DSN);
+} catch (PDOException $e) {
+ exit('Connection failed: ' . $e->getMessage());
+}
+
+// For generic usage or just trying:
+// require_once('xml_creater.php');
+// In common cases, you have to implement your own code to act fully and correctly.
+require_once('ands_tpa.php');
+
+// Default, there is no compression supported
+$compress = FALSE;
+if (isset($compression) && is_array($compression)) {
+ if (in_array('gzip', $compression) && ini_get('output_buffering')) {
+ $compress = TRUE;
+ }
+}
+
+if (SHOW_QUERY_ERROR) {
+ echo "Args:\n"; print_r($args);
+}
+
+if (isset($args['verb'])) {
+ switch ($args['verb']) {
+
+ case 'Identify':
+ // we never use compression in Identify
+ $compress = FALSE;
+ if(count($args)>1) {
+ foreach($args as $key => $val) {
+ if(strcmp($key,"verb")!=0) {
+ $errors[] = oai_error('badArgument', $key, $val);
+ }
+ }
+ }
+ if (empty($errors)) include 'identify.php';
+ break;
+
+ case 'ListMetadataFormats':
+ $checkList = array("ops"=>array("identifier"));
+ checkArgs($args, $checkList);
+ if (empty($errors)) include 'listmetadataformats.php';
+ break;
+
+ case 'ListSets':
+ if(isset($args['resumptionToken']) && count($args) > 2) {
+ $errors[] = oai_error('exclusiveArgument');
+ }
+ $checkList = array("ops"=>array("resumptionToken"));
+ checkArgs($args, $checkList);
+ if (empty($errors)) include 'listsets.php';
+ break;
+
+ case 'GetRecord':
+ $checkList = array("required"=>array("metadataPrefix","identifier"));
+ checkArgs($args, $checkList);
+ if (empty($errors)) include 'getrecord.php';
+ break;
+
+ case 'ListIdentifiers':
+ case 'ListRecords':
+ if(isset($args['resumptionToken'])) {
+ if (count($args) > 2) {
+ $errors[] = oai_error('exclusiveArgument');
+ }
+ $checkList = array("ops"=>array("resumptionToken"));
+ } else {
+ $checkList = array("required"=>array("metadataPrefix"),"ops"=>array("from","until","set"));
+ }
+ checkArgs($args, $checkList);
+ if (empty($errors)) include 'listrecords.php';
+ break;
+
+ default:
+ // we never use compression with errors
+ $compress = FALSE;
+ $errors[] = oai_error('badVerb', $args['verb']);
+ } /*switch */
+} else {
+ $errors[] = oai_error('noVerb');
+}
+
+if (!empty($errors)) { oai_exit(); }
+
+if ($compress) {
+ ob_start('ob_gzhandler');
+}
+
+header(CONTENT_TYPE);
+
+if(isset($outputObj)) {
+ $outputObj->display();
+} else {
+ exit("There is a bug in codes");
+}
+
+ if ($compress) {
+ ob_end_flush();
+ }
+
+?>
diff --git a/oaidp-config.php b/oaidp-config.php
new file mode 100644
index 0000000..b036f34
--- /dev/null
+++ b/oaidp-config.php
@@ -0,0 +1,298 @@
+$adminEmail: the e-mail addresses of administrators of the repository.
+ *
+ * - $repositoryIdentifier : For a data provider there is only one. For repositories to comply with the oai
+ * format it has to be unique identifiers for items records. Basically using domainname will be fine.
+ * See: http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm.
+ *
+ * - $SETS: An array with key words . List of supported SETs.
+ *
+ * - $METADATAFORMATS: List of supported metadata formats. It is a two-dimensional array with keys.
+ * Each supported format is one element of this array at the first dimension.
+ * The key is the name of a metadata format.
+ * The exact number of items within each format associated array depends on the nature of a metadata format.
+ * Most definitions are done here but handlers themselves are defined in separated files because only the names of PHP script are listed here.
+ * - metadataPrefix
+ * - schema
+ * - metadataNamespace
+ * - myhandler
+ * - other optional items: record_prefix, record_namespace and etc.
+ *
+ * - $SQL: Settings for database and queries from database
+ *
+ * - $DSN: DSN for connecting your database. Reference PDO for details.
+ *
+ * The rest of settings will not normally need to be adjusted. Read source code for details.
+*/
+
+/**
+ * Whether to show error message for dubug.
+ * For installation, testing and debuging set SHOW_QUERY_ERROR to TRUE
+ * If set to TRUE, application will die and display query and database error message
+ * as soon as there is a problem. Do not set this to TRUE on a production site,
+ * since it will show error messages to everybody.
+ * If set FALSE, will create XML-output, no matter what happens.
+ */
+// If everything is running ok, you should use this
+define('SHOW_QUERY_ERROR',FALSE);
+
+/**
+ * \property CONTENT_TYPE
+ * The content-type the WWW-server delivers back. For debug-puposes, "text/plain"
+ * is easier to view. On a production site you should use "text/xml".
+ */
+#define('CONTENT_TYPE','Content-Type: text/plain');
+// If everything is running ok, you should use this
+define('CONTENT_TYPE', 'Content-Type: text/xml');
+
+/**
+ * Identifier settings. It needs to have proper values to reflect the settings of the data provider.
+ *
+ * - $identifyResponse['repositoryName'] : compulsory. A human readable name for the repository;
+ * - $identifyResponse['baseURL'] : compulsory. The base URL of the repository;
+ * - $identifyResponse['protocolVersion'] : compulsory. The version of the OAI-PMH supported by the repository;
+ * - $identifyResponse['earliestDatestamp'] : compulsory. A UTCdatetime that is the guaranteed lower limit of all datestamps recording changes, modifications, or deletions in the repository. A repository must not use datestamps lower than the one specified by the content of the earliestDatestamp element. earliestDatestamp must be expressed at the finest granularity supported by the repository.
+ * - $identifyResponse['deletedRecord'] : the manner in which the repository supports the notion of deleted records. Legitimate values are no ; transient ; persistent with meanings defined in the section on deletion.
+ * - $identifyResponse['granularity'] : the finest harvesting granularity supported by the repository. The legitimate values are YYYY-MM-DD and YYYY-MM-DDThh:mm:ssZ with meanings as defined in ISO8601.
+ *
+ */
+$identifyResponse = array();
+
+// MUST (only one)
+// please adjust
+$identifyResponse["repositoryName"] = 'Your organisation';
+
+// For ANDS to harvest of RIF-CS, originatingSource is plantaccelerator.org.au
+// $dataSource = "plantaccelerator.org.au";
+define('DATASOURCE','domain.of.your.organisation');
+
+// do not change
+define('MY_URI','http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME']);
+// You can use a static URI as well.
+// $baseURL = "http://my.server.org/oai/oai2.php";
+$identifyResponse["baseURL"] = MY_URI;
+
+// do not change
+$identifyResponse["protocolVersion"] = '2.0';
+
+// MUST (only one)
+// the earliest datestamp in your repository,
+// please adjust
+// Only date is needed even later it will be formatted according to the granularity.
+$identifyResponse["earliestDatestamp"] = '2011-01-01';
+
+// How your repository handles deletions
+// no: The repository does not maintain status about deletions.
+// It MUST NOT reveal a deleted status.
+// persistent: The repository persistently keeps track about deletions
+// with no time limit. It MUST consistently reveal the status
+// of a deleted record over time.
+// transient: The repository does not guarantee that a list of deletions is
+// maintained. It MAY reveal a deleted status for records.
+//
+// If your database keeps track of deleted records change accordingly.
+// Currently if $record['deleted'] is set to 'true', $status_deleted is set.
+// Some lines in listidentifiers.php, listrecords.php, getrecords.php
+// must be changed to fit the condition for your database.
+$identifyResponse["deletedRecord"] = 'no';
+$deletedRecord = $identifyResponse["deletedRecord"]; // a shorthand for checking the configuration of Deleted Records
+
+// MAY (only one)
+//granularity is days
+//$granularity = 'YYYY-MM-DD';
+// granularity is seconds
+$identifyResponse["granularity"] = 'YYYY-MM-DDThh:mm:ssZ';
+
+// this is appended if your granularity is seconds.
+// do not change
+if (strcmp($identifyResponse["granularity"],'YYYY-MM-DDThh:mm:ssZ')==0) {
+ $identifyResponse["earliestDatestamp"] = $identifyResponse["earliestDatestamp"].'T00:00:00Z';
+}
+
+// MUST (multiple)
+// please adjust
+$adminEmail = array('some.one@contact.com');
+
+/** Compression methods supported. Optional (multiple). Default: null.
+*
+* Currently only gzip is supported (you need output buffering turned on,
+* and php compiled with libgz).
+* The client MUST send "Accept-Encoding: gzip" to actually receive
+*/
+// $compression = array('gzip');
+$compression = null;
+
+// MUST (only one)
+// You may choose any name, but for repositories to comply with the oai
+// format it has to be unique identifiers for items records.
+// see: http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm
+// Basically use domainname
+// please adjust
+$repositoryIdentifier = 'url.organisation.';
+
+// For RIF-CS, especially with ANDS, each registryObject much has a group for the ownership of data.
+// For detail please see ANDS guide on its web site. Each data provider should have only one REG_OBJ_GROUP
+// for this purpose.
+define('REG_OBJ_GROUP','Something agreed on');
+
+// If Identifier needs to show NODE description. It is defined in identify.php
+// You may include details about your community and friends (other
+// data-providers).
+// Please check identify.php for other possible containers
+// in the Identify response
+$show_identifier = false;
+// MUST (only one)
+// should not be changed. Only useful when NODE description is included in the response to Identifier
+$delimiter = ':';
+
+
+/** Maximum mumber of the records to deliver
+ * (verb is ListRecords)
+ * If there are more records to deliver
+ * a ResumptionToken will be generated.
+ */
+define('MAXRECORDS',10);
+
+/** Maximum mumber of identifiers to deliver
+ * (verb is ListIdentifiers)
+ * If there are more identifiers to deliver
+ * a ResumptionToken will be generated.
+ */
+define('MAXIDS',40);
+
+/** After 24 hours resumptionTokens become invalid. Unit is second. */
+define('TOKEN_VALID',24*3600);
+$expirationdatetime = gmstrftime('%Y-%m-%dT%TZ', time()+TOKEN_VALID);
+/** Where token is saved and path is included */
+define('TOKEN_PREFIX','/tmp/ANDS_DBPD-');
+
+// define all supported sets in your repository
+$SETS = array (
+ array('setSpec'=>'class:activity', 'setName'=>'Activities'),
+ array('setSpec'=>'class:collection', 'setName'=>'Collections'),
+ array('setSpec'=>'class:party', 'setName'=>'Parties')/*,
+ array('setSpec'=>'phdthesis', 'setName'=>'PHD Thesis', 'setDescription'=>'
+ This set contains metadata describing electronic music recordings made during the 1950ies
+ ') //,
+ // array('setSpec'=>'math', 'setName'=>'Mathematics') ,
+ // array('setSpec'=>'phys', 'setName'=>'Physics')
+ */);
+
+// define all supported metadata formats, has to be an array
+//
+// myhandler is the name of the file that handles the request for the
+// specific metadata format.
+// [record_prefix] describes an optional prefix for the metadata
+// [record_namespace] describe the namespace for this prefix
+
+$METADATAFORMATS = array (
+ 'rif' => array('metadataPrefix'=>'rif',
+ 'schema'=>'http://services.ands.org.au/sandbox/orca/schemata/registryObjects.xsd',
+ 'metadataNamespace'=>'http://ands.org.au/standards/rif-cs/registryObjects/',
+ 'myhandler'=>'record_rif.php'
+ ),
+ 'oai_dc' => array('metadataPrefix'=>'oai_dc',
+ 'schema'=>'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
+ 'metadataNamespace'=>'http://www.openarchives.org/OAI/2.0/oai_dc/',
+ 'myhandler'=>'record_dc.php',
+ 'record_prefix'=>'dc',
+ 'record_namespace' => 'http://purl.org/dc/elements/1.1/'
+ )
+ );
+
+if (!is_array($METADATAFORMATS)) { exit("Configuration of METADATAFORMAT has been wrongly set. Correct your ".__FILE__);}
+
+// The shorthand of xml schema namespace, no need to change this
+define('XMLSCHEMA', 'http://www.w3.org/2001/XMLSchema-instance');
+
+//
+// DATABASE SETUP
+//
+
+// change according to your local DB setup.
+$DB_HOST = '127.0.0.1';
+$DB_USER = 'your-name';
+$DB_PASSWD = 'your-pwd';
+$DB_NAME = 'your-db';
+
+// Data Source Name: This is the universal connection string
+// if you use something other than mysql edit accordingly.
+// Example for MySQL
+// $DSN = "mysql://$DB_USER:$DB_PASSWD@$DB_HOST/$DB_NAME";
+// Example for Oracle
+// $DSN = "oci8://$DB_USER:$DB_PASSWD@$DB_NAME";
+
+$DSN = "pgsql:host=$DB_HOST;port=5432;dbname=$DB_NAME;user=$DB_USER;password=$DB_PASSWD";
+
+// the charset you store your metadata in your database
+// currently only utf-8 and iso8859-1 are supported
+$charset = "iso8859-1";
+
+// if entities such as < > ' " in your metadata has already been escaped
+// then set this to true (e.g. you store < as < in your DB)
+$xmlescaped = false;
+
+// We store multiple entries for one element in a single row
+// in the database. SQL['split'] lists the delimiter for these entries.
+// If you do not do this, do not define $SQL['split']
+// $SQL['split'] = ';';
+
+// the name of the table where your store your metadata's header
+$SQL['table'] = 'oai_headers';
+
+// the name of the column where you store the unique identifiers
+// pointing to your item.
+// this is your internal identifier for the item
+$SQL['identifier'] = 'oai_identifier';
+
+$SQL['metadataPrefix'] = 'oai_metadataprefix';
+
+// If you want to expand the internal identifier in some way
+// use this (but not for OAI stuff, see next line)
+$idPrefix = '';
+
+// this is your external (OAI) identifier for the item
+// this will be expanded to
+// oai:$repositoryIdentifier:$idPrefix$SQL['identifier']
+// should not be changed
+//
+// Commented out 24/11/10 14:19:09
+// $oaiprefix = "oai".$delimiter.$repositoryIdentifier.$delimiter.$idPrefix;
+$oaiprefix = "";
+
+// adjust anIdentifier with sample contents an identifier
+// $sampleIdentifier = $oaiprefix.'anIdentifier';
+
+// the name of the column where you store your datestamps
+$SQL['datestamp'] = 'datestamp';
+
+// the name of the column where you store information whether
+// a record has been deleted. Leave it as it is if you do not use
+// this feature.
+$SQL['deleted'] = 'deleted';
+
+// to be able to quickly retrieve the sets to which one item belongs,
+// the setnames are stored for each item
+// the name of the column where you store sets
+$SQL['set'] = 'oai_set';
+?>
diff --git a/oaidp-util.php b/oaidp-util.php
new file mode 100644
index 0000000..e99e5c5
--- /dev/null
+++ b/oaidp-util.php
@@ -0,0 +1,332 @@
+return parameter sets to true.
+ * \param $msg Type: string Message needs to be shown
+ * \see SHOW_QUERY_ERROR in oaidp-config.php
+ */
+function debug_message($msg) {
+ if (!SHOW_QUERY_ERROR) return;
+ echo $msg,"\n";
+}
+
+/** Check if provided correct arguments for a request.
+ *
+ * Only number of parameters is checked.
+ * metadataPrefix has to be checked before it is used.
+ * set has to be checked before it is used.
+ * resumptionToken has to be checked before it is used.
+ * from and until can easily checked here because no extra information
+ * is needed.
+ */
+function checkArgs($args, $checkList) {
+// global $errors, $TOKEN_VALID, $METADATAFORMATS;
+ global $errors, $METADATAFORMATS;
+// $verb = $args['verb'];
+ unset($args["verb"]);
+
+ debug_print_r('checkList',$checkList);
+ debug_print_r('args',$args);
+
+ // "verb" has been checked before, no further check is needed
+ if(isset($checkList['required'])) {
+ for($i = 0; $i < count($checkList["required"]); $i++) {
+ debug_message("Checking: par$i: ". $checkList['required'][$i] . " in ");
+ debug_var_dump("isset(\$args[\$checkList['required'][\$i]])",isset($args[$checkList['required'][$i]]));
+ // echo "key exists". array_key_exists($checkList["required"][$i],$args)."\n";
+ if(isset($args[$checkList['required'][$i]])==false) {
+ // echo "caught\n";
+ $errors[] = oai_error('missingArgument', $checkList["required"][$i]);
+ } else {
+ // if metadataPrefix is set, it is in required section
+ if(isset($args['metadataPrefix'])) {
+ $metadataPrefix = $args['metadataPrefix'];
+ // Check if the format is supported, it has enough infor (an array), last if a handle has been defined.
+ if (!array_key_exists ($metadataPrefix, $METADATAFORMATS) || !(is_array($METADATAFORMATS[$metadataPrefix])
+ || !isset($METADATAFORMATS[$metadataPrefix]['myhandler']))) {
+ $errors[] = oai_error('cannotDisseminateFormat', 'metadataPrefix', $metadataPrefix);
+ }
+ }
+ unset($args[$checkList["required"][$i]]);
+ }
+ }
+ }
+ debug_message('Before return');
+ debug_print_r('errors',$errors);
+ if (!empty($errors)) return;
+
+ // check to see if there is unwanted
+ foreach($args as $key => $val) {
+ debug_message("checkArgs: $key");
+ if(!in_array($key, $checkList["ops"])) {
+ debug_message("Wrong\n".print_r($checkList['ops'],true));
+ $errors[] = oai_error('badArgument', $key, $val);
+ }
+ switch ($key) {
+ case 'from':
+ case 'until':
+ if(!checkDateFormat($val)) {
+ $errors[] = oai_error('badGranularity', $key, $val);
+ }
+ break;
+
+ case 'resumptionToken':
+ // only check for expairation
+ if((int)$val+TOKEN_VALID < time())
+ $errors[] = oai_error('badResumptionToken');
+ break;
+ }
+ }
+}
+
+/** Validates an identifier. The pattern is: '/^[-a-z\.0-9]+$/i' which means
+ * it accepts -, letters and numbers.
+ * Used only by function oai_error 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 (SHOW_QUERY_ERROR) { echo "timeval: $time_val\n"; }
+ 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);
+ }
+}
+
+/** Retrieve all defined 'setSpec' from configuraiton of $SETS.
+ * It is used by ANDS_TPA::create_obj_node();
+*/
+function prepare_set_names() {
+ global $SETS;
+ $n = count($SETS);
+ $a = array_fill(0,$n,'');
+ for ($i = 0; $i <$n; $i++) {
+ $a[$i] = $SETS[$i]['setSpec'];
+ }
+ return $a;
+}
+
+/** Finish a request when there is an error: send back errors. */
+function oai_exit()
+{
+// global $CONTENT_TYPE;
+ header(CONTENT_TYPE);
+ global $args,$errors,$compress;
+ $e = new ANDS_Error_XML($args,$errors);
+ if ($compress) {
+ ob_start('ob_gzhandler');
+ }
+
+ $e->display();
+
+ if ($compress) {
+ ob_end_flush();
+ }
+
+ exit();
+}
+
+// ResumToken section
+/** Generate a string based on the current Unix timestamp in microseconds for creating resumToken file name. */
+function get_token()
+{
+ list($usec, $sec) = explode(" ", microtime());
+ return ((int)($usec*1000) + (int)($sec*1000));
+}
+
+/** Create a token file.
+ * It has three parts which is separated by '#': cursor, extension of query, metadataPrefix.
+ * Called by listrecords.php.
+ */
+function createResumToken($cursor, $extquery, $metadataPrefix) {
+ $token = get_token();
+ $fp = fopen (TOKEN_PREFIX.$token, 'w');
+ if($fp==false) {
+ exit("Cannot write. Writer permission needs to be changed.");
+ }
+ fputs($fp, "$cursor#");
+ fputs($fp, "$extquery#");
+ fputs($fp, "$metadataPrefix#");
+ fclose($fp);
+ return $token;
+}
+
+/** Read a saved ResumToken */
+function readResumToken($resumptionToken) {
+ $rtVal = false;
+ $fp = fopen($resumptionToken, 'r');
+ if ($fp!=false) {
+ $filetext = fgets($fp, 255);
+ $textparts = explode('#', $filetext);
+ fclose($fp);
+ unlink ($resumptionToken);
+ $rtVal = array((int)$textparts[0], $textparts[1], $textparts[2]);
+ }
+ return $rtVal;
+}
+
+// Here are a couple of queries which might need to be adjusted to
+// your needs. Normally, if you have correctly named the columns above,
+// this does not need to be done.
+
+/** this function should generate a query which will return
+ * all records
+ * the useless condition id_column = id_column is just there to ease
+ * further extensions to the query, please leave it as it is.
+ */
+function selectallQuery ($metadPrefix = "rif", $id = '')
+{
+ global $SQL;
+ $query = "SELECT * FROM ".$SQL['table'] . " WHERE ".$SQL['metadataPrefix']." LIKE '%$metadPrefix%'";
+ if ($id != '') {
+ $query .= " AND ".$SQL['identifier']." ='$id'";
+ }
+ return $query;
+}
+
+/** this function will return metadataFormat of a record */
+function idFormatQuery($id)
+{
+ global $SQL;
+ return 'select '.$SQL['metadataPrefix'].' FROM '.$SQL['table']. " WHERE ".$SQL['identifier']." = '".$id."'";
+}
+
+/** this function will return identifier and datestamp for all records
+ * not very useful
+ */
+function idQuery ($metadPrefix = "rif", $id = '')
+{
+ global $SQL;
+
+ if ($SQL['set'] != '') {
+ $query = 'select '.$SQL['identifier'].','.$SQL['datestamp'].','.$SQL['set'].' FROM '.$SQL['table']. " WHERE ".$SQL['metadataPrefix']." LIKE '%$metadPrefix%'";
+ } else {
+ $query = 'select '.$SQL['identifier'].','.$SQL['datestamp'].' FROM '.$SQL['table']. " WHERE ".$SQL['metadataPrefix']." LIKE '%$metadPrefix%'";
+ }
+
+ if ($id != '') {
+ $query .= " AND ".$SQL['identifier']." = '$id'";
+ }
+
+ return $query;
+}
+
+/** filter for until, appends to the end of SQL query */
+function untilQuery($until)
+{
+ global $SQL;
+
+ return ' AND '.$SQL['datestamp']." <= '$until'";
+}
+
+/** filter for from , appends to the end of SQL query */
+function fromQuery($from)
+{
+ global $SQL;
+
+ return ' AND '.$SQL['datestamp']." >= '$from'";
+}
+
+/** filter for sets, appends to the end of SQL query */
+function setQuery($set)
+{
+ global $SQL;
+ // strip off "class:" which is not saved in database
+ if(strstr($set,"class:")) $set = substr($set,6);
+ return ' AND '.$SQL['set']." LIKE '%$set%'";
+}
+
+/** for accurately to assess how many records satisfy conditions for all DBs */
+function rowCount($metadataPrefix, $extQuery, $db) {
+ global $SQL;
+ $n = 0;
+ $sql = "SELECT COUNT(*) FROM ".$SQL['table'] . " WHERE ".$SQL['metadataPrefix']." LIKE '%$metadataPrefix%'" . $extQuery;
+ if ($res = $db->query($sql)) {
+ $n = $res->fetchColumn();
+ }
+ return $n;
+}
+
+/** A worker function for processing an error when a query was executed
+ * \param $query string, original query
+ * \param $e PDOException, the PDOException object
+*/
+function process_pdo_error($query, $e) {
+ echo $query.' was failed\n';
+ echo $e->getMessage();
+}
+
+/** When query return no result, throw an Exception of Not found.
+ * \param $db PDO
+ * \param $query string
+ * \return $res PDOStatement
+ */
+function exec_pdo_query($db, $query)
+{
+ $res = $db->query($query);
+ if ($res===false) {
+ throw new Exception($query.":\nIt found nothing.\n");
+ } else return $res;
+}
+?>
diff --git a/record_dc.php b/record_dc.php
new file mode 100644
index 0000000..57be707
--- /dev/null
+++ b/record_dc.php
@@ -0,0 +1,14 @@
+create_metadata($cur_record);
+}
diff --git a/record_rif.php b/record_rif.php
new file mode 100644
index 0000000..c7be32e
--- /dev/null
+++ b/record_rif.php
@@ -0,0 +1,34 @@
+create_metadata($cur_record);
+ $obj_node = new ANDS_TPA($outputObj, $metadata_node, $db);
+ try {
+ $obj_node->create_obj_node($setspec, $identifier);
+ } catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), " when adding $identifier\n";
+ }
+}
+
diff --git a/xml_creater.php b/xml_creater.php
new file mode 100644
index 0000000..dd402d4
--- /dev/null
+++ b/xml_creater.php
@@ -0,0 +1,328 @@
+"ListRecords","resumptionToken"=>"9CD1DA87F59C3E960871F4F3C9D093887C17D174");
+ * // Example 1: Error response
+ * $error_array[] = oai_error("badVerb","Rubish");
+ * $error_array[] = oai_error("sameVerb");
+ * $e = new ANDS_Error_XML($par_array,$error_array);
+ * $e->display();
+ *
+
+ * // Example 2: Normal response without error codes
+ * $par_array = array("verb"=>"ListRecords","resumptionToken"=>"9CD1DA87F59C3E960871F4F3C9D093887C17D174");
+ * $test = new ANDS_Response_XML($par_array);
+ * $record_node = $test->create_record();
+ * $test->create_header("function: identifier string",gmdate("Y-m-d\TH:i:s\Z"),"collection",$record_node);
+ * $test->create_metadata($record_node);
+ * $test->display();
+ * \endcode
+ *
+ * \see http://www.openarchives.org/OAI/openarchivesprotocol.html#ErrorConditions
+ */
+
+/*
+http://www.openarchives.org/OAI/openarchivesprotocol.html#ErrorConditions
+
+badArgument:
+ The request includes illegal arguments, is missing required arguments, includes a repeated argument, or values for arguments have an illegal syntax. Applied to all verbs.
+
+badResumptionToken:
+ The value of the resumptionToken argument is invalid or expired. Applied to: ListIdentifiers, ListRecords, ListSets
+
+badVerb:
+ Value of the verb argument is not a legal OAI-PMH verb, the verb argument is missing, or the verb argument is repeated. N/A
+
+cannotDisseminateFormat:
+ The metadata format identified by the value given for the metadataPrefix argument is not supported by the item or by the repository. Applied to GetRecord, ListIdentifiers, ListRecords
+
+idDoesNotExist:
+ The value of the identifier argument is unknown or illegal in this repository. Applied to GetRecord, ListMetadataFormats
+
+noRecordsMatch:
+ The combination of the values of the from, until, set and metadataPrefix arguments results in an empty list. Applied to ListIdentifiers, ListRecords
+
+noMetadataFormats:
+ There are no metadata formats available for the specified item. Applied to ListMetadataFormats.
+
+noSetHierarchy:
+ The repository does not support sets. Applied to ListSets, ListIdentifiers, ListRecords
+
+*/
+
+/** utility funciton to mapping error codes to readable messages */
+function oai_error($code, $argument = '', $value = '')
+{
+ switch ($code) {
+ case 'badArgument' :
+ $text = "The argument '$argument' (value='$value') included in the request is not valid.";
+ break;
+
+ case 'badGranularity' :
+ $text = "The value '$value' of the argument '$argument' is not valid.";
+ $code = 'badArgument';
+ break;
+
+ case 'badResumptionToken' :
+ $text = "The resumptionToken '$value' does not exist or has already expired.";
+ break;
+
+ case 'badRequestMethod' :
+ $text = "The request method '$argument' is unknown.";
+ $code = 'badVerb';
+ break;
+
+ case 'badVerb' :
+ $text = "The verb '$argument' provided in the request is illegal.";
+ break;
+
+ case 'cannotDisseminateFormat' :
+ $text = "The metadata format '$value' given by $argument is not supported by this repository.";
+ break;
+
+ case 'exclusiveArgument' :
+ $text = 'The usage of resumptionToken as an argument allows no other arguments.';
+ $code = 'badArgument';
+ break;
+
+ case 'idDoesNotExist' :
+ $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.';
+ }
+ break;
+
+ case 'missingArgument' :
+ $text = "The required argument '$argument' is missing in the request.";
+ $code = 'badArgument';
+ break;
+
+ case 'noRecordsMatch' :
+ $text = 'The combination of the given values results in an empty list.';
+ break;
+
+ case 'noMetadataFormats' :
+ $text = 'There are no metadata formats available for the specified item.';
+ break;
+
+ case 'noVerb' :
+ $text = 'The request does not provide any verb.';
+ $code = 'badVerb';
+ break;
+
+ case 'noSetHierarchy' :
+ $text = 'This repository does not support sets.';
+ break;
+
+ case 'sameArgument' :
+ $text = 'Do not use the same argument more than once.';
+ $code = 'badArgument';
+ break;
+
+ case 'sameVerb' :
+ $text = 'Do not use verb more than once.';
+ $code = 'badVerb';
+ break;
+
+ case 'notImp' :
+ $text = 'Not yet implemented.';
+ $code = 'debug';
+ break;
+
+ default:
+ $text = "Unknown error: code: '$code', argument: '$argument', value: '$value'";
+ $code = 'badArgument';
+ }
+ return $code."|".$text;
+}
+
+/**
+ * A wraper of DOMDocument for data provider
+ */
+class ANDS_XML {
+
+ public $doc; /**< Type: DOMDocument. Handle of current XML Document object */
+
+ /**
+ * Constructs an ANDS_XML object.
+ *
+ * @param $par_array Type: array.
+ * Array of request parameters for creating an ANDS_XML object.
+ * \see create_request.
+ */
+ function __construct($par_array) {
+ $this->doc = new DOMDocument("1.0","UTF-8");
+ // oai_node equals to $this->doc->documentElement;
+ $oai_node = $this->doc->createElement("OAI-PMH");
+ $oai_node->setAttribute("xmlns","http://www.openarchives.org/OAI/2.0/");
+ $oai_node->setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
+ $oai_node->setAttribute("xsi:schemaLocation","http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd");
+ $this->addChild($oai_node,"responseDate",gmdate("Y-m-d\TH:i:s\Z"));
+ $this->doc->appendChild($oai_node);
+ $this->create_request($par_array);
+ }
+
+ /**
+ * Add a child node to a parent node on a XML Doc: a worker function.
+ *
+ * @param $mom_node
+ * Type: DOMNode. The target node.
+ *
+ * @param $name
+ * Type: string. The name of child nade is being added
+ *
+ * @param $value
+ * Type: string. Text for the adding node if it is a text node.
+ *
+ * @return DOMElement $added_node
+ * The newly created node, can be used for further expansion.
+ * If no further expansion is expected, return value can be igored.
+ */
+
+ function addChild($mom_node,$name, $value='') {
+ $added_node = $this->doc->createElement($name,$value);
+ $added_node = $mom_node->appendChild($added_node);
+ return $added_node;
+ }
+
+
+ /**
+ * Create an OAI request node.
+ *
+ * @param $par_array Type: array
+ * The attributes of a request node. They describe the verb of the request and other associated parameters used in the request.
+ * Keys of the array define attributes, and values are their content.
+ */
+
+ function create_request($par_array) {
+ $request = $this->addChild($this->doc->documentElement,"request",MY_URI);
+ foreach($par_array as $key => $value) {
+ $request->setAttribute($key,$value);
+ }
+ }
+
+ /**
+ * Display a doc in a readable, well-formatted way for display or saving
+ */
+ function display() {
+ $pr = new DOMDocument();
+ $pr->preserveWhiteSpace = false;
+ $pr->formatOutput = true;
+ $pr->loadXML($this->doc->saveXML());
+ echo $pr->saveXML();
+ }
+}
+
+/**
+ * Generate an XML response when a request cannot be finished
+ *
+ * It has only one derived member function
+ */
+class ANDS_Error_XML extends ANDS_XML {
+ function __construct($par_array, $error_array) {
+ parent::__construct($par_array);
+
+ $oai_node = $this->doc->documentElement;
+ foreach($error_array as $e) {
+ list($code, $value) = explode("|", $e);
+ $node = $this->addChild($oai_node,"error",$value);
+ $node->setAttribute("code",$code);
+ }
+ }
+}
+
+/**
+ * Generate an XML response to a request if no error has occured
+ *
+ * This is the class to further develop to suits a publication need
+ */
+class ANDS_Response_XML extends ANDS_XML {
+ public $verbNode; /**< Type: DOMElement. Verb node itself. */
+ protected $verb; /**< Type: string. The verb in the request */
+
+ function __construct($par_array) {
+ parent::__construct($par_array);
+ $this->verb = $par_array["verb"];
+ $this->verbNode = $this->addChild($this->doc->documentElement,$this->verb);
+ }
+
+/** Add direct child nodes to verb node (OAI-PMH), e.g. response to ListMetadataFormats.
+ * Different verbs can have different required child nodes.
+ * \see create_record, create_header
+ * \see http://www.openarchives.org/OAI/2.0/openarchivesprotocol.htm.
+ *
+ * \param $nodeName Type: string. The name of appending node.
+ * \param $value Type: string. The content of appending node.
+ */
+ function add2_verbNode($nodeName, $value=null) {
+ return $this->addChild($this->verbNode,$nodeName,$value);
+ }
+
+ /**
+ * Create an empty \ node. Other nodes will be appended to it later.
+ */
+ function create_record() {
+ return $this->add2_verbNode("record");
+ }
+
+ /** Headers are enclosed inside of \ to the query of ListRecords, ListIdentifiers and etc.
+ *
+ * \param $identifier Type: string. The identifier string for node \.
+ * \param $timestamp Type: timestamp. Timestapme in UTC format for node \.
+ * \param $ands_class Type: mix. Can be an array or just a string. Content of \.
+ * \param $add_to_node Type: DOMElement. Default value is null.
+ * In normal cases, $add_to_node is the \ node created previously. When it is null, the newly created header node is attatched to $this->verbNode.
+ * Otherwise it will be attatched to the desired node defined in $add_to_node.
+ */
+ function create_header($identifier,$timestamp,$ands_class, $add_to_node=null) {
+ if(is_null($add_to_node)) {
+ $header_node = $this->add2_verbNode("header");
+ } else {
+ $header_node = $this->addChild($add_to_node,"header");
+ }
+ $this->addChild($header_node,"identifier",$identifier);
+ $this->addChild($header_node,"datestamp",$timestamp);
+ if (is_array($ands_class)) {
+ foreach ($ands_class as $setspec) {
+ $this->addChild($header_node,"setSpec",$setspec);
+ }
+ } else { $this->addChild($header_node,"setSpec",$ands_class); }
+ return $header_node;
+ }
+
+ /** Create metadata node for holding metadata. This is always added to \ node.
+ *
+ * \param $mom_record_node DOMElement. A node acts as the parent node.
+ *
+ * @return $meta_node Type: DOMElement.
+ * The newly created registryObject node which will be used for further expansion.
+ * metadata node itself is maintained by internally by the Class.
+ */
+ function create_metadata($mom_record_node) {
+ $meta_node = $this->addChild($mom_record_node,"metadata");
+ return $meta_node;
+ }
+
+ /** If there are too many records request could not finished a resumpToken is generated to let harvester know
+ *
+ * \param $token Type: string. A random number created somewhere?
+ * \param $expirationdatetime Type: string. A string representing time.
+ * \param $num_rows Type: integer. Number of records retrieved.
+ * \param $cursor Type: string. Cursor can be used for database to retrieve next time.
+ */
+ function create_resumpToken($token, $expirationdatetime, $num_rows, $cursor=null) {
+ $resump_node = $this->addChild($this->verbNode,"resumptionToken",$token);
+ if(isset($expirationdatetime)) {
+ $resump_node->setAttribute("expirationDate",$expirationdatetime);
+ }
+ $resump_node->setAttribute("completeListSize",$num_rows);
+ $resump_node->setAttribute("cursor",$cursor);
+ }
+}
+