granularity=$granularity; } /** * \brief Conversion d'une date unix (nomnbres de secondes depuis le 01/01/1970) en date au format iso8601 selon la granularité * @param integer $time date au format unix (nombres de secondes depuis le 01/01/1970) * @return string date au format YYYY-MM-DD ou YYYY-MM-DDThh:mm:ssZ selon la granularité */ public function unixtime_to_iso8601($time) { $granularity=str_replace("T","\\T",$this->granularity); $granularity=str_replace("Z","\\Z",$granularity); $granularity=str_replace("YYYY","Y",$granularity); $granularity=str_replace("DD","d",$granularity); $granularity=str_replace("hh","H",$granularity); $granularity=str_replace("mm","i",$granularity); $granularity=str_replace("MM","m",$granularity); $granularity=str_replace("ss","s",$granularity); $date=date($granularity,$time); return $date; } /** * \brief Conversion d'une date au format iso8601 en date au format unix (nomnbres de secondes depuis le 01/01/1970) selon la granularité * @param string $date date au format iso8601 YYYY-MM-DD ou YYYY-MM-DDThh:mm:ssZ selon la granularité * @return integer date au format unix (nombres de secondes depuis le 01/01/1970) */ public function iso8601_to_unixtime($date) { $parts=explode("T",$date); if (count($parts)==2) { $day=$parts[0]; $time=$parts[1]; } else { $day=$parts[0]; } $days=explode("-",$day); if ($this->granularity=="YYYY-MM-DDThh:mm:ssZ") { if ($time) $times=explode(":",$time); if ($times[2]) { if (substr($times[2],strlen($times[2])-1,1)=="Z") $times[2]=substr($times[2],0,strlen($times[2])-1); } } $unixtime=mktime((int) $times[0],(int) $times[1],(int) $time[2],(int) $days[1],(int) $days[2],(int) $days[0]); return $unixtime; } } /* * oai_out_protocol * \brief Cette classe gère toute l'entrée sortie http et le protocol oai * Cette classe ne connait pas ses enregistrement ni leur types, elle les récupère grace à une instance d'une classe fille de oai_out_get_records * Norme du protocole: http://www.openarchives.org/OAI/openarchivesprotocol.html */ class oai_out_protocol { private $msg=array(); private $repositoryName=""; private $adminEmail; private $sets=array(); private $repositoryIdentifier=""; private $oai_out_get_records_object=NULL; private $known_metadata_formats=array( "pmb_xml_unimarc" => array( "metadataPrefix" => "pmb_xml_unimarc", "metadataNamespace" => "http://www.pmbservices.fr", "schema" => "http://www.pmbservices.fr/notice.xsd" ), "oai_dc" => array( "metadataPrefix" => "oai_dc", "metadataNamespace" => "http://www.openarchives.org/OAI/2.0/oai_dc/", "schema" => "http://www.openarchives.org/OAI/2.0/oai_dc.xsd" ) ); private $nb_results=100; private $token_life_expectancy=600; private $compression=true; private $deletion_support="no"; private $errored=false; private $xmlheader_sent=false; private $base_url=""; //Constructeur public function __construct($oai_out_get_records_object, &$msg, $repositoryName, $adminEmail, $sets, $repositoryIdentifier, $nb_results, $token_life_expectancy, $compression, $deletion_support, $additional_metadataformats, $base_url, $deletion_transient_duration = 0) { $this->msg = $msg; $this->oai_out_get_records_object=$oai_out_get_records_object; $this->repositoryName = $repositoryName; $this->adminEmail = $adminEmail; $this->sets = $sets; $this->repositoryIdentifier = $repositoryIdentifier; $this->nb_results = $nb_results; $this->token_life_expectancy = $token_life_expectancy; $this->compression = $compression; $this->deletion_support = $deletion_support; $this->known_metadata_formats = array_merge($this->known_metadata_formats, $additional_metadataformats); $this->base_url = $base_url; } //Renvoie l'entête public function oai_header() { global $verb; $iso8601 = new iso8601("YYYY-MM-DDThh:mm:ssZ"); $curdate = $iso8601->unixtime_to_iso8601(time()); $this->xmlheader_sent = true; return ' '.$curdate.' errored ? '' : 'verb="'.$verb.'"').'>'.XMLEntities($this->base_url).''; } //Renvoie le pied de page public function oai_footer() { return ''; } //Renvoie une erreur public function oai_error($error_code, $error_string) { global $charset; $this->errored = true; $buffer = XMLEntities($error_string); $buffer = $charset != "utf-8" ? utf8_encode($buffer) : $buffer; $result = ''.$buffer.''; return $result; } //Renvoie le résultat du verb Identify public function oai_identify() { global $charset; global $pmb_version_brut; global $metadataPrefix; $params = array_merge($_GET,$_POST); unset($params['verb']); unset($params['source_id']); unset($params['database']); if(count($params)) { return $this->oai_error('badArgument', $this->msg['badArgument']); } $result = ''; $buffer = XMLEntities($this->repositoryName); $buffer = $charset != 'utf-8' ? utf8_encode($buffer) : $buffer; $result .= ''.$buffer.''; $result .= ''.XMLEntities($this->base_url).''; $result .= '2.0'; $buffer = XMLEntities($this->adminEmail); $buffer = $charset != "utf-8" ? utf8_encode($buffer) : $buffer; $result .= ''.$buffer.''; $unix_earliestdate = $this->oai_out_get_records_object->get_earliest_datestamp(); $iso8601 = new iso8601("YYYY-MM-DDThh:mm:ssZ"); $earliestdate = $iso8601->unixtime_to_iso8601($unix_earliestdate); $result .= ''.$earliestdate.''; $result .= ''.$this->deletion_support.''; $result .= 'YYYY-MM-DDThh:mm:ssZ'; $buffer = XMLEntities($this->oai_out_get_records_object->get_sample_oai_identifier()); $buffer = $charset != "utf-8" ? utf8_encode($buffer) : $buffer; $buffer_ri = XMLEntities($this->oai_out_get_records_object->repository_identifier); $buffer_ri = $charset !="utf-8" ? utf8_encode($buffer_ri) : $buffer_ri; $result .= ' oai '.$buffer_ri.' : '.$buffer.' '; /* * A REVOIR $result .= ' PMB OAI Connector Erwan Martin emartin@sigb.net PMB Services '.$pmb_version_brut.' http://sigb.net '; */ $result .= ""; return $result; } //Renvoie le résultat du verb ListSets public function oai_list_sets() { global $charset; $result = ''; $result .= ''; foreach ($this->sets as $aset) { $buffer = XMLEntities($aset["caption"]); $buffer = $charset != "utf-8" ? utf8_encode($buffer) : $buffer; $result .= ' set_'.XMLEntities($aset["id"]).' '.$buffer.' '; } $result .= ''; return $result; } //Renvoie le résultat du verb ListRecords public function oai_list_records($root_tag='ListRecords') { global $charset, $dbh, $set, $resumptionToken, $from, $until; global $metadataPrefix; //pour compatibilité avec l'ancien fonctionnement $metadataPrefix = str_replace('convert:', 'convert_', $metadataPrefix); //Vérifications des parametres passes $params = array_merge($_GET,$_POST); unset($params['verb']); unset($params['source_id']); unset($params['database']); //resumptionToken is an exclusive argument //http://www.openarchives.org/OAI/2.0/openarchivesprotocol.htm#ListIdentifiers if(isset($params['resumptionToken'])) { unset($params['resumptionToken']); if(count($params)) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); $error.= $this->oai_error('badResumptionToken', $this->msg['badResumptionToken']); return $error; } } else if(!isset($params['metadataPrefix'])) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); return $error; } if (!$metadataPrefix) { $metadataPrefix = 'oai_dc'; } if ( (substr($metadataPrefix, 0, 8) != 'convert_') && !in_array($metadataPrefix, array_keys($this->known_metadata_formats))) { return $this->oai_error('cannotDisseminateFormat', sprintf($this->msg['cannotDisseminateFormat'], XMLEntities($metadataPrefix))); } if($root_tag=='ListIdentifiers') { $metadataPrefix = '__oai_identifier'; } //Un peu de ménage dans les tokens $sql = "DELETE FROM connectors_out_oai_tokens WHERE NOW() >= connectors_out_oai_token_expirationdate"; pmb_mysql_query($sql, $dbh); //On aura besoin d'un objet date iso magique $iso8601 = new iso8601("YYYY-MM-DDThh:mm:ssZ"); $result = ""; $max_records = $this->nb_results; $total_number_of_records = 0; $datefrom = false; $dateuntil = false; $deleted_fetch_count = 0; //Un token? Cherchons le dans la base de donnée et restaurons son environnement if ($resumptionToken) { $sql = "SELECT connectors_out_oai_token_environnement FROM connectors_out_oai_tokens WHERE connectors_out_oai_token_token = '".addslashes($resumptionToken)."'"; $res = pmb_mysql_query($sql, $dbh); if (!pmb_mysql_num_rows($res)) return $this->oai_error('badResumptionToken', $this->msg['badResumptionToken']); $row = pmb_mysql_fetch_assoc($res); $config = unserialize($row['connectors_out_oai_token_environnement']); $set_id_list = $config["sets"]; $datefrom = $config["datefrom"]; $dateuntil = $config["dateuntil"]; $metadataPrefix = $config["metadataprefix"]; $deleted_fetch_count = $config["deleted_fetch_count"]; } else { //Sinon config de début de recherche //Vérifions si on souhaite un set précis $error = false; if (isset($set) && $set) { $the_set_id = substr($set, 4); //On a un id, vérifions qu'il existe dans la liste $found=false; foreach ($this->sets as $aset) { if ($aset['id'] == $the_set_id) { $found = true; break; } } //Non? Erreur! if (!$found) { $error = $this->oai_error('noRecordsMatch', $this->msg['noRecordsMatch']); $error.= $this->oai_error('noSetHierarchy', $this->msg['noSetHierarchy']); } else { //Oui? On génère la "liste" des sets $set_id_list = array(0=>array('id' => $the_set_id)); } } else { //Sinon on fouille dans tous les sets $set_id_list = $this->sets; } if($error) { return $error; } $from_format = 0; if (isset($from) && $from) { if(preg_match("#^\d{4}-\d{2}-\d{2}$#",$from)) { $from_format = 1; }else if(preg_match("#^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$#",$from)) { $from_format = 2; } if(!$from_format) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); return $error; } $datefrom = $iso8601->iso8601_to_unixtime($from); if($datefrom < 0) $datefrom = 0; } $until_format = 0; if (isset($until) && $until) { if(preg_match("#^\d{4}-\d{2}-\d{2}$#",$until)) { $until_format = 1; }else if(preg_match("#^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$#",$until)) { $until_format = 2; } if(!$until_format) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); return $error; } $dateuntil = $iso8601->iso8601_to_unixtime($until); if($dateuntil < 0) $dateuntil = 0; } if($from_format && $until_format && ($from_format!=$until_format)) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); return $error; } } //Allons chercher les enregistrements grace à la classe associée $records = array(); $already_included_sets = array(); foreach($set_id_list as &$aset) { connector_out_set::set_already_included_sets($already_included_sets); if (!isset($aset["fetched_count"])) $aset["fetched_count"] = 0; //Si on en a déjà assez, on ne fait que compter (pour le total) if (count($records) >= $max_records) { $total_number_of_records += $this->oai_out_get_records_object->get_record_count($aset["id"], $datefrom, $dateuntil); $already_included_sets[] = $aset["id"]; continue; } //Si on a déjà tout extrait dans ce set, on compte et on continue if ($aset["fetched_count"]) { $current_set_count = $this->oai_out_get_records_object->get_record_count($aset["id"], $datefrom, $dateuntil); if ($aset["fetched_count"] == $current_set_count) { $total_number_of_records += $current_set_count; $already_included_sets[] = $aset["id"]; continue; } } //Allons chercher les enregistrements du set $number_to_fetch = $max_records - count($records); $set_records = $this->oai_out_get_records_object->get_records($aset["id"], $metadataPrefix, $aset["fetched_count"], $number_to_fetch, $datefrom, $dateuntil); $aset["fetched_count"] += count($set_records); $current_set_count = $this->oai_out_get_records_object->get_record_count($aset["id"], $datefrom, $dateuntil); $already_included_sets[] = $aset["id"]; $total_number_of_records += $current_set_count; foreach($set_records as $notice_id => $record_content) { if (!isset($records[$notice_id])) { $records[$notice_id] = $record_content; } } } //Si pas d'enregistrement, le protocol veut qu'on renvoie une erreur if (!$records) { return $this->oai_error('noRecordsMatch', $this->msg['noRecordsMatch']); } // On regarde les notices supprimées $deleted_records = oai_out_get_records::get_deleted_records(); $total_number_of_records += count($deleted_records); //Affichons les enregistrements $result .= " <".$root_tag.">"; foreach ($records as $arecords) { $result .= $arecords; } // On insère des notices supprimées s'il y a de la place $number_to_fetch = $max_records - count($records); if ($number_to_fetch) { for ($i = 0; $i < $deleted_fetch_count; $i++) { array_shift($deleted_records); } // On génère les enregistrements supprimés foreach ($deleted_records as $notice_id => $deleted_record) { $oai_record = ""; $oai_record .= ""; $oai_record .= '
oai:'.XMLEntities($this->oai_out_get_records_object->repository_identifier).':'.$notice_id.' '.$deleted_record['datestamp'].''; foreach ($deleted_record['sets'] as $aset_id) { $oai_record .= "set_".$aset_id.""; } $oai_record .= '
'; $oai_record .= "
"; $result.= $oai_record; $deleted_fetch_count++; $number_to_fetch--; if (!$number_to_fetch) { break; } } } //c'est moche pour l'instant, mais c'est validé pour Florent //pour les conversions persos, il faut le faire dans la feuille xslt if ($metadataPrefix == 'pmb_xml_unimarc' && $this->oai_out_get_records_object->oai_pmh_valid) { $result = str_replace('','',$result); } //Calculons le curseur $cursor = 0; foreach($set_id_list as $aset_c) { if (!isset($aset_c["fetched_count"])) continue; $cursor += $aset_c["fetched_count"]; } if ($cursor < $total_number_of_records) { //Enregistrons l'environnement $env=array( "sets" => $set_id_list, "datefrom" => $datefrom, "dateuntil" => $dateuntil, "metadataprefix" => $metadataPrefix, "deleted_fetch_count" => $deleted_fetch_count ); $token = md5(microtime()); $sql = "INSERT INTO connectors_out_oai_tokens (connectors_out_oai_token_token, connectors_out_oai_token_environnement, connectors_out_oai_token_expirationdate) VALUES ('".$token."', '".addslashes(serialize($env))."', NOW() + INTERVAL ".$this->token_life_expectancy." SECOND)"; pmb_mysql_query($sql, $dbh); $token_expiration_date = time() + $this->token_life_expectancy; $result .= ''.$token.''; } $result .= " "; return $result; } public function oai_list_identifier() { global $metadataPrefix; return $this->oai_list_records('ListIdentifiers'); } public function oai_get_record() { global $identifier; global $metadataPrefix; global $charset; //pour compatibilité avec l'ancien fonctionnement $metadataPrefix = str_replace('convert:', 'convert_', $metadataPrefix); if (!$metadataPrefix){ return $this->oai_error("badArgument", $this->msg['badArgument']); }elseif ( (substr($metadataPrefix, 0, 8) != 'convert_') && !in_array($metadataPrefix, array_keys($this->known_metadata_formats))) { return $this->oai_error('cannotDisseminateFormat', sprintf($this->msg['cannotDisseminateFormat'], XMLEntities($metadataPrefix))); } if(!$identifier){ return $this->oai_error("badArgument", $this->msg['badArgument']); } $record = $this->oai_out_get_records_object->get_record($identifier, $metadataPrefix); if ($record === false) { return $this->oai_error("idDoesNotExist", $this->msg['idDoesNotExist']); } //c'est moche pour l'instant, mais c'est validé pour Florent //pour les conversions persos, il faut le faire dans la feuille xslt if ($metadataPrefix == 'pmb_xml_unimarc' && $this->oai_out_get_records_object->oai_pmh_valid) { $record = str_replace('','',$record); } $result = ""; $result .= $record; $result .= ""; return $result; } //Renvoie le résultat du verb ListMetadataFormat public function oai_list_metadata_formats() { //Vérifications des parametres passes $params = array_merge($_GET,$_POST); $identifier=0; unset($params['verb']); unset($params['source_id']); unset($params['database']); if(isset($params['identifier'])) { $identifier = $params['identifier']; unset($params['identifier']); } if(count($params)) { $error = $this->oai_error('badArgument', $this->msg['badArgument']); return $error; } if(($identifier)&&($this->oai_out_get_records_object->get_record($identifier, 'oai_dc')==false)) { return $this->oai_error('idDoesNotExist', $this->msg['idDoesNotExist']); } //Sinon, il faut retourner la liste des ListMetadataFormats $result = ''; foreach ($this->known_metadata_formats as $aformat) { $result .= ' '.$aformat['metadataPrefix'].' '.$aformat['schema'].' '.$aformat['metadataNamespace'].' '; } $result .= ''; return $result; } public function sets_being_refreshed(){ global $charset, $dbh, $set; global $verb; $set_id_list = array(); //dans certains cas, cela n'a pas d'importance... switch($verb) { case 'ListRecords': case 'GetRecord': //Vérifions si on souhaite un set précis if (isset($set) && $set) { $the_set_id = substr($set, 4); //On a un id, vérifions qu'il existe dans la liste $found=false; foreach ($this->sets as $aset) { if ($aset["id"] == $the_set_id) { $found = true; break; } } //Non? Erreur! if ($found) { $set_id_list = array($the_set_id); } } else{ $set_id_list = array(); foreach($this->sets as $aset){ $set_id_list[] = $aset['id']; } } $query = "select being_refreshed from connectors_out_sets where being_refreshed=1 and connector_out_set_id in (".implode(",",$set_id_list).")"; $res = pmb_mysql_query($query); if(pmb_mysql_num_rows($res)>0){ $result = true; }else { $result = false; } break; default: $result = false; break; } return $result; } } /* * oai_out_get_records * \brief Cette classe utilisée par la classe oai_out_protocol permet de récupérer les metadatas des enregistrements (en UTF-8) * */ abstract class oai_out_get_records { public $error_code=""; public $error_string=""; private $msg=array(); protected $total_record_count_per_set=array(); protected static $deleted_records = array(); //Constructeur public function __construct(&$msg) { $this->msg=$msg; } //Renvoi un exemple d'identifier abstract public function get_sample_oai_identifier(); //Renvoi le datestamp du plus viel enregistrement abstract public function get_earliest_datestamp(); //Retourne le nombre d'enregistrements abstract public function get_record_count($set_id, $datefrom=false, $dateuntil=false); //Retrouve un enregistrement abstract public function get_record($rec_id, $format); //Liste les enregistrements abstract public function get_records($set_id="", $format, $first=false, $count=false, $datefrom=false, $dateuntil=false); public static function set_deleted_records($deleted_records = array()) { static::$deleted_records = $deleted_records; } public static function get_deleted_records() { return static::$deleted_records; } } /* * oai_out_get_records_notice * \brief Cette classe récupère les enregistrements pour un entrepot oai de notices * */ class oai_out_get_records_notice extends oai_out_get_records { public $oai_cache_duration = 84000; public $source_set_ids=array(); public $repository_identifier=""; public $notice_statut_deletion=0; public $include_items=0; public $include_links=array('genere_lien'=>0); protected $xslt = ""; public $deletion_management = 0; public $deletion_management_transient_duration = 0; public $use_items_update_date = 0; //Constructeur public function __construct(&$msg,$xslt="") { parent::__construct($msg); $this->xslt = $xslt; } public function get_sample_oai_identifier() { $result = 123456789; //Allons chercher un notice_id dans un set foreach ($this->source_set_ids as $asetid) { $co_set = new_connector_out_set_typed($asetid); $co_set->update_if_expired(); $values = $co_set->get_values(); //Set vide? On cherche dans un autre if (!$values) continue; //On en a un? On le prend $result = $values[0]; break; } $result = "oai:".$this->repository_identifier.":".$result; return $result; } public function get_earliest_datestamp() { //Allons chercher la date la plus vieille pour chaque set, et ensuite on prendra la plus vieille $current_min_unix_timestamp = time(); foreach ($this->source_set_ids as $asetid) { $co_set = new_connector_out_set_typed($asetid); $co_set->update_if_expired(); $set_min_date = $co_set->get_earliest_updatedate(); $current_min_unix_timestamp = $set_min_date < $current_min_unix_timestamp ? $set_min_date : $current_min_unix_timestamp; } return $current_min_unix_timestamp; } public function get_record($rec_id, $format) { global $charset; //Extractons l'id de la notice $notice_id = substr(strrchr($rec_id, ":"), 1); if (!$notice_id) return false; //Vérifions que la notice est bien dans les sets de la source $notice_sets = connector_out_set_noticecaddie::get_notice_setlist($notice_id); $notice_sets = array_intersect($notice_sets, $this->source_set_ids); if (!$notice_sets) return false; $co_set = new_connector_out_set_typed($notice_sets[0]); $co_set->update_if_expired(); $oai_cache = new external_services_converter_oairecord(1, $this->oai_cache_duration, $co_set->cache->cache_duration_in_seconds(), $this->source_set_ids, $this->repository_identifier, $this->notice_statut_deletion, $this->include_items,$this->xslt,$this->include_links, $this->deletion_management, $this->deletion_management_transient_duration); $records = $oai_cache->convert_batch(array($notice_id), $format, 'utf-8'); /* if ($records && $records[$notice_id]) if (($charset != 'utf-8') && !in_array($format, $this->utf8_formats)) $records[$notice_id] = utf8_encode($records[$notice_id]);*/ return $records ? $records[$notice_id] : false; } public function get_records($set_id=0, $format, $first=false, $count=false, $datefrom=false, $dateuntil=false) { global $charset; //Récupérons du cache les ids des notices $co_set = new_connector_out_set_typed($set_id); $co_set->update_if_expired(); //la méthode update_if_expired renvoie toutes les notices en cache //il faut donc ré-initialiser si des critères de date sont présents if ($datefrom || $dateuntil) { $co_set->cache->values = array(); } $notice_ids = $co_set->get_values($first, $count, $datefrom, $dateuntil, $this->use_items_update_date); $this->total_record_count[$set_id] = $co_set->get_value_count($datefrom, $dateuntil, $this->use_items_update_date); //Récupérons les enregistrements (avec gestion du cache) $oai_cache = new external_services_converter_oairecord(1, $this->oai_cache_duration, $co_set->cache->cache_duration_in_seconds(), $this->source_set_ids, $this->repository_identifier, $this->notice_statut_deletion, $this->include_items,$this->xslt,$this->include_links, $this->deletion_management, $this->deletion_management_transient_duration); $oai_cache->set_date_from($datefrom); $oai_cache->set_date_until($dateuntil); $records = $oai_cache->convert_batch($notice_ids, $format, 'utf-8'); /* if ($records) { if (($charset != 'utf-8') && !in_array($format, $this->utf8_formats)) { foreach ($records as $rnotice_id => $rrecord_content) { $records[$rnotice_id] = utf8_encode($rrecord_content); } } }*/ return $records; } public function get_record_count($set_id, $datefrom=false, $dateuntil=false) { if (!isset($this->total_record_count[$set_id])) { $co_set = new_connector_out_set_typed($set_id); $co_set->update_if_expired(); $this->total_record_count[$set_id] = $co_set->get_value_count($datefrom, $dateuntil, $this->use_items_update_date); } return $this->total_record_count[$set_id]; } } /* * external_services_converter_oairecord * \brief Cette classe génère les enregistrement oai de notices complets et les met en cache * */ class external_services_converter_oairecord extends external_services_converter { private $set_life_duration; private $source_set_ids=array(); private $repository_identifier=""; private $deleted_record_statut=0; private $include_items=0; private $xslt = ""; private $include_links=0; private $deletion_management; private $deletion_management_transient_duration; protected $date_from; protected $date_until; public function __construct($object_type, $life_duration, $set_life_duration, $source_set_ids, $repository_identifier, $deleted_record_statut, $include_items,$xslt="",$include_links, $deletion_management = 0, $deletion_management_transient_duration = 0) { parent::__construct($object_type, $life_duration); $this->set_life_duration = (int) $set_life_duration; $this->source_set_ids = $source_set_ids; $this->repository_identifier = $repository_identifier; $this->deleted_record_statut = $deleted_record_statut; $this->include_items = $include_items; $this->xslt = $xslt; $this->include_links = $include_links; $this->deletion_management = $deletion_management; $this->deletion_management_transient_duration = $deletion_management_transient_duration; } public function convert_batch($objects, $format, $target_charset='utf-8') { //Va chercher dans le cache les notices encore bonnes parent::convert_batch($objects, "oai_".$format, $target_charset); //Converti les notices qui doivent l'être $this->convert_uncachedoairecords($format, $target_charset); return $this->results; } public function convert_batch_to_oairecords($notices_to_convert, $format, $target_charset) { global $dbh; if (!$notices_to_convert) //Rien à faire? On fait rien return; //Allons chercher les dates et les statuts des notices $notice_datestamps=array(); $notice_statuts=array(); $notice_ids = $notices_to_convert; //Par paquets de 100 pour ne pas brusquer mysql $notice_idsz = array_chunk($notice_ids, 100); $iso8601 = new iso8601("YYYY-MM-DDThh:mm:ssZ"); foreach ($notice_idsz as $anotice_ids) { $sql = "SELECT notice_id, UNIX_TIMESTAMP(update_date) AS datestamp, statut FROM notices WHERE notice_id IN (".implode(",", $anotice_ids).")"; $res = pmb_mysql_query($sql, $dbh); while($row=pmb_mysql_fetch_assoc($res)) { $notice_datestamps[$row["notice_id"]] = $iso8601->unixtime_to_iso8601($row["datestamp"]); $notice_statuts[$row["notice_id"]] = $row["statut"]; } } //Si il existe un status correspondant à la suppression, on génère ces enregistrements et on les supprime de la liste à générer. if ($this->deleted_record_statut) { $deleted_records = array(); foreach ($notice_statuts as $notice_id => $anotice_statut) { if ($anotice_statut == $this->deleted_record_statut) { $notice_sets = connector_out_set_noticecaddie::get_notice_setlist($notice_id); $notice_sets = array_intersect($notice_sets, $this->source_set_ids); $deleted_records[$notice_id] = array( 'datestamp' => $datestamps[$notice_id], 'sets' => $notice_sets ); unset($notices_to_convert[array_search($notice_id, $notices_to_convert)]); } } oai_out_get_records::set_deleted_records($deleted_records); } else if ($this->deletion_management) { oai_out_get_records::set_deleted_records($this->get_deleted_records($this->source_set_ids, $notices_to_convert, $iso8601)); } //Convertissons les notices au format demandé si on ne souhaite pas uniquement les entêtes $only_identifier = $format == "__oai_identifier"; if (!$only_identifier) { $converter = new external_services_converter_notices(1, $this->set_life_duration); $converter->params["include_items"] = $this->include_items; $converter->params["include_links"] = $this->include_links; $metadatas = $converter->convert_batch($notices_to_convert, $format, $target_charset,$this->xslt); } //Fabriquons les enregistrements foreach ($notices_to_convert as $notice_id) { $notice_sets = connector_out_set_noticecaddie::get_notice_setlist($notice_id); $notice_sets = array_intersect($notice_sets, $this->source_set_ids); $oai_record = ""; if (!$only_identifier) $oai_record .= ""; $oai_record .= '
oai:'.XMLEntities($this->repository_identifier).':'.$notice_id.' '.$notice_datestamps[$notice_id].''; foreach ($notice_sets as $aset_id) { $oai_record .= "set_".$aset_id.""; } $oai_record .= '
'; if (!$only_identifier) { $oai_record .= ""; $oai_record .= $metadatas[$notice_id]; $oai_record .= ""; } if (!$only_identifier) $oai_record .= "
"; $this->results[$notice_id] = $oai_record; } } public function convert_uncachedoairecords($format, $target_charset='utf-8') { $notices_to_convert=array(); foreach ($this->results as $notice_id => $aresult) { if (!$aresult) { $notices_to_convert[] = $notice_id; } } $this->convert_batch_to_oairecords($notices_to_convert, $format, $target_charset); //Cachons les notices converties maintenant. foreach ($notices_to_convert as $anotice_id) { if ($this->results[$anotice_id]) $this->encache_value($anotice_id, $this->results[$anotice_id], "oai_".$format); } } /** * Récupère les notices marquées comme supprimées de tout les sets * @param array $notice_sets Tableau des identifiants de sets * @param array $records_not_deleted Tableau des identifiants de notices présentes dans au moins un set * @param iso8601 $iso8601 * * @return array Renvoie un tableau array({id_notice} => array('datestamp', 'sets')) */ private function get_deleted_records($record_sets, $records_not_deleted, $iso8601) { $deleted_records = array(); $query = "select num_notice, num_set, unix_timestamp(deletion_date) as datestamp from connectors_out_oai_deleted_records where num_set in (".implode(",", $record_sets).")"; if (count($records_not_deleted)) { $query.= " and num_notice not in (".implode(",", $records_not_deleted).")"; } if (!empty($this->date_from)) { $query.= " and deletion_date > FROM_UNIXTIME(".$this->date_from.")"; } if (!empty($this->date_until)) { $query.= " and deletion_date < FROM_UNIXTIME(".$this->date_until.")"; } if ($this->deletion_management == 1) { $query .= " and timestampdiff(second, deletion_date, now()) < ".$this->deletion_management_transient_duration; } $result = pmb_mysql_query($query); if ($result && pmb_mysql_num_rows($result)) { while ($row = pmb_mysql_fetch_object($result)) { $deleted_records[$row->num_notice]['sets'][] = $row->num_set; $timestamp = $iso8601->unixtime_to_iso8601($row->datestamp); if (!isset($deleted_records[$row->num_notice]['datestamp']) || ($timestamp > $deleted_records[$row->num_notice]['datestamp'])) { $deleted_records[$row->num_notice]['datestamp'] = $timestamp; } } } foreach ($deleted_records as $record_id => $record) { // Si la notice n'est pas supprimée de tous les sets if (count($not_deleted_sets = connector_out_set_noticecaddie::get_notice_setlist($record_id))) { // Si la notice n'est pas supprimée de tous les sets de la source, on l'enlève des notices supprimées if (count(array_intersect($not_deleted_sets, $record_sets))) { unset($deleted_records[$record_id]); } } } return $deleted_records; } public function set_date_from($date_from) { $this->date_from = $date_from; } public function set_date_until($date_until) { $this->date_until = $date_until; } } /* * oai_out_server * \brief Cette classe fait le lien entre toutes les autres et fait tourner le bouzin * */ class oai_out_server { private $msg=array(); /** * * @var oai_source */ private $oai_source_object = NULL; private $sets=array(); //Constructeur public function __construct(&$msg, &$oai_source_object) { $this->msg = $msg; $this->oai_source_object = $oai_source_object; } //Fait tourner le serveur public function process() { global $verb; //Pour ne pas avoir les entêtes définissant le fichier comme du xml, placer un &nx dans l'url (pour pouvoir utiliser le debugger zend par exemple) global $nx; if (!isset($nx)) header('Content-Type: text/xml'); $outsets = new connector_out_sets(); foreach ($outsets->sets as &$aset) { if (in_array($aset->id, $this->oai_source_object->included_sets)) $this->sets[] = array( "id" => $aset->id, "caption" => $aset->caption ); } //Créons l'object que le serveur va utiliser pour récupérer les enregistrements $get_records_objects = new oai_out_get_records_notice($this->msg,$this->oai_source_object->config['feuille_xslt']); $get_records_objects->oai_cache_duration = $this->oai_source_object->cache_complete_records_seconds; $get_records_objects->source_set_ids = $this->oai_source_object->included_sets; $get_records_objects->repository_identifier = $this->oai_source_object->repositoryIdentifier; $get_records_objects->notice_statut_deletion = $this->oai_source_object->link_status_to_deletion ? $this->oai_source_object->linked_status_to_deletion : 0; $get_records_objects->include_items = $this->oai_source_object->include_items ? $this->oai_source_object->include_items : 0; $get_records_objects->include_links = $this->oai_source_object->include_links ? $this->oai_source_object->include_links : 0; $get_records_objects->deletion_management = $this->oai_source_object->deletion_management ? $this->oai_source_object->deletion_management : 0; $get_records_objects->deletion_management_transient_duration = $this->oai_source_object->deletion_management_transient_duration ? $this->oai_source_object->deletion_management_transient_duration : 0; $get_records_objects->use_items_update_date = $this->oai_source_object->use_items_update_date ? $this->oai_source_object->use_items_update_date : 0; $get_records_objects->oai_pmh_valid = $this->oai_source_object->oai_pmh_valid ? $this->oai_source_object->oai_pmh_valid : 0; $additional_metadataformat = array(); foreach ($this->oai_source_object->allowed_admin_convert_paths as $convert_path) { $additional_metadataformat["convert_".$convert_path] = array( "metadataPrefix" => "convert_".$convert_path, "metadataNamespace" => "http://www.pmbservices.fr/"."convert_".$convert_path, "schema" => "http://www.pmbservices.fr/notice.xsd" ); } //Créons l'objet protocol if ($this->oai_source_object->link_status_to_deletion) { $deletion = "transient"; } else { switch ($this->oai_source_object->deletion_management) { case 0 : $deletion = "no"; break; case 1 : $deletion = "transient"; $deletion_transient_duration = $this->oai_source_object->deletion_management_transient_duration; break; case 2 : $deletion = "persistent"; break; default : $deletion = "none"; break; } } $base_url = $this->oai_source_object->baseURL; if (!$base_url) { $base_url = curPageBaseURL(); $base_url = substr($default_base_url, 0, strrpos($default_base_url, '/')+1); $base_url .= 'ws/connector_out.php?source_id='.$this->oai_source_object->id; } $oai_out_protocol = new oai_out_protocol($get_records_objects, $this->msg, $this->oai_source_object->repository_name, $this->oai_source_object->admin_email, $this->sets, $this->oai_source_object->repositoryIdentifier, $this->oai_source_object->chunksize, $this->oai_source_object->token_lifeduration, $this->oai_source_object->allow_gzip_compression, $deletion, $additional_metadataformat, $base_url, $deletion_transient_duration); //Si on peut compresser, on compresse if ($this->oai_source_object->allow_gzip_compression) $test = ob_start("ob_gzhandler"); $response = ''; //Si la source n'est pas bien configurée if (!$this->oai_source_object->repository_name || !$this->oai_source_object->admin_email || !$this->oai_source_object->repositoryIdentifier) { echo $oai_out_protocol->oai_header(); echo $oai_out_protocol->oai_error('unconfigured', $this->msg["unconfigured_source"]); echo $oai_out_protocol->oai_footer(); return; } //Le validateur precise de verifier l'unicite des arguments // /!\ Il faudra peut être prendre en compte les POSTS. $error = false; $qs_params = explode('&',$_SERVER['QUERY_STRING']); $args = array(); foreach($qs_params as $k=>$v) { $tmp = explode('=',$v); if(!$tmp[1]) { $tmp[1]=''; } $args[$tmp[0]] = $tmp[1]; } if(count($args) != count($qs_params)) { $response .= $oai_out_protocol->oai_error('badArgument', $this->msg['badArgument']); $error = true; } unset($qs_params); unset($args); unset($tmp); if(!$error) { //Sinon c'est parti //on regarde si un des sets manipulés n'est en cours de rafraississement, si oui, on bloque tout et fait patienter le client if($oai_out_protocol->sets_being_refreshed()){ header('HTTP/1.1 503 Service Temporarily Unavailable',true,503); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 10'); }else{ switch($verb) { case 'Identify': $response .= $oai_out_protocol->oai_identify(); break; case 'ListRecords': $response .= $oai_out_protocol->oai_list_records(); break; case 'GetRecord': $response .= $oai_out_protocol->oai_get_record(); break; case 'ListSets': $response .= $oai_out_protocol->oai_list_sets(); break; case 'ListIdentifiers': $response .= $oai_out_protocol->oai_list_identifier(); break; case 'ListMetadataFormats': $response .= $oai_out_protocol->oai_list_metadata_formats(); break; default: $response .= $oai_out_protocol->oai_error('badVerb', $this->msg['illegal_verb']); break; } } } //Header $response = $oai_out_protocol->oai_header() . $response; //Footer $response .= $oai_out_protocol->oai_footer(); echo $response; } } ?>