From fbad6dd2fc2a8d526e77e85d62050790d4ca53ef Mon Sep 17 00:00:00 2001 From: Eduardo Humberto Date: Thu, 24 Jan 2019 18:44:55 -0200 Subject: [PATCH] create xml helpers classes (ref. #181) --- ...tainacan-rest-oaipmh-expose-controller.php | 4 +- .../class-tainacan-oaipmh-expose.php | 36 +++++- .../class-tainacan-xml-create.php | 113 ++++++++++++++++++ .../class-tainacan-xml-error.php | 23 ++++ .../class-tainacan-xml-response.php | 95 +++++++++++++++ 5 files changed, 263 insertions(+), 8 deletions(-) create mode 100644 src/oaipmh-expose/class-tainacan-xml-create.php create mode 100644 src/oaipmh-expose/class-tainacan-xml-error.php create mode 100644 src/oaipmh-expose/class-tainacan-xml-response.php diff --git a/src/api/endpoints/class-tainacan-rest-oaipmh-expose-controller.php b/src/api/endpoints/class-tainacan-rest-oaipmh-expose-controller.php index 019b17f31..758f83d9e 100644 --- a/src/api/endpoints/class-tainacan-rest-oaipmh-expose-controller.php +++ b/src/api/endpoints/class-tainacan-rest-oaipmh-expose-controller.php @@ -55,8 +55,8 @@ class REST_Oaipmh_Expose_Controller extends REST_Controller { default: $this->controller_oai->config(); - $this->controller_oai->errors[] = $this->controller_oai->oai_error('badArgument', $verb); - $this->controller_oai->oai_exit( [], $this->controller_oai->errors); + $this->controller_oai->errors[] = $this->controller_oai->oai_error('noVerb'); + $this->controller_oai->oai_exit( $request, $this->controller_oai->errors); break; } } diff --git a/src/oaipmh-expose/class-tainacan-oaipmh-expose.php b/src/oaipmh-expose/class-tainacan-oaipmh-expose.php index 6f9ae9fe6..fa82e8bfc 100644 --- a/src/oaipmh-expose/class-tainacan-oaipmh-expose.php +++ b/src/oaipmh-expose/class-tainacan-oaipmh-expose.php @@ -82,10 +82,10 @@ class OAIPMH_Expose { $this->expirationdatetime = gmstrftime('%Y-%m-%dT%TZ', time() + TOKEN_VALID); /** Where token is saved and path is included */ - //if(!is_dir(dirname(__FILE__).'/../../data/tokens/')){ - // mkdir(dirname(__FILE__).'/../../data/socialdb_tokens/'); - //} - //define('TOKEN_PREFIX', dirname(__FILE__).'/../../data/tokens/'); + $token_path = $this->create_token_dir(); + if($token_path){ + define('TOKEN_PREFIX', $token_path); + } $this->SETS = array( array('setSpec' => 'class:activity', 'setName' => 'Activities'), @@ -213,6 +213,7 @@ class OAIPMH_Expose { } } } + /** 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. @@ -221,6 +222,7 @@ class OAIPMH_Expose { 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(): ':_'. @@ -229,12 +231,14 @@ class OAIPMH_Expose { 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 */ @@ -264,18 +268,37 @@ class OAIPMH_Expose { /** Finish a request when there is an error: send back errors. */ function oai_exit($args,$errors) { header($this->CONTENT_TYPE); - $e = new ANDS_Error_XML($args, $errors); + $e = new XML_Error($args, $errors); $e->display(); exit(); } + /** + * LOG + */ + protected function create_token_dir() { + $upload_dir = wp_upload_dir(); + $upload_dir = trailingslashit( $upload_dir['basedir'] ); + $logs_folder = $upload_dir . 'tainacan/tokens'; + + if (!is_dir($logs_folder)) { + if (!mkdir($logs_folder)) { + return false; + } + } + + return $logs_folder; + } /** 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. + + /** + * + * Create a token file. * It has three parts which is separated by '#': cursor, extension of query, metadataPrefix. * Called by listrecords.php. */ @@ -293,6 +316,7 @@ class OAIPMH_Expose { fclose($fp); return $token; } + /** Read a saved ResumToken */ function readResumToken($resumptionToken) { $rtVal = false; diff --git a/src/oaipmh-expose/class-tainacan-xml-create.php b/src/oaipmh-expose/class-tainacan-xml-create.php new file mode 100644 index 000000000..ad9520aeb --- /dev/null +++ b/src/oaipmh-expose/class-tainacan-xml-create.php @@ -0,0 +1,113 @@ +doc = new \DOMDocument("1.0", "UTF-8"); + //to have indented output, not just a line + $this->doc->preserveWhiteSpace = false; + $this->doc->formatOutput = true; + + // ------------- Interresting part here ------------ + //creating an xslt adding processing line + //$xslt = $this->doc->createProcessingInstruction('xml-stylesheet', ' type="text/xsl" href="'. get_template_directory_uri().'/controllers/export/oai2.xsl"'); + + //var_dump($xslt); + //exit(); + //adding it to the xml + //this->doc->appendChild($xslt); + + + // 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. + */ + public function addChild($mom_node, $name, $value = '') { + $added_node = $this->doc->createElement($name, $value); + $added_node = $mom_node->appendChild($added_node); + + return $added_node; + } + + /** + * 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 addChildDC($mom_node, $name, $value = '') { + $added_node = $this->doc->createElementNS('http://purl.org/dc/elements/1.1/',$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(); + } +} diff --git a/src/oaipmh-expose/class-tainacan-xml-error.php b/src/oaipmh-expose/class-tainacan-xml-error.php new file mode 100644 index 000000000..a292278b7 --- /dev/null +++ b/src/oaipmh-expose/class-tainacan-xml-error.php @@ -0,0 +1,23 @@ +doc->documentElement; + + if($error_array){ + foreach ($error_array as $e) { + list($code, $value) = explode("|", $e); + $node = $this->addChild($oai_node, "error", $value); + $node->setAttribute("code", $code); + } + } + } +} diff --git a/src/oaipmh-expose/class-tainacan-xml-response.php b/src/oaipmh-expose/class-tainacan-xml-response.php new file mode 100644 index 000000000..52934684e --- /dev/null +++ b/src/oaipmh-expose/class-tainacan-xml-response.php @@ -0,0 +1,95 @@ +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. + */ + public 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. + */ + public 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. + */ + public 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. + */ + public 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. + */ + public 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); + } +} \ No newline at end of file