uri = $uri; if (!isset($args['verb']) || empty($args['verb'])) { $this->errors[] = new OAI2Exception('badVerb'); } else { $verbs = array('Identify', 'ListMetadataFormats', 'ListSets', 'ListIdentifiers', 'ListRecords', 'GetRecord'); if (in_array($args['verb'], $verbs)) { $this->verb = $args['verb']; unset($args['verb']); $this->args = $args; $this->identifyResponse = $identifyResponse; $this->listMetadataFormatsCallback = $callbacks['ListMetadataFormats']; $this->listSetsCallback = $callbacks['ListSets']; $this->listRecordsCallback = $callbacks['ListRecords']; $this->getRecordCallback = $callbacks['GetRecord']; $this->response = new OAI2XMLResponse($this->uri, $this->verb, $this->args); call_user_func(array($this, $this->verb)); } else { $this->errors[] = new OAI2Exception('badVerb'); } } } public function response() { if (empty($this->errors)) { return $this->response->doc; } else { $errorResponse = new OAI2XMLResponse($this->uri, $this->verb, $this->args); // Remove a tag do verbo quando tiver erro. $verbNode = $errorResponse->doc->getElementsByTagName($this->response->verb)->item(0); $verbNode->parentNode->removeChild($verbNode); $oai_node = $errorResponse->doc->documentElement; foreach ($this->errors as $e) { $node = $errorResponse->addChild($oai_node, "error", $e->getMessage()); $node->setAttribute("code", $e->getOAI2Code()); } return $errorResponse->doc; } } public function Identify() { if (count($this->args) > 0) { foreach ($this->args as $key => $val) { $this->errors[] = new OAI2Exception('badArgument'); } } else { foreach ($this->identifyResponse as $key => $val) { $this->response->addToVerbNode($key, $val); } } } public function ListMetadataFormats() { foreach ($this->args as $argument => $value) { if ($argument != 'identifier') { $this->errors[] = new OAI2Exception('badArgument'); } } if (isset($this->args['identifier'])) { $identifier = $this->args['identifier']; } else { $identifier = ''; } if (empty($this->errors)) { try { if ($formats = call_user_func($this->listMetadataFormatsCallback, $identifier)) { foreach ($formats as $key => $val) { $cmf = $this->response->addToVerbNode("metadataFormat"); $this->response->addChild($cmf, 'metadataPrefix', $key); $this->response->addChild($cmf, 'schema', $val['schema']); $this->response->addChild($cmf, 'metadataNamespace', $val['metadataNamespace']); } } else { $this->errors[] = new OAI2Exception('noMetadataFormats'); } } catch (OAI2Exception $e) { $this->errors[] = $e; } } } public function ListSets() { if (isset($this->args['resumptionToken'])) { if (count($this->args) > 1) { $this->errors[] = new OAI2Exception('badArgument'); } else { if ((int) $val + $this->token_valid < time()) { $this->errors[] = new OAI2Exception('badResumptionToken'); } } $resumptionToken = $this->args['resumptionToken']; } else { $resumptionToken = null; } if (empty($this->errors)) { if ($sets = call_user_func($this->listSetsCallback, $resumptionToken)) { foreach ($sets as $set) { $setNode = $this->response->addToVerbNode("set"); foreach ($set as $key => $val) { if ($key == 'setDescription') { $desNode = $this->response->addChild($setNode, $key); $des = $this->response->doc->createDocumentFragment(); $des->appendXML($val); $desNode->appendChild($des); } else { $this->response->addChild($setNode, $key, $val); } } } } else { $this->errors[] = new OAI2Exception('noSetHierarchy'); } } } public function GetRecord() { if (!isset($this->args['metadataPrefix'])) { $this->errors[] = new OAI2Exception('badArgument'); } else { $metadataFormats = call_user_func($this->listMetadataFormatsCallback); if (!isset($metadataFormats[$this->args['metadataPrefix']])) { $this->errors[] = new OAI2Exception('cannotDisseminateFormat'); } } if (!isset($this->args['identifier'])) { $this->errors[] = new OAI2Exception('badArgument'); } if (empty($this->errors)) { try { if ($record = call_user_func($this->getRecordCallback, $this->args['identifier'], $this->args['metadataPrefix'])) { $identifier = $record['identifier']; $datestamp = $this->formatDatestamp($record['datestamp']); $set = $record['set']; $status_deleted = (isset($record['deleted']) && ($record['deleted'] == 'true') && (($this->identifyResponse['deletedRecord'] == 'transient') || ($this->identifyResponse['deletedRecord'] == 'persistent'))); $cur_record = $this->response->addToVerbNode('record'); $cur_header = $this->response->createHeader($identifier, $datestamp, $set, $cur_record); if ($status_deleted) { $cur_header->setAttribute("status", "deleted"); } else { $this->add_metadata($cur_record, $record); } } else { $this->errors[] = new OAI2Exception('idDoesNotExist'); } } catch (OAI2Exception $e) { $this->errors[] = $e; } } } public function ListIdentifiers() { $this->ListRecords(); } public static function log($content) { file_put_contents('/tmp/saida.txt', var_export($content, true) . "\n", FILE_APPEND); } public function ListRecords() { $MIOLO = MIOLO::getInstance(); $busPreference = $MIOLO->getBusiness('gnuteca3', 'BusPreference'); $limit = $busPreference->getPreference('gnuteca3', 'OAI_PMH_RECORDS_LIMIT', false); $maxItems = $limit ? $limit : 999; $deliveredRecords = 0; $metadataPrefix = $this->args['metadataPrefix']; $from = isset($this->args['from']) ? $this->args['from'] : NULL; $until = isset($this->args['until']) ? $this->args['until'] : NULL; $set = isset($this->args['set']) ? $this->args['set'] : NULL; if (isset($this->args['resumptionToken'])) { if (count($this->args) > 2) { $this->errors[] = new OAI2Exception('badArgument'); } else { //if ((int) $val + $this->token_valid < time()) if ( false ) { //$this->errors[] = new OAI2Exception('badResumptionToken'); } else { if (!file_exists($this->token_prefix . $this->args['resumptionToken'])) { $this->errors[] = new OAI2Exception('badResumptionToken'); } else { if ($readings = $this->readResumptionToken($this->token_prefix . $this->args['resumptionToken'])) { list($deliveredRecords, $metadataPrefix, $from, $until, $set) = $readings; $this->args['from'] = $from; $this->args['until'] = $until; $this->args['set'] = $set; $this->args['metadataPrefix'] = $metadataPrefix; } else { $this->errors[] = new OAI2Exception('badResumptionToken'); } } } } } else { if (!isset($this->args['metadataPrefix'])) { $this->errors[] = new OAI2Exception('badArgument'); } else { $metadataFormats = call_user_func($this->listMetadataFormatsCallback); if (!isset($metadataFormats[$this->args['metadataPrefix']])) { $this->errors[] = new OAI2Exception('cannotDisseminateFormat'); } } if (isset($this->args['from'])) { if (!$this->checkDateFormat($this->args['from'])) { $this->errors[] = new OAI2Exception('badArgument'); } } if (isset($this->args['until'])) { if (!$this->checkDateFormat($this->args['until'])) { $this->errors[] = new OAI2Exception('badArgument'); } } } if (empty($this->errors)) { try { $records_count = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, true); $records = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, false, $deliveredRecords, $maxItems); foreach ($records as $record) { $identifier = $record['identifier']; // $datestamp = $record['datestamp']; $datestamp = $this->formatDatestamp($record['datestamp']); $setspec = $record['set']; $status_deleted = (isset($record['deleted']) && ($record['deleted'] === true) && (($this->identifyResponse['deletedRecord'] == 'transient') || ($this->identifyResponse['deletedRecord'] == 'persistent'))); if ($this->verb == 'ListRecords') { $cur_record = $this->response->addToVerbNode('record'); // Cria o cabeçalho, $cur_header = $this->response->createHeader($identifier, $datestamp, $setspec, $cur_record); if (!$status_deleted) { $this->add_metadata($cur_record, $record); } } else { // for ListIdentifiers, only identifiers will be returned. $cur_header = $this->response->createHeader($identifier, $datestamp, $setspec); } if ($status_deleted) { $cur_header->setAttribute("status", "deleted"); } } // ATENÇÃO: Cria o token caso seja feita uma coleta seletiva que entregue menos registros que o padrão, // o uso do token faz utilizar os mesmos filtros, se com o mesmo filtro tenha quantidade de registros diferentes, o token é renovado. // Will we need a new ResumptionToken? if ($records_count - $deliveredRecords > $maxItems) { $deliveredRecords += $maxItems; $restoken = $this->createResumptionToken($deliveredRecords); $expirationDatetime = gmstrftime('%Y-%m-%dT%TZ', time() + $this->token_valid); } elseif (isset($args['resumptionToken'])) { // Last delivery, return empty ResumptionToken $restoken = null; $expirationDatetime = null; } if (isset($restoken)) { $this->response->createResumptionToken($restoken, $expirationDatetime, $records_count, $deliveredRecords); } } catch (OAI2Exception $e) { $this->errors[] = $e; } } } private function add_metadata($cur_record, $record) { $meta_node = $this->response->addChild($cur_record, "metadata"); $collectionNode = $this->response->addChild($meta_node, 'collection'); $recordNode = $this->response->addChild($collectionNode, 'record'); $dataFieldNode = array(); $leader = ''; $size = 0; foreach ( $record['tags'] as $name => $value ) { if ( is_object($value) ) { $value = (array) $value; } // Testa se é leader. if ( $value['fieldid'] == '000' ) { $leader = self::xmlEscape($value['content']); $size += 24; } elseif ( strlen($value['subfieldid']) == 0 || ( in_array($value['fieldid'], array('001', '003', '005', '008')) ) ) // Testa se é um campo de controle. { // Soma cabeçalho 12 + separdor de conteúdo 1 + conteúdo. $size += 13 + strlen(self::xmlEscape($value['content'])); $controlField = $this->response->addChild($recordNode, 'controlfield', self::xmlEscape($value['content'])); $controlField->setAttribute('tag', $value['fieldid']); } else { if ( !isset($dataFieldNode[$value['fieldid']][$value['line']]) ) { $dataFieldNode[$value['fieldid']][$value['line']] = $this->response->addChild($recordNode, 'datafield'); // Soma cabeçalho 12+ separador do campo 1 + 2 indicadores. $size += 15; // Ajusta indicador. $value['indicator1'] = $value['indicator1'] == "" ? ' ' : $value['indicator1']; $value['indicator2'] = $value['indicator2'] == "" ? ' ' : $value['indicator2']; $dataFieldNode[$value['fieldid']][$value['line']]->setAttribute('tag', $value['fieldid']); $dataFieldNode[$value['fieldid']][$value['line']]->setAttribute('ind1', $value['indicator1']); $dataFieldNode[$value['fieldid']][$value['line']]->setAttribute('ind2', $value['indicator2']); } // Subcampos. $subfieldNode = $this->response->addChild($dataFieldNode[$value['fieldid']][$value['line']], 'subfield', self::xmlEscape($value['content'])); $subfieldNode->setAttribute('code', $value['subfieldid']); // Soma ^a (separador + subcampo) + conteúdo. $size += 2 + strlen(self::xmlEscape($value['content'])); } } // Separador de material. $size += 1; // Adiciona leader. if ( strlen($leader) ) { $leader = str_pad($size, 5, '0', STR_PAD_LEFT) . substr($leader, 5); $controlField = $this->response->addChild($recordNode, 'leader', $leader); } } private static function xmlEscape($string) { return str_replace(array('&', '<', '>', '\'', '"'), array('&', '<', '>', ''', '"'), $string); } private function createResumptionToken($delivered_records) { list($usec, $sec) = explode(" ", microtime()); $token = ((int) ($usec * 1000) + (int) ($sec * 1000)); $fp = fopen($this->token_prefix . $token, 'w'); if ($fp == false) { exit("Cannot write. Writer permission needs to be changed."); } $metadataPrefix = $this->args['metadataPrefix']; fputs($fp, "$delivered_records#"); fputs($fp, "$metadataPrefix#"); fputs($fp, "{$this->args['from']}#"); fputs($fp, "{$this->args['until']}#"); fputs($fp, "{$this->args['set']}#"); fclose($fp); return $token; } private function readResumptionToken($resumptionToken) { $rtVal = false; $fp = fopen($resumptionToken, 'r'); if ($fp != false) { $filetext = fgets($fp, 255); $textparts = explode('#', $filetext); fclose($fp); unlink($resumptionToken); $rtVal = array_values($textparts); } return $rtVal; } /** * All datestamps used in this system are GMT even * return value from database has no TZ information */ private function formatDatestamp($datestamp) { return date("Y-m-d\TH:i:s\Z", strtotime($datestamp)); } /** * The database uses datastamp without time-zone information. * It needs to clean all time-zone informaion from time string and reformat it */ private function checkDateFormat($date) { $date = str_replace(array("T", "Z"), " ", $date); $time_val = strtotime($date); if (!$time_val) { return false; } if (strstr($date, ":")) { return date("Y-m-d H:i:s", $time_val); } else { return date("Y-m-d", $time_val); } } }