Subversion Repositories eFlore/Applications.cel

Compare Revisions

No changes between revisions

Ignore whitespace Rev 2458 → Rev 2459

/trunk/jrest/services/ImportXLS.php
30,8 → 30,8
error_reporting(-1);
ini_set('html_errors', 0);
ini_set('xdebug.cli_color', 2);
require_once('lib/PHPExcel/Classes/PHPExcel.php');
require_once('FormateurGroupeColonne.php');
require_once 'lib/PHPExcel/Classes/PHPExcel.php';
require_once 'FormateurGroupeColonne.php';
 
date_default_timezone_set('Europe/Paris');
 
/trunk/jrest/services/CelWidgetExport.php
21,11 → 21,11
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
// la sortie est binaire (xls), mais OLE n'est pas compatible E_ALL en PHP-5.4
error_reporting(error_reporting() & ~E_STRICT);
require_once("lib/OLE.php");
require_once("lib/Spreadsheet/Excel/Writer.php");
require_once 'lib/OLE.php';
require_once 'lib/Spreadsheet/Excel/Writer.php';
 
class CelWidgetExport extends Cel {
 
private $nom_fichier_export = 'cel_export';
// certains paramètres apparaissent plusieurs fois car ils ont des alias
// dans certains widgets
57,23 → 57,23
'transmission' => 'transmission',
'obsids' => 'obsids',
);
private $limite_decoupage_defaut = 9000;
 
private $limite_decoupage_defaut = 9000;
 
private $format = 'csv';
 
public $id_utilisateur = null;
 
public $export_prive = false;
 
// un cache, initialisé par certaines fonctions de préchargement, à la manière
// de ce qui est fait par FormateurGroupeColonne
static $cache = Array();
 
public function getRessource() {
return $this->getElement(array());
}
 
/**
* Méthode appelée avec une requête de type GET.
*/
82,7 → 82,7
case 'calcul':
$this->getCalcul();
break;
 
case 'export':
$this->getExport();
break;
90,11 → 90,11
$this->getExport();
}
}
 
private function getCalcul() {
$criteres = $this->traiterParametresAutorises($_GET);
$criteres['transmission'] = 1;
 
// Définit si l'on exporte les obs privées ainsi que les champs étendus privés
$this->export_prive = $this->doitEtPeutExporterObsPrivees($criteres);
if($this->export_prive) {
102,22 → 102,22
$this->id_utilisateur = $criteres['id_utilisateur'];
}
$chercheur_observations = new RechercheObservation($this->config);
 
$numero_page = isset($criteres['debut']) ? $criteres['debut'] : 0;
$limite = isset($criteres['limite']) ? $criteres['limite'] : 0;
$colonnes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
 
unset($criteres['limite']);
unset($criteres['debut']);
unset($criteres['format']);
unset($criteres['colonnes']);
 
$nb_observations = $chercheur_observations->compterObservations(null, $criteres);
$limite_decoupage = $this->calculerNbLignesMaxParFichier(explode(',', $colonnes));
 
$url_telechargements = array();
$intervalle = 0;
 
$params_url = $criteres;
unset($params_url['transmission']);
do {
129,13 → 129,13
$intervalle += $limite_decoupage;
$nb_observations -= $limite_decoupage;
} while($nb_observations > 0);
 
$this->envoyerJson($url_telechargements);
}
 
private function calculerNbLignesMaxParFichier($colonnes) {
$limite = $this->limite_decoupage_defaut;
$limite = $this->limite_decoupage_defaut;
 
switch($this->format) {
case 'csv':
$limite = 20000;
150,7 → 150,7
 
return $limite;
}
 
private function getExport() {
$criteres = $this->traiterParametresAutorises($_GET);
// ne pas faire de super-requête en cas d'absence de paramètres
166,7 → 166,7
$this->id_utilisateur = $criteres['ce_utilisateur'];
}
$chercheur_observations = new RechercheObservation($this->config);
 
$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
$groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
177,7 → 177,7
 
if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
implode(',', $criteres['obsids']));
 
unset($criteres['limite']);
unset($criteres['debut']);
unset($criteres['format']);
225,7 → 225,7
default:
}
}
 
protected function traiterParametresAutorises(Array $parametres) {
$parametres_traites = array();
$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
238,7 → 238,7
$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
return $parametres_traites;
}
 
private function envoyerCsv($csv) {
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
245,12 → 245,12
echo $csv;
exit;
}
 
private function envoyerXls($workbook) {
$workbook->close();
exit;
}
 
private function convertirEnCsv(&$data, $colonnes) {
$chemin_temp = "php://temp";
$outstream = fopen($chemin_temp, 'r+');
266,7 → 266,7
}
 
// header
fputcsv($outstream, $intitule_champs, ',', '"');
fputcsv($outstream, $intitule_champs, ',', '"');
// lignes
foreach($data as &$ligne) {
$ligne = self::filtrerDonneesSensibles($ligne);
278,11 → 278,11
fclose($outstream);
return $csv;
}
 
private function convertirEnXls(&$data, $colonnes) {
$this->extendSpreadsheetProductor = new SpreadsheetProductor();
$this->extendSpreadsheetProductor->initSpreadsheet();
 
$workbook = new Spreadsheet_Excel_Writer();
// avant la définition du titre de la worksheet !
$workbook->setVersion(8);
291,7 → 291,7
$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
$worksheet->setInputEncoding('utf-8');
$workbook->send($this->nom_fichier_export.'.xls');
 
$nb_lignes = 1;
 
$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
306,7 → 306,7
 
// header
$indice = 0;
foreach ($intitule_champs as &$intitule) {
foreach ($intitule_champs as &$intitule) {
$worksheet->write(0,$indice++,$intitule);
}
 
321,7 → 321,7
}
return $workbook;
}
 
private function convertirEnPdf(&$observations) {
if(count($observations) > 300) die('erreur: trop de données');
//require_once('GenerateurPDF.php');
329,7 → 329,7
$pdf->export($observations);
return $pdf;
}
 
static function filtrerDonneesSensibles($ligne) {
if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
$ligne['latitude'] = '';
337,12 → 337,12
}
return $ligne;
}
 
private function doitEtPeutExporterObsPrivees($criteres) {
return isset($criteres['ce_utilisateur']) &&
return isset($criteres['ce_utilisateur']) &&
$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
}
 
private function peutExporterObsPrivees($id_utilisateur) {
$gestion_utilisateur = new User($this->config);
$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
/trunk/jrest/lib/RechercheImage.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/GenerateurPDF.php
File deleted
/trunk/jrest/lib/NameParser.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/ChampEtendu.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/RechercheInfosTaxonBeta.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/RechercheInfosTaxon.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/CartoGroupage.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/Cel.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/FormateurGroupeColonne.php
File deleted
/trunk/jrest/lib/Bdd2.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/GestionObservation.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/Bdd.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/GestionChampsEtendus.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/ImageRecreation.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/Decoupage.class.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/DecoupageNomLatin.class.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/RechercheObservation.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/GestionMotsClesChemin.php
File deleted
/trunk/jrest/lib/ExtracteurMetadonnees.php
File deleted
\ No newline at end of file
/trunk/jrest/lib/GestionImage.php
File deleted
\ No newline at end of file
/trunk/jrest/bibliotheque/RechercheImage.php
New file
0,0 → 1,322
<?php
// ATTENTION ! Classe compatible uniquement avec nouveau format de bdd du cel //
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author Aurélien Peronnet <aurelien@tela-botania.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
* in : utf8
* out : utf8
*
* Librairie recherche d'images a partir de divers critères
*
*/
class RechercheImage extends Cel {
 
function rechercherImagesEtObservationAssociees($id_utilisateur = null, $criteres = array(), $debut = 0, $limite = 50) {
$images_trouvees = $this->rechercherImages($id_utilisateur, $criteres, $debut, $limite);
 
$retour = array();
foreach($images_trouvees as $image) {
$image['id_observation'] = $this->obtenirInformationsObservationsAssociees($image['ce_utilisateur'],$image['id_image']);
$retour[] = $image ;
}
return $retour;
}
 
public function rechercherImages($id_utilisateur = null, $criteres = array(), $debut = 0 , $limite = 50) {
$ordre = (isset($criteres['tri']) && $criteres['tri']) ? $criteres['tri'] : 'ci.ordre';
unset($criteres['tri']);
$direction = (isset($criteres['dir']) && $criteres['dir']) ? $criteres['dir'] : 'ASC';
unset($criteres['dir']);
 
$debut = ($debut < 0) ? 0 : $debut;
$requete_recherche_images = 'SELECT ci.* ';
 
if ($this->doitJoindreTableObs($criteres)) {
$requete_recherche_images .= $this->fabriquerRequeteJointureObs();
$requete_recherche_images .= ($id_utilisateur != null) ? 'AND ci.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
} else {
$requete_recherche_images .= 'FROM cel_images ci ';
$requete_recherche_images .= ($id_utilisateur != null) ? 'WHERE ci.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
}
 
$sous_requete_recherche = $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
$requete_recherche_images .= $sous_requete_recherche;
 
$requete_recherche_images .= ' ORDER BY '.$ordre.' '.$direction.' LIMIT '.$debut.','.$limite ;
 
$resultats_images = array();
$resultats_images = Cel::db()->requeter($requete_recherche_images);
 
return $resultats_images;
}
 
public function compterImages($id_utilisateur = null, $criteres = array()) {
$ordre = (isset($criteres['tri']) && $criteres['tri']) ? $criteres['tri'] : 'ci.ordre';
unset($criteres['tri']);
$direction = (isset($criteres['dir']) && $criteres['dir']) ? $criteres['dir'] : 'ASC';
unset($criteres['dir']);
 
$requete_recherche_images = 'SELECT COUNT(*) AS nb_images ';
 
if ($this->doitJoindreTableObs($criteres)) {
$requete_recherche_images .= $this->fabriquerRequeteJointureObs();
$requete_recherche_images .= ($id_utilisateur != null) ? 'AND ci.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
} else {
$requete_recherche_images .= 'FROM cel_images ci ';
$requete_recherche_images .= ($id_utilisateur != null) ? 'WHERE ci.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
}
$sous_requete_recherche = $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
 
$requete_recherche_images .= $sous_requete_recherche;
$nb_images = 0;
 
$resultat_requete_nombre_images = Cel::db()->requeter($requete_recherche_images);
 
if ($resultat_requete_nombre_images && is_array($resultat_requete_nombre_images) && count($resultat_requete_nombre_images) > 0) {
$nb_images = $resultat_requete_nombre_images[0]['nb_images'];
}
 
return $nb_images;
}
 
private function doitJoindreTableObs($criteres = array()) {
$criteres_obs = array('zone_geo','ce_zone_geo','taxon','transmission','recherche');
return count(array_intersect(array_keys($criteres),$criteres_obs)) > 0;
}
 
private function fabriquerRequeteJointureObs() {
$requete = 'FROM cel_images ci '.
'LEFT JOIN cel_obs co '.
'ON ci.ce_observation = co.id_observation '.
'WHERE 1 ';
return $requete;
}
 
public function obtenirInformationsObservationsAssociees($id_utilisateur, $id_image) {
$requete = 'SELECT ce_observation FROM cel_images WHERE id_image = '.$id_image;
$resultats = Cel::db()->requeter($requete);
 
$idsObsListe = array();
foreach ($resultats as $liaison) {
$idsObsListe[] = $liaison['ce_observation'];
}
$ids_obs = implode(',', $idsObsListe);
 
$infos_obs = '';
if (trim($ids_obs) != '') {
$requete = 'SELECT * FROM cel_obs WHERE id_observation IN ('.$ids_obs.') AND ce_utilisateur = "'.$id_utilisateur.'"';
$resultats = Cel::db()->requeter($requete);
 
foreach ($resultats as $obs_liee) {
$infos_obs .= $obs_liee['ordre'].'#'.$obs_liee['nom_sel'].'#'.$obs_liee['transmission'].';;' ;
}
}
return $infos_obs;
}
 
private function fabriquerSousRequeteRecherche($id_utilisateur, $criteres) {
$sous_requete = ' AND ';
foreach($criteres as $nom => $valeur) {
if($valeur == null || trim($nom) == "" || trim($valeur) == "") {
continue;
}
 
switch($nom) {
case "id_image";
$sous_requete .= 'ci.id_image = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ';
break;
case "mots_cles";
$sous_requete .= $this->creerSousRequeteMotsCles($valeur);
break;
case "id_mots_cles";
$liste_mc = '"'.str_replace(';','","',$valeur).'"';
$tpl_sous_requete = GestionMotsClesChemin::obtenirTemplateRequeteMotsClesIds('images');
$sous_requete .= 'id_image IN ('.sprintf($tpl_sous_requete, $liste_mc).')';
$sous_requete .= ' AND ' ;
break;
case "commentaire":
$mots_comment_liste = explode(" " , $valeur) ;
 
foreach($mots_comment_liste as $mot_comment) {
$mot_comment = trim($mot_comment) ;
$sous_requete .= 'ci.'.$nom.' LIKE '.Cel::db()->proteger('%'.$mot_comment.'%') ;
$sous_requete .= ' AND ' ;
}
break;
case "annee":
case "mois":
case "jour":
$sous_requete .= $this->fabriquerSousRequeteRechercheDate($nom, $valeur) ;
$sous_requete .= ' AND ' ;
break;
case "tampon":
$ids_tampon = rtrim($valeur, ',') ;
$sous_requete .= 'ci.id_images IN ( '.Cel::db()->proteger($ids_tampon).')' ;
break;
case "recherche":
$sous_requete .= $this->fabriquerSousRequeteRechercheGenerale($id_utilisateur, $valeur);
$sous_requete .= ' AND ';
break;
case "transmission":
$sous_requete .= 'co.transmission = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ';
break;
case "taxon":
$valeur = str_replace('indetermine','null',$valeur);
$sous_requete .= ' (';
$sous_requete .= 'co.nom_ret LIKE '.Cel::db()->proteger($valeur.'%') ;
$sous_requete .= ' OR ' ;
$sous_requete .= 'co.nom_sel LIKE '.Cel::db()->proteger($valeur.'%') ;
$sous_requete .= ') AND ' ;
break;
case "auteur":
$sous_requete .= '(ci.ce_utilisateur LIKE '.Cel::db()->proteger($valeur.'%').' OR '.
'ci.courriel_utilisateur LIKE '.Cel::db()->proteger($valeur.'%').' OR '.
'ci.nom_utilisateur LIKE '.Cel::db()->proteger($valeur.'%').' OR '.
'ci.prenom_utilisateur LIKE '.Cel::db()->proteger($valeur.'%').
') AND ';
break;
case "ce_zone_geo":
if($valeur == "NULL") {
$sous_requete .= "(co.ce_zone_geo IS NULL OR co.ce_zone_geo = '')";
} else {
$sous_requete .= '(co.ce_zone_geo LIKE '.(is_numeric($valeur) ? Cel::db()->proteger('INSEE-C:'.$valeur.'%') : Cel::db()->proteger($valeur)).') ';
}
break;
case "zone_geo":
if($valeur == "NULL") {
$sous_requete .= "(co.zone_geo IS NULL OR co.zone_geo = '')";
} else {
$sous_requete .= '(co.zone_geo = '.Cel::db()->proteger($valeur).') ';
}
break;
case "famille":
$sous_requete .= 'co.famille = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ' ;
break;
default:
$sous_requete .= 'ci.'.$nom.' = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ' ;
break;
}
}
$sous_requete = rtrim($sous_requete,' AND ');
return $sous_requete;
}
 
private function fabriquerSousRequeteRechercheGenerale($id_utilisateur, $chaine_recherche) {
if (trim($chaine_recherche) == '') {
return '';
}
 
$chaine_recherche = strtolower($chaine_recherche);
$chaine_recherche = str_replace(' ','_',$chaine_recherche);
 
$requete = ' ('.
'ci.nom_original LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.nom_ret LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.nom_sel LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.zone_geo LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.ce_zone_geo LIKE '.Cel::db()->proteger('%'.$chaine_recherche.'%').' OR '.
//TODO: recherche multicriteres sur mots clés texte ne fonctionne pas à cause de la jointure
//'ci.mots_cles_texte LIKE '.Cel::db()->proteger('%'.$chaine_recherche.'%').' OR '.
'ci.ce_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'ci.courriel_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'ci.nom_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'ci.prenom_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' '.
') ';
 
return $requete;
}
 
private function fabriquerSousRequeteRechercheDate($intervalle, $valeur) {
$correspondance_champ = array('annee' => 'YEAR','mois' => 'MONTH','jour' => 'DAY');
$requete_recherche_date = '';
 
if (is_numeric($valeur) && $valeur != "00") {
$requete_recherche_date = '('.$correspondance_champ[$intervalle].'(ci.date_prise_de_vue) = '.Cel::db()->proteger($valeur).') ';
} else {
$requete_recherche_date = '(ci.date_prise_de_vue IS NULL OR ci.date_prise_de_vue = "0000-00-00")';
}
 
return $requete_recherche_date;
}
 
private function creerSousRequeteMotsCles($mot_cle) {
//TODO: une requête plus efficace serait possible en utilisant
// les vraies tables de mots clés et en faisant disparaitre ce champ maudit
$requete = '';
if (preg_match('/.*OU.*/', $mot_cle)) {
$mots_cles_tab = explode('OU',$mot_cle);
foreach($mots_cles_tab as $mot_cle_item) {
$requete .= '(ci.mots_cles_texte LIKE '.Cel::db()->proteger('%'.$mot_cle_item.'%').') OR ';
}
$requete = '('.rtrim($requete,'OR ').')';
} else if (preg_match('/.*ET.*/', $mot_cle)) {
$mots_cles_tab = explode('ET',$mot_cle);
foreach($mots_cles_tab as $mot_cle_item) {
$requete .= '(ci.mots_cles_texte LIKE '.Cel::db()->proteger('%'.$mot_cle_item.'%').') AND ';
}
$requete = '('.rtrim($requete, 'AND ').') ';
} else {
$requete = "(ci.mots_cles_texte LIKE ".Cel::db()->proteger('%'.$mot_cle.'%').') ';
}
$requete .= ' AND ';
return $requete;
}
 
public function formaterPourEnvoiCel(&$tableau_images) {
foreach ($tableau_images as &$image) {
$ids_mots_cles = $this->getIdsMotsClesImage($image['id_image']);
$mots_cles_chaine = '';
foreach ($ids_mots_cles as $id_mot_cle) {
$mots_cles_chaine .= $id_mot_cle['id_mot_cle'].',';
}
$mots_cles_chaine = rtrim($mots_cles_chaine,',');
$image['mots_cles'] = $mots_cles_chaine;
}
 
return $tableau_images;
}
 
public function obtenirCourrielUtilisateurPourIdImage($id_image) {
$requete = 'SELECT courriel_utilisateur FROM cel_images WHERE id_image = '.Cel::db()->proteger($id_image);
 
$utilisateur_courriel = Cel::db()->requeter($requete);
 
$retour = false;
if (!empty($utilisateur_courriel) && isset($utilisateur_courriel[0]['courriel_utilisateur'])) {
$retour = $utilisateur_courriel[0]['courriel_utilisateur'];
}
return $retour;
}
 
private function getIdsMotsClesImage($id_image) {
$requete = 'SELECT DISTINCT id_mot_cle '.
'FROM cel_mots_cles_images_liaison '.
"WHERE id_element_lie = $id_image ";
 
return Cel::db()->requeter($requete);
}
 
// TODO: fonction temporaire
private function formaterDateSqlVersDateAvecSlash($date_sql) {
$date_formatee = '';
$date = explode('-', $date_sql) ;
if (count($date) > 2) {
$date_formatee = $date[2].'/'.$date[1].'/'.$date[0] ;
}
return $date_formatee;
}
}
/trunk/jrest/bibliotheque/GenerateurPDF.php
New file
0,0 → 1,486
<?php
 
/**
* @category PHP
* @package jrest
* @author Raphaël Droz <raphael@tela-botania.org>
* @copyright 2013 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
*/
 
// Include the main TCPDF library (search for installation path).
date_default_timezone_set("Europe/Paris");
require_once('tcpdf_config.php');
require_once('tcpdf/tcpdf.php');
 
Class GenerateurPDF {
 
public $pdf;
 
function GenerateurPDF($utilisateur = NULL) {
// create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
 
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($utilisateur ? $utilisateur['prenom'] . ' ' . $utilisateur['nom'] : 'CEL - Tela Botanica');
$pdf->SetTitle('Observations en étiquettes');
$pdf->SetSubject('Étiquettes des observations');
$pdf->SetKeywords('botaniques, observations, étiquettes, cel, tela-botanica');
 
// set default header data
// $pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 005', PDF_HEADER_STRING);
 
// set header and footer fonts
// $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
// $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
 
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
 
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
 
// set auto page breaks
// $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
$pdf->SetAutoPageBreak(FALSE, PDF_MARGIN_BOTTOM);
 
$pdf->SetFont('times', '', 11);
$pdf->setCellPaddings(1, 1, 1, 1);
$pdf->setCellMargins(1, 1, 1, 1);
 
$pdf->AddPage();
$pdf->setEqualColumns(2);
 
$this->pdf = $pdf;
}
 
 
 
function export($obs) {
$pdf = &$this->pdf;
 
$i = 0;
while($i < count($obs)) {
$pdf->selectColumn(0);
// Multicell test
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
$pdf->Ln();
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
$pdf->Ln();
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
/*$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 1, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 1, 1, '', '', true);*/
 
$pdf->selectColumn(1);
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
$pdf->Ln();
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
$pdf->Ln();
$this->doHTMLcell($obs[$i++]); if(!isset($obs[$i])) break;
/*$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);*/
 
if(isset($obs[$i])) $pdf->AddPage();
}
}
 
function getlinenb4($txt) {
// store current object
$this->pdf->startTransaction();
// store starting values
$start_y = $this->pdf->GetY();
$start_page = $this->pdf->getPage();
 
$this->pdf->MultiCell($this->column_width, $h=0, $txt, $border=0, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
$end_y = $this->pdf->GetY();
$end_page = $this->pdf->getPage();
// calculate height
$height = 0;
if ($end_page == $start_page) {
$height = $end_y - $start_y;
} else {
for ($page=$start_page; $page <= $end_page; ++$page) {
$this->setPage($page);
if ($page == $start_page) {
// first page
$height = $this->pdf->h - $start_y - $this->pdf->bMargin;
} elseif ($page == $end_page) {
// last page
$height = $end_y - $this->pdf->tMargin;
} else {
$height = $this->pdf->h - $this->pdf->tMargin - $this->bMargin;
}
}
}
// restore previous object
$this->pdf = $this->pdf->rollbackTransaction();
return $height;
}
 
function getlinenb3($txt) {
return $this->pdf->getStringHeight($this->column_width, $txt);
}
 
function getlinenb2($txt) {
//var_dump($line, $this->pdf->GetStringWidth($line));
return ceil($this->pdf->GetStringWidth($txt) / $this->column_width);
}
 
function getlinenb($txt) {
return $this->pdf->getStringHeight('', $txt) / ($this->pdf->getFontSize() * $this->pdf->getCellHeightRatio());
}
 
// singe la propriété CSS3 "text-overflow" : "ellipsis"
function elude($txt, $limite_lignes = 3) {
// echo strlen($txt) . ' '. $this->getlinenb($txt) . ' ' . $limite_lignes . "\n";
 
$cell_paddings = $this->pdf->getCellPaddings();
$marge = $cell_paddings['T'] + $cell_paddings['B'];
$line_height = $this->pdf->getStringHeight($this->column_width, "a") - $marge;
if($limite_lignes > 1) {
$lim = $line_height * $limite_lignes + $marge; // $line_height + ($line_height - $marge) * ($limite_lignes - 1);
} else {
$lim = $line_height + $marge;
}
 
while(strlen($txt) > 4 && ($nb = $this->getlinenb3(strip_tags($txt))) > $lim) {
//echo "$nb / $line_height: $txt\n";
// TODO: mb_internal_encoding()
$txt = mb_substr($txt, 0, -4, 'UTF-8') . '…';
}
//echo "$txt: $nb / $limite_lignes \n";
return $txt;
}
 
 
// TODO: affichage pays dans "localité"
// ORDER BY id_observation
// italique pour nom d'espèce, mais pas auteur
function doHTMLcell(&$obs) {
$this->pdf->setCellMargins(0,0,0,0);
$width = $this->column_width = 98;
 
//echo "cell_padding['T']: " . $this->pdf->getCellPaddings()['T'] . ", cell_padding['B']: " . $this->pdf->getCellPaddings()['B'] . "\n";
 
$lh = $this->pdf->getFontSize() * $this->pdf->getCellHeightRatio();
//$lh = $this->pdf->GetLineWidth();
 
/*
var_dump($this->pdf->GetLineWidth(),
$this->pdf->GetCharWidth("a"),
$this->pdf->getStringHeight(60, "Ê"),
$this->pdf->getHTMLFontUnits("plop"),
$this->pdf->GetStringWidth("aa"),
$lh,
 
5,
$this->getlinenb4("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum..."),
$this->elude("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum...", 5),
 
4,
$this->getlinenb4("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"),
$this->elude("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum...", 4),
 
3,
$this->getlinenb4("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet"),
$this->elude("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum...", 3),
 
2,
$this->getlinenb4("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore"),
$this->elude("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum...", 2),
 
1,
$this->getlinenb4("Observation : Lorem ipsum dolor sit amet, consectetur"),
$this->elude("Observation : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum...", 1)
);
die;
*/
 
 
/* $str = '<strong>Observation</strong> : ' . $obs['commentaire'];
echo $this->getlinenb(strip_tags($str)) . "\n";
echo $this->getlinenb2(strip_tags($str)) . "\n";
echo $this->pdf->getStringHeight($width, strip_tags($str)) . "\n";
echo $this->pdf->getStringHeight($width, "a") . "\n";
echo ( $this->pdf->getStringHeight($width, strip_tags($str)) / $this->pdf->getStringHeight($width, "a")) . "\n";
 
die;*/
 
// 3ème paramètre = '' equivalent à $this->pdf->getX()
// 4ème paramètre = '' equivalent à $this->pdf->getY()
 
// référentiel
/* $this->pdf->writeHTMLCell($w = $width, '', '', '',
$html = '<strong>Référentiel</strong> : ' . $obs['nom_referentiel'],
$border = 'LTR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true); */
 
// famille
$this->pdf->writeHTMLCell($w = $width, '', '', '',
$html = '<strong>Famille</strong> : ' . strtoupper($obs['famille']),
$border = 'LTR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
 
/*
// taxon
// la taille maximum en bdtfx est 115 caractères UTF-8 (num_nom: 101483)
// SELECT num_nom, CONCAT(nom_sci, ' ', auteur) AS a, CHAR_LENGTH(CONCAT(nom_sci, ' ', auteur)) FROM bdtfx_v1_01 ORDER BY CHAR_LENGTH(a) DESC limit 2;
$nom = '<em>' . $obs['nom_ret'] . '</em>';
if($obs['certitude'] && stripos($obs['certitude'], 'certain') === false) {
$nom .= ' (' . $obs['certitude'] . ')';
}
$this->pdf->writeHTMLCell($w = $width, $lh * 3.5, '', '',
//$html = '<strong>Espèce</strong> : ' . self::elude('Espèce : ', $obs['nom_ret'], 2),
//$html = $this->elude('<strong>Taxon</strong> : ' . $nom, 3),
$html = '<strong>Taxon</strong> : ' . $nom, // on ne strip pas le nom de taxon, car pas plus de 3 lignes
$border = 'LR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
*/
 
// ou bien nom saisi...
// la taille maximum dans cel_obs au 12/07/2013 est 112 caractères UTF-8 (id_observation: 787762)
// SELECT id_observation, TRIM(nom_sel), CHAR_LENGTH(TRIM(nom_sel)) FROM cel_obs ORDER BY CHAR_LENGTH(TRIM(nom_sel)) DESC LIMIT 2;
$nom = '<em>' . $obs['nom_sel'] . '</em>';
if($obs['certitude'] && stripos($obs['certitude'], 'certain') === false) {
$nom .= ' (' . $obs['certitude'] . ')';
}
$this->pdf->writeHTMLCell($w = $width, $lh * 3.5, '', '',
//$html = '<strong>Espèce</strong> : ' . self::elude('Espèce : ', $obs['nom_ret'], 2),
//$html = $this->elude('<strong>Taxon</strong> : ' . $nom, 3),
$html = '<strong>Taxon</strong> : ' . mb_substr(trim($nom), 0, 115, 'UTF-8'), // on ne strip pas le nom sélectionné, car pas plus de 3 lignes, mais on assure la mise en page
$border = 'LR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
// collecteur
// TODO: pseudo
$limite_nom = 26;
$prenom = $obs['prenom_utilisateur'];
if(mb_strlen($prenom . ' ' . $obs['nom_utilisateur'], 'UTF-8') > $limite_nom) {
$prenom = mb_substr($prenom, 0, 26 - mb_strlen($obs['nom_utilisateur'], 'UTF-8') - 1 /* espace */ - 1 /* … */, 'UTF-8') . '…';
//var_dump($prenom);die;
}
$this->pdf->writeHTMLCell($w = $width - 25, '', '', '',
$html = '<strong>Collecteur</strong> : ' . $prenom . ' ' . $obs['nom_utilisateur'],
$border = 'L', $ln = 0, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
// N°: TODO: writeHTMLCell() semble bugger
$this->pdf->Cell($w = 25, '',
$txt = 'N° : ' . $obs['id_observation'], //. sprintf("%04d", $obs['ordre'])
$border = 'R', $ln = 1, $align = 'R', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
/*$this->pdf->writeHTMLCell($w = 30, '', '', '',
$html = '<strong>N°</strong> : ' . $obs['id_observation'], //. sprintf("%04d", $obs['ordre']),
$border = 'R', $ln = 1, $fill = true, $reset = true, $align = 'L', $autopadding = true);*/
 
// localité
// TODO: département
// TEST: Corse (2A, 2B)
$info_dep = "<strong>Localité</strong> : %s";
$donnees_dep = array($obs['zone_geo']);
if($obs['ce_zone_geo']) {
$info_dep .= " (%s)";
if(strpos($obs['ce_zone_geo'], 'INSEE') !== false) $donnees_dep[] = preg_replace('/^[^\d]*(\d\d).*/', '\1', $obs['ce_zone_geo']);
else $donnees_dep[] = $obs['ce_zone_geo'];
}
 
$info_loc = '';
$donnees_loc = array();
if($obs['lieudit']) {
$info_loc = "%s";
$donnees_loc[] = $obs['lieudit'];
}
if($obs['station']) {
$info_loc .= ", %s";
$donnees_loc[] = $obs['station'];
}
if($obs['milieu']) {
$info_loc .= " [%s]";
$donnees_loc[] = $obs['milieu'];
}
$this->pdf->writeHTMLCell($w = $width, $lh * 3.5, '', '',
//$html = "<strong>Localité</strong> : " .
//self::elude('Localité : ', sprintf("%s (%s)\n%s, %s [%s]", $obs['zone_geo'], $obs['ce_zone_geo'], $obs['lieudit'], $obs['station'], $obs['milieu'] ), 3),
// $html = self::elude(sprintf("<strong>Localité</strong> : %s (%s)\n%s, %s [%s]", $obs['zone_geo'], $obs['ce_zone_geo'], $obs['lieudit'], $obs['station'], $obs['milieu']), 3),
$html = $this->elude(vsprintf($info_dep, $donnees_dep), 2) . "\n" .
$this->elude(vsprintf($info_loc, $donnees_loc), 2),
$border = 'LR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
// lon/lat/alt
$info_geo = '';
$donnees = array();
if($obs['latitude'] && $obs['longitude'] /* TODO: clean DB ! */ && $obs['latitude'] != 0.00000) {
$info_geo .= "%.5f N / %.5f E";
array_push($donnees, $obs['latitude'], $obs['longitude']);
}
if($obs['altitude']) {
$info_geo .= ", %dm";
array_push($donnees, $obs['altitude']);
}
$this->pdf->writeHTMLCell($w = $width, '', '', '',
$html = vsprintf("<strong>Lat. / Lon. , Alt.</strong> : " . $info_geo, $donnees),
$border = 'LR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
// commentaire
$this->pdf->writeHTMLCell($w = $width, $lh * 4.5, '', '',
//$html = '<strong>Observation</strong> : ' . self::elude('Observation : ', $obs['commentaire']),
$html = self::elude('<strong>Observations</strong> : ' . $obs['commentaire'], 4),
$border = 'LR', $ln = 1, $fill = false, $reset = true, $align = 'L', $autopadding = true);
 
// date, note: en 64 bits, strtotime() renvoi un entier négatif (!= FALSE) pour l'an 0
if(strncmp("0000", $obs['date_observation'], 4) == 0) $temps = false;
else $temps = strtotime($obs['date_observation']);
$this->pdf->writeHTMLCell($w = $width, '', '', '',
$html = '<strong>Date de récolte</strong> : ' . ($temps ? strftime("%d/%m/%Y", $temps) : '               '),
$border = 'LBR', $ln = 1, $fill = false, $reset = true, $align = 'R', $autopadding = true);
 
}
 
function docell($obs) {
$this->pdf->setCellMargins(0,0,0,0);
 
$this->pdf->Cell($w = 60, '',
$txt = 'Famille : ' . $obs['famille'],
$border = 'LT', $ln = 0, $align = 'L', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
 
$this->pdf->Cell($w = 20, '',
$txt = 'N° : ' . $obs['id_observation'] /*. sprintf("%04d", $obs['ordre']) */,
$border = 'TR', $ln = 1, $align = 'L', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
 
$this->pdf->Cell($w = 80, '',
$txt = 'Espèce : ' . $obs['nom_ret'],
$border = 'RL', $ln = 1, $align = 'L', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
 
$this->pdf->Cell($w = 80, '',
$txt = 'Collecteur : ' . $obs['prenom_utilisateur'] . ' ' . $obs['nom_utilisateur'],
$border = 'RL', $ln = 1, $align = 'L', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
 
$this->pdf->MultiCell(80, 20,
$txt = sprintf("Localité : %s (%s)\n%s, %s", $obs['zone_geo'], $obs['ce_zone_geo'], $obs['lieudit'], $obs['station']),
$border = 'RL', 'L', 0, 1, '', '', true);
 
$this->pdf->Cell($w = 80, '',
$txt = sprintf("Latitude, Longitude : %s / %s", $obs['latitude'], $obs['longitude']),
$border = 'RL', $ln = 1, $align = 'L', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
 
$this->pdf->MultiCell(80, 20,
$txt = 'Observation : ' . self::elude('Observation : ', $obs['commentaire']),
$border = 'RL', 'L', 0, 1, '', '', true);
 
$this->pdf->Cell($w = 80, '',
$txt = 'Date : ' . strftime("%d/%m/%Y", strtotime($obs['date_observation'])),
$border = 'LBR', $ln = 1, $align = 'R', $fill = false, $link = false, $stretch = 1, $ignore_min_height = false, $calign = 'T', $valign = 'M');
}
 
 
// singe la propriété CSS3 "text-overflow" : "ellipsis"
function elude_bis($intitule, $commentaire, $lignes = 3) {
// TODO: GetLineWidth, GetCharWidth()
$limite = $lignes /* lignes */ * 43 /* caractères */ - strlen($intitule);
if(mb_strlen($commentaire, 'UTF-8') < $limite) return $commentaire;
return mb_substr($commentaire, 0, $limite - 2) . '…';
}
 
 
function export1($observations) {
$pdf = &$this->pdf;
// MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0)
 
$pdf->setEqualColumns(2);
 
$i = 0;
while($i < count($observations)) {
$obs = $observations[$i];
 
$pdf->selectColumn(0);
// Multicell test
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 1, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 1, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 1, 1, '', '', true);
$pdf->Ln();
 
$pdf->selectColumn(1);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);
$pdf->MultiCell(0, 25, self::doTemplate($obs), 1, 'L', 0, 1, '', '', true);
 
$i += 6;
if(isset($observations[$i])) $pdf->AddPage();
}
}
 
static function doTemplate($obs) {
$pattern =
<<<EOF
Famille: %s (%d)
Espèce: %s
Collecteur: %s
Localité: %s
Observation: %s Date: %s
EOF;
return sprintf($pattern,
 
$obs['famille'],
$obs['ordre'],
$obs['nom_ret'],
$obs['prenom_utilisateur'] . ' ' . $obs['nom_utilisateur'],
$obs['zone_geo'],
$obs['commentaire'],
strftime("%Y-%m-%d", strtotime($obs['date_observation']))
);
}
 
 
 
function export2($observations) {
$pdf = &$this->pdf;
 
$pdf->setEqualColumns(2);
 
$i = 0;
$y = $pdf->getY();
$x = $pdf->getX();
while($i < count($observations)) {
$obs = $observations[$i++];
 
$pdf->selectColumn(0);
// Multicell test
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 0, self::doHTMLTemplate($obs), 1, 0, 0, true);
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 1, self::doHTMLTemplate($obs), 1, 0, 0, true);
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 2, self::doHTMLTemplate($obs), 1, 0, 0, true);
//$pdf->Ln();
 
$pdf->selectColumn(1);
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 0, self::doHTMLTemplate($obs), 1, 1, 1, true);
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 1, self::doHTMLTemplate($obs), 1, 1, 1, true);
$pdf->writeHTMLCell(0, 25, $x, $y + 25 * 2, self::doHTMLTemplate($obs), 1, 1, 1, true);
 
$i += 6;
if(isset($observations[$i])) $pdf->AddPage();
}
}
 
static function doHTMLTemplate($obs) {
$pattern =
<<<EOF
<p>Famille: %s <span style="text-align: right">(%d)</span><br/>
Espèce: %s<br/>
Collecteur: %s<br/>
Localité: %s<br/>
Observation: %s Date: %s</p>
EOF;
return sprintf($pattern,
 
$obs['famille'],
$obs['ordre'],
$obs['nom_ret'],
$obs['prenom_utilisateur'] . ' ' . $obs['nom_utilisateur'],
$obs['zone_geo'],
$obs['commentaire'],
strftime("%Y-%m-%d", strtotime($obs['date_observation']))
);
}
 
}
/trunk/jrest/bibliotheque/GestionChampsEtendus.php
New file
0,0 → 1,363
<?php
/**
* PHP Version 5
* in=utf8
* out=utf8
*
* @category PHP
* @package jrest
* @author Aurelien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
* Classe de gestion de l'ajout, modification et suppression des champs supplémentaires des obs et images
*/
class GestionChampsEtendus extends Cel {
 
private $mode = null;
private $table_champs_etendus = null;
private $champ_id = null;
 
public function GestionChampsEtendus($config, $mode) {
parent::__construct($config);
$this->mode = $mode;
 
if ($this->mode == 'obs') {
$this->mode = 'obs';
$this->table_champs_etendus = 'cel_obs_etendues';
$this->champ_id = 'id_observation';
} else if ($this->mode == 'image') {
$this->mode = 'image';
$this->table_champs_etendus = 'cel_images_etendues';
$this->champ_id = 'id_image';
} else {
throw new Exception('Mode inconnu, les modes autorisés sont "obs" et "image"');
}
}
 
/**
* Renvoie true ou false suivant que l'element indiqué possède une valeur
* pour la clé indiquée
*
* @param int $id_element_lie
* @param string $cle
* @param string $valeur
* @return bool
*/
public function existe($id_element_lie, $cle) {
$id = Cel::db()->proteger($id_element_lie);
$cle = Cel::db()->proteger($cle);
$requete = 'SELECT COUNT(*) >= 1 AS existe '.
"FROM {$this->table_champs_etendus} ".
"WHERE {$this->champ_id} = $id ".
" AND cle = $cle ";
 
$resultat = Cel::db()->requeter($requete);
return ($resultat[0]['existe'] == '1');
}
 
/**
* Renvoie tous les champs étendus associé à l'élément passé en paramètre
*
* @param int $id_element_lie
* @return array tableau associatif des champs de la table etendu
*/
public function consulter($id_element_lie) {
$id = Cel::db()->proteger($id_element_lie);
$requete = "SELECT * FROM {$this->table_champs_etendus} WHERE {$this->champ_id} = $id ";
$resultat = Cel::db()->requeter($requete);
return $resultat;
}
 
/**
* Renvoie tous les champs étendus associés aux éléments passés en paramètre.
* Retour sous forme de tableau associatif de tableau d'objets ChampEtendu.
* Les objets ChampEtendu sont regroupés par id d'élement.
*
* @param array $ids_element_lies tableau d'id des éléments liés (obs ou image).
* @return array tableau associatif de tableau d'objets ChampEtendu.
*/
public function consulterParLots(Array $ids_element_lies) {
$champs_etendus_par_element = array();
if (!empty($ids_element_lies)) {
$ids_element_lies = array_map(array(Cel::db(), 'proteger'),$ids_element_lies);
$ids = implode(',', $ids_element_lies);
 
$requete = "SELECT * FROM {$this->table_champs_etendus} WHERE {$this->champ_id} IN ($ids) ";
$resultats = Cel::db()->requeter($requete);
 
foreach ($resultats as &$ligne) {
$id_element = $ligne[$this->champ_id];
if (!isset($champs_etendus_par_element[$id_element])) {
$champs_etendus_par_element[$id_element] = array();
}
$champ_etendu = new ChampEtendu();
$champ_etendu->id = $id_element;
$champ_etendu->cle = $ligne['cle'];
$champ_etendu->valeur = $ligne['valeur'];
 
$champs_etendus_par_element[$id_element][] = $champ_etendu;
}
}
return $champs_etendus_par_element;
}
 
/**
* Ajoute un champ étendu.
* Si la clé existe déjà, seule valeur du champ est mise à jour
*
* @param ChampEtendu $champ_etendu
* @return bool true si l'ajout a eu lieu
*/
public function ajouter(ChampEtendu $champ_etendu) {
$id = Cel::db()->proteger($champ_etendu->id);
$cle = Cel::db()->proteger($champ_etendu->cle);
$valeur = Cel::db()->proteger($champ_etendu->valeur);
 
$requete = "INSERT INTO {$this->table_champs_etendus} ".
"( {$this->champ_id}, cle, valeur) ".
"VALUES ($id, $cle, $valeur) ".
"ON DUPLICATE KEY UPDATE valeur = VALUES(valeur)";
 
// la partie on duplicate key est spécifique mysql mais il sera facile de s'en passer le jour
// où l'on change de sgbd
$ajout = Cel::db()->executer($requete);
return ($ajout !== false);
}
 
/**
* Ajoute plusieurs champs étendus à la fois.
* Si la clé existe déjà, seule la valeur du champ est mise à jour
*
* @param array $champs_etendus tableau d'objets ChampEtendu
* @return bool true si l'ajout a eu lieu
*/
public function ajouterParLots(Array $champs_etendus) {
if(! $champs_etendus) return TRUE; // le tableau ... vide à été inséré
 
$lignes = array();
foreach ($champs_etendus as $champ_etendu) {
$id = Cel::db()->proteger($champ_etendu->id);
$cle = Cel::db()->proteger($champ_etendu->cle);
$valeur = Cel::db()->proteger($champ_etendu->valeur);
 
$lignes[] = "($id, $cle, $valeur)";
}
 
// la partie on duplicate key est spécifique mysql mais il sera facile de s'en passer le jour
// où l'on change de sgbd
$ajout = Cel::db()->executer(sprintf(
"INSERT INTO %s (%s, cle, valeur) VALUES %s ON DUPLICATE KEY UPDATE valeur = VALUES(valeur) -- %s:%d",
$this->table_champs_etendus,
$this->champ_id,
implode(',', $lignes),
__FILE__, __LINE__));
return ($ajout !== false);
}
 
/**
* Modifie un champ étendu associé à l'élément passé en paramètre
*
* @param ChampEtendu $champ_etendu
* @return bool true si la modification a eu lieu
*/
public function modifier(ChampEtendu $champ_etendu) {
$id = Cel::db()->proteger($champ_etendu->id);
$cle = Cel::db()->proteger($champ_etendu->cle);
$valeur = Cel::db()->proteger($champ_etendu->valeur);
 
$requete = "UPDATE {$this->table_champs_etendus} ".
"SET valeur = $valeur ".
"WHERE cle = $cle".
" AND {$this->champ_id} = $id ";
 
$modif = Cel::db()->executer($requete);
return ($modif !== false);
}
 
/**
* Supprime le champ champ étendu associé à l'élément et au nom de clé passés en paramètre
*
* @param int $id_element_lie
* @param string $cle
* @return bool
*/
public function supprimer($id_element_lie, $cle) {
$id = Cel::db()->proteger($id_element_lie);
$cle = Cel::db()->proteger($cle);
$requete = "DELETE FROM {$this->table_champs_etendus} WHERE cle = $cle AND {$this->champ_id} = $id ";
$suppr = Cel::db()->executer($requete);
return ($suppr !== false);
}
 
/**
* Supprime tous les champs champ étendu associés à l'élément passés en paramètre
*
* @param int $id_element_lie
* @return bool
*/
public function vider($id_element_lie) {
$id = Cel::db()->proteger($id_element_lie);
$requete = "DELETE FROM {$this->table_champs_etendus} WHERE {$this->champ_id} = $id ";
$suppr = Cel::db()->executer($requete);
return ($suppr !== false);
}
 
/**
* Supprime tous les champs champ étendu associés aux éléments passés en paramètre
*
* @param array $ids_elements_lies
* @return bool
*/
public function viderParLots($ids_elements_lies) {
$ids = $this->protegerTableau($ids_elements_lies);
$requete = "DELETE FROM {$this->table_champs_etendus} WHERE {$this->champ_id} IN (".implode(',',$ids).") ";
$suppr = Cel::db()->executer($requete);
return ($suppr !== false);
}
 
/**
* Revnoie tous les intitules aux éléments passés en paramètre
*
* @param array $ids_elements_lies
* @return bool
*/
public function consulterClesParLots($ids_elements_lies) {
$cles_fmt = array();
if(!empty($ids_elements_lies)) {
$ids = $this->protegerTableau($ids_elements_lies);
$requete = "SELECT cle FROM {$this->table_champs_etendus} WHERE {$this->champ_id} IN (".implode(',',$ids).") ";
$cles = Cel::db()->requeter($requete);
$i = 0;
foreach($cles as &$cle) {
$cles_fmt[$cle['cle']] = $i++;
}
}
return array_values(array_flip($cles_fmt));
}
 
/**
* Renvoie la liste des groupes de champs étendus
*/
public function consulterGroupesChampsEtendusPredefinis() {
$groupes = array();
$requete = "SELECT cc.cle as cle_groupe, cc.label as nom_groupe, ccc.cle as cle_champ, ccc.label as label_champ, ccc.invisible, ccc.prive ".
"FROM `cel_catalogue_champs_etendus` cc ".
"INNER JOIN cel_catalogue_champs_etendus_liaison cl ON cc.cle = cl.groupe ".
"INNER JOIN cel_catalogue_champs_etendus ccc ON ccc.cle = cl.champ";
 
$groupes_champs = Cel::db()->executerRequete($requete);
$cle_groupe = "";
$infos_groupe = null;
foreach ($groupes_champs as $groupe_champ) {
// les champs sont ordonnés par groupe, ce qui permet de les assembler
// séquentiellement en créeant un nouveau groupe lorsque la clé
// de groupe du champ actuel est différénte de la précédente
if($cle_groupe != $groupe_champ['cle_groupe']) {
if($infos_groupe != null) {
$groupes[] = $infos_groupe;
}
$infos_groupe = array();
$cle_groupe = $groupe_champ['cle_groupe'];
}
if(empty($infos_groupe)) {
$infos_groupe = array(
'cle' => $groupe_champ['cle_groupe'],
'nom' => $groupe_champ['nom_groupe'],
'champs' => array()
);
}
$infos_groupe['champs'][] = array(
'cle' => $groupe_champ['cle_champ'],
'label' => $groupe_champ['label_champ'],
'options' => array('invisible' => $groupe_champ['invisible'],
'prive' => $groupe_champ['prive']
)
);
}
// Ajout du dernier groupe, qui serait ignoré sinon, étant donné que l'ajout
// au tableau se fait au début de la boucle
//TODO: voir si on ne peut pas simplifier ça
if(!empty($infos_groupe)) {
$groupes[] = $infos_groupe;
}
 
return $groupes;
}
/**
* Renvoie le catalogue des champs étendus
*/
public function consulterCatalogueChampsEtendusPredefinis($ordonner_par_cle = false) {
$requete = "SELECT cle, label, invisible, prive ".
"FROM cel_catalogue_champs_etendus cc ";
$catalogue = Cel::db()->executerRequete($requete);
$infos_champs = array();
foreach ($catalogue as $champ) {
$champ_fmt = array(
'cle' => $champ['cle'],
'label' => $champ['label'],
'options' => array('invisible' => $champ['invisible'],
'prive' => $champ['prive']
)
);
if($ordonner_par_cle) {
$infos_champs[$champ_fmt['cle']] = $champ_fmt;
} else {
$infos_champs[] = $champ_fmt;
}
}
return $infos_champs;
}
 
/**
* Transforme un label en clé.
* Supprime tous les accents et caractères spéciaux.
* Accole les mots pour créer un chatmot.
*
* @param string le label.
* @return string la clé correspondante
*/
public function transformerLabelEnCle($label) {
//TODO: cette fonction est elle encore pertinente
// maintenant que la notion de label est supprimée ?
$cle = strtolower(trim($label));
 
// Suppression des mots inutiles
$mots_a_remplacer = array(' le ', ' la ', ' les ', ' des ', ' de ', " l'", " d'", ' à ', ' au ');
$cle = str_replace($mots_a_remplacer, ' ', $cle);
 
// Remplacement parenthèses et crochets et leurs contenus
$cle = preg_replace('/\([^)]+\)/', '', $cle);
$cle = preg_replace('/\[[^\]]+\]/', '', $cle);
 
// Remplacement des accents (voir : http://www.weirdog.com/blog/php/supprimer-les-accents-des-caracteres-accentues.html )
$cle = htmlentities($cle, ENT_NOQUOTES, 'utf-8');
$cle = preg_replace('/&([A-za-z])(?:acute|cedil|circ|grave|orn|ring|slash|th|tilde|uml);/', '\1', $cle);
$cle = preg_replace('/&([A-za-z]{2})(?:lig);/', '\1', $cle); // pour les ligatures e.g. '&oelig;'
$cle = preg_replace('/&[^;]+;/', '', $cle); // supprime les autres caractères
 
// Suppression définitive de tout ce qui n'est pas ASCII
$cle = preg_replace('/[^a-zA-Z0-9 ]/', ' ', $cle);
$cle = preg_replace('/\s+/', ' ', $cle);
 
// Accollement des mots
$cle = ucwords($cle);
$cle = str_replace(' ', '', $cle);
$cle{0} = strtolower($cle{0});
 
return $cle;
}
}
?>
/trunk/jrest/bibliotheque/RechercheInfosTaxonBeta.php
New file
0,0 → 1,264
<?php
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author David Delon <david@tela-botania.org>
* @author Aurélien Peronnet <aurelien@tela-botania.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
*
* La classe appelle les web service d'eflore pour éviter que le code client
* ne soit dépendant de la version d'eflore
*/
class RechercheInfosTaxonBeta extends Cel {
 
const DEBUG = FALSE;
 
private $url_service_nom = null;
private $url_service_taxon = null;
private $url_service_chorologie = null;
 
private $masque_recherche = null;
private $code_referentiel = 'bdtfx';
 
// initialisé à TRUE par rechercherInfosSurTexteCodeOuNumTax()
// si l'espèce passée a le motif <ref>:(nt|nn):<num>, eg: isfan:nt:1591
public $is_notation_spe = FALSE;
 
// un cache utilisé pour les requêtes effectuées sur /service:eflore:0.1/bdtfx/noms?masque=
// qui sont lourdes, et parfois identiques (cf cas de l'import XLS)
static $cache = array();
 
public function RechercheInfosTaxonBeta($config, $code_referentiel = 'bdtfx') {
parent::__construct($config);
$this->setReferentiel($code_referentiel);
}
 
public function setReferentiel($code_referentiel = 'bdtfx') {
$this->code_referentiel = $code_referentiel;
$this->formaterUrlsServices($this->config);
}
 
private function formaterUrlsServices($config) {
$this->url_service_nom = str_replace('{referentiel}', $this->code_referentiel ,$config['eflore']['url_service_nom']);
$this->url_service_taxon = str_replace('{referentiel}', $this->code_referentiel ,$config['eflore']['url_service_taxon']);
$this->url_service_chorologie_obs = $config['eflore']['url_service_chorologie_obs'];
$this->config = $config;
}
 
public function rechercherGenreEspeceSurPrefixe($genre = null, $espece = null) {
$liste_genre_espece = array();
$this->masque_recherche = trim(trim($genre).' '.trim($espece,' *'));
$masque = urlencode($this->masque_recherche);
if(self::DEBUG) error_log("CEL fetch: " . $this->url_service_nom.'?masque='.$masque.'&recherche=etendue&retour.format=min&navigation.limite=50&ns.structure=au');
$urlService = $this->url_service_nom.'?masque='.$masque.'&recherche=etendue&retour.format=min&navigation.limite=50&ns.structure=au';
$requete = @file_get_contents($urlService);
if($requete != '') {
$requete = json_decode($requete);
if(is_object($requete) && isset($requete->resultat)) {
foreach ($requete->resultat as $id => $res) {
$retenu = ($res->retenu == "true") ? '3' : '4';
$liste_genre_espece[] = array($res->nom_sci_complet, $id, $retenu, $res->nom_sci);
}
}
usort($liste_genre_espece, array($this, 'comparerParRetenuPuisNom'));
}
return $liste_genre_espece;
}
 
function comparerParRetenuPuisNom($a, $b) {
if($a[2] == 3 && $b[2] != 3) {
return -1;
} elseif($a[2] != 3 && $b[2] == 3) {
return 1;
} else {
// maintient l'ordre lexicographique - et normalement le genre en premier, en utilisant le nom_sci (sans auteur)
return strcasecmp($a[3], $b[3]);
// @WTF levenshtein c'était juste pour garder le genre en premier ?
//return levenshtein($this->masque_recherche, $a[0]) >= levenshtein($this->masque_recherche, $b[0]);
}
}
 
public function effectuerRequeteInfosComplementairesEtFormaterNom($numNom) {
$resultat_infos_complementaires = (array)$this->effectuerRequeteInfosComplementairesSurNumNom($numNom);
$retour_infos_complementaires = array();
if (isset($resultat_infos_complementaires['nom_retenu_complet']) && $resultat_infos_complementaires['nom_retenu_complet']) {
$retour_infos_complementaires=array((self::supprimerBiblio($resultat_infos_complementaires['nom_retenu_complet'])));
}
 
return $retour_infos_complementaires;
}
 
public function rechercherInformationsComplementairesSurNom($nom_saisi) {
$nom_saisi = trim($nom_saisi);
// Essai de recherche sur le nom saisi tel quel
$liste_genre_espece = $this->effectuerRequeteUrlRecherche($nom_saisi, 'stricte');
if($liste_genre_espece) return $liste_genre_espece;
 
// Essai de recherche stricte en tentant de supprimer le nom d'auteur
if( ($nom_saisi_sans_auteur = self::supprimerAuteur($nom_saisi)) ) { // ne pas faire la requête sur un mot vide
$liste_genre_espece = $this->effectuerRequeteUrlRecherche($nom_saisi_sans_auteur, 'stricte');
}
if($liste_genre_espece) return $liste_genre_espece;
 
// avant-dernière tentative : essai de recherche étendue
$liste_genre_espece = $this->effectuerRequeteUrlRecherche($nom_saisi, 'etendue');
if($liste_genre_espece) return $liste_genre_espece;
 
// dernière tentative: concaténation (nom_sci,auteur) (= nom-retenu généré utilisé comme nom_sci)
$liste_genre_espece = $this->effectuerRequeteUrlRecherche($nom_saisi, 'concat');
 
return $liste_genre_espece;
}
 
private function effectuerRequeteUrlRecherche($nom_saisi, $mode = 'stricte') {
$url = sprintf(
'%1$s?masque=%2$s&recherche=%3$s&ns.format=txt&retour.champs=%4$s&navigation.limite=1',
$this->url_service_nom,
urlencode($nom_saisi),
$mode,
implode(',', array("id","nom_sci","auteur","nom_retenu.id","famille","num_taxonomique","nom_retenu_complet")));
 
if(! array_key_exists($url, self::$cache)) {
if(self::DEBUG) error_log("CEL fetch: " . $url);
$res = @json_decode(file_get_contents($url));
self::$cache[$url] = $res;
} else {
$res = self::$cache[$url];
}
if(!$res) return NULL;
$resultat = (array)$res->resultat;
return array_pop($resultat);
}
 
static function supprimerAuteur($nom_saisi) {
// TODO: gérer les hybrides
if(self::estUnHybride($nom_saisi) || self::estUneFormuleHybridite($nom_saisi)) {
$nom_decoupe = explode(' ', $nom_saisi);
$derniere_position_hybride = array_keys($nom_decoupe, 'x');
$nom_saisi_sans_auteur = implode(' ',array_slice($nom_decoupe, 0, end($derniere_position_hybride) + 2));
} else {
/* Attention le parseur de nom n'est pas fiable à 100%
mais ça marche dans la plupart des cas
à part les formules d'hybridité saisies avec un auteur */
$nameparser = new NameParser();
$auteur = $nameparser->parse_auth($nom_saisi);
$nom_saisi_sans_auteur = str_replace($auteur, '', $nom_saisi);
}
 
return trim($nom_saisi_sans_auteur);
}
 
static function estUneFormuleHybridite($nom_saisi) {
return strpos($nom_saisi,' x ') !== false;
}
 
static function estUnHybride($nom_saisi) {
return strpos($nom_saisi,'x ') === 0;
}
 
public function effectuerRequeteInfosComplementairesSurNumNom($num_nom, $ref = NULL) {
if($ref && isset($this->config['eflore']['api_host'])) {
if(self::DEBUG) error_log("CEL fetch: " .$this->config['eflore']['api_host'] . '/');
return @json_decode(file_get_contents($this->config['eflore']['api_host'] . '/' .
$ref . '/' .
'noms' . '/' .
$num_nom .
'?retour.champs=' . implode(',', array('nom_sci,auteur',
'id',
'nom_retenu_complet',
'nom_retenu.id',
'num_taxonomique',
'famille'))));
}
// XXX: compat
if(self::DEBUG) error_log("CEL fetch: " . $this->url_service_nom.'/'.$num_nom.'?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille');
return @json_decode(file_get_contents($this->url_service_nom.'/'.$num_nom.'?retour.champs=nom_sci,auteur,id,nom_retenu_complet,nom_retenu.id,num_taxonomique,famille'));
}
 
static function supprimerBiblio($nom) {
return trim(preg_replace('/ \[.*\]/','',$nom));
}
 
public function rechercherNumTaxSurNumNom($num_nom) {
$nt = null;
$url = $this->url_service_nom."/".$num_nom.'?retour.champs=num_taxonomique';
if(self::DEBUG) error_log("CEL fetch: $url");
$resultat = @file_get_contents($url);
if($resultat != '') {
$infos = json_decode($resultat);
$nt = $infos->num_taxonomique;
}
 
return $nt;
}
 
public function taxonEstPresentDansDepartement($num_taxon,$code_departement) {
$presence_taxon = false;
$url = $this->url_service_chorologie_obs.'?masque.departement='.$code_departement.'&masque.determination.nt='.$num_taxon.'&navigation.limite=1';
if(self::DEBUG) error_log("CEL fetch: $url");
$resultat = @file_get_contents($url);
if($resultat != '') {
$resultat = json_decode($resultat);
if(is_object($resultat) && isset($resultat->resultat) && count($resultat->resultat) > 0) {
$presence_taxon = true;
}
}
return $presence_taxon;
}
 
/* texte libre, nom scientifique,
ou code nomenclatural (format bdtfx:nn:999999)
ou code taxonomique (format bdtfx:nt:999999)
TODO: voir ce qu'on fait pour l'import de différent référentiels */
function rechercherInfosSurTexteCodeOuNumTax($identifiant_espece) {
preg_match('/(' . implode('|', Cel::$referentiels_valides) .'):(nn|nt):(\d+)/i', $identifiant_espece, $elements);
if($elements) {
$this->is_notation_spe = TRUE;
list(, $ref, $type, $num) = $elements;
 
if($ref != $this->code_referentiel) {
// TODO: ignorer la colonne référentiel, et utiliser le référentiel donné
// mais il faut alors avertir le service (d'import/modif) d'utiliser le référentiel
// passé au nom d'espèce
// Seul le effectuerRequeteInfosComplementairesSurNumNom() le supporte, car c'est encore
// un peu complexe à implémenter proprement pour cause d'attributs de classes.
}
// Numero nomenclatural
if ($type == 'nn') {
$obj = $this->effectuerRequeteInfosComplementairesSurNumNom($num, $ref);
}
// Numero taxonomique
else {
//TODO: retourner moins de champs grâce au paramètre retour.champs
if(self::DEBUG) error_log("CEL fetch: " . $this->url_service_taxon."/nt:".$num);
$obj = @json_decode(file_get_contents($this->url_service_taxon."/nt:".$num));
}
if($obj) $obj->ref = $ref;
return $obj;
}
 
// Nom scientifique
return $this->rechercherInformationsComplementairesSurNom($identifiant_espece);
}
 
public function rechercherSynonymesSurNumNom($num_nom) {
$retour = array();
if(self::DEBUG) error_log("CEL fetch: " . $this->url_service_nom.'/'.$num_nom.'/relations/synonymie/?retour.format=min');
$resultat = @file_get_contents($this->url_service_nom.'/'.$num_nom.'/relations/synonymie/?retour.format=min');
if($resultat != '') {
$resultat = json_decode($resultat);
if(is_object($resultat) && isset($resultat->resultat) && count($resultat->resultat) > 0) {
$retour = $resultat->resultat;
}
}
return $retour;
}
}
/trunk/jrest/bibliotheque/ChampEtendu.php
New file
0,0 → 1,23
<?php
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
* Classe conteneur servant à gérer les infos des champs étendus.
*
*/
class ChampEtendu {
public $id = '';
public $cle = '';
public $valeur = '';
}
?>
/trunk/jrest/bibliotheque/NameParser.php
New file
0,0 → 1,358
<?php
 
/**
* Taxamatch-Webservice PHP v1.0.0
* @author Michael Giddens
* @link http://www.silverbiology.com
*/
 
/* Adapation par David Delon Decembre 2010 : gestion sous espece
*/
 
 
/**
* Class NameParser
* Used to convert a string to a standarized format.
*/
class NameParser {
 
/**
* Whether to debug or nor
* @var bool|integer
*/
public $debug_flag;
 
 
 
/**
* Constructor
*/
public function __construct( ) {
}
 
/**
* Sets value to the method property
* @param mixed $name class property name
* @param mixed $value class property value
*/
public function set($name,$value) {
$this->$name = $value;
}
 
/**
* Reduce Spaces
* This will reduce the string to only allow once space between characters
* @param string $str : string to reduce space
* @return string : string with only once space between characters
*/
private function reduce_spaces( $str ) {
$str = preg_replace("/ {2,}/", ' ', $str );
$str = trim( $str );
return( $str );
}
 
/**
* Function: parse_auth
* Purpose: Produce a parsed version of authority of a taxon name
* @author Tony Rees (Tony.Rees@csiro.au)
* Date created: March 2008
* Inputs: authority string as str
* Remarks:
* (1) Performs authority expension of known abbreviated authornames using
* table "auth_abbrev_test1" (must be available and populated with relevant content)
* (2) Recognises "and", "et", "&" as equivalents (special case for "et al.") - all parsed to ampersand
* (3) Recognises (e.g.) "Smith 1980" and "Smith, 1980" as equivalents - comma is removed in these cases
* (4) Recognises (e.g.) "F. J. R. Taylor, 1980" and "F.J.R. Taylor, 1980" as equivalents -
* extra space after full stops is ignored in these cases
* (5) Returns uppercase string, diacritical marks intact
*
* @param string $str : authority string
* @param integer $upcase : convert to uppercase if $upcase = 1
* @return string : parsed author string
*/
public function parse_auth( $str, $upcase=1 ) {
 
$this->debug['parse_auth'][] = "1";
$temp = $str = trim($str);
if ( ($str == NULL) || ($str == '') ) {
$this->debug['parse_auth'][] = "1a";
return '';
}
 
if ( ( $temp == null ) || ( $temp == '') ) {
$this->debug['parse_auth'][] = "2a";
return('');
} else {
 
$this->debug['parse_auth'][] = "2b";
// add space after full stops, except at end (NB, will also add spece before some close brackets)
$temp = rtrim( str_replace('.', '. ', $temp) );
$this->debug['parse_auth'][] = "4 (temp:$temp)";
//normalise "et", "and" to ampersand (et al. is a special case)
// if ( $temp like '% et al%' ) {
if ( preg_match('/ et al/', $temp) ) {
$temp = str_replace(' et al','zzzzz', $temp);
$this->debug['parse_auth'][] = "4a (temp:$temp)";
}
$temp = str_replace(' et ',' & ', $temp );
$temp = str_replace(' and ',' & ', $temp );
 
$temp = str_replace('zzzzz',' et al', $temp);
 
$this->debug['parse_auth'][] = "5 (temp:$temp)";
//remove commas before dates (only)
// like '%, 17%'
if ( preg_match('/, 17/', $temp) ) {
$temp = str_replace(', 17',' 17', $temp);
$this->debug['parse_auth'][] = "5a (temp:$temp)";
}
// like '%, 18%'
if ( preg_match('/, 18/', $temp) ) {
$temp = str_replace(', 18',' 18', $temp);
$this->debug['parse_auth'][] = "5b (temp:$temp)";
}
// like '%, 19%'
if ( preg_match('/, 19/', $temp) ) {
$temp = str_replace(', 19',' 19', $temp);
$this->debug['parse_auth'][] = "5c (temp:$temp)";
}
// like '%, 20%'
if ( preg_match('/, 20/', $temp) ) {
$temp = str_replace(', 20',' 20', $temp);
$this->debug['parse_auth'][] = "5d (temp:$temp)";
}
// reduce multiple internal spaces to single space
$temp = $this->reduce_spaces( $temp );
// like '% -%'
$temp = str_replace(' -', '-', $temp);
 
$this->debug['parse_auth'][] = "6 (temp:$temp)";
foreach( explode(' ', $temp) as $this_word ) {
//$this->debug['parse_auth'][] = "7 (this_word:$this_word)";
$elapsed_chars = '';
// like '(%'
if ( preg_match('/^\(/', $this_word) ) {
$elapsed_chars .= '(';
$this_word = substr( $this_word, 1 );
//$this->debug['parse_auth'][] = "7a (this_word:$this_word) (elapsed_chars:$elapsed_chars)";
}
 
// Add back the word to the final translation
$elapsed_chars .= $this_word . ' ';
//$this->debug['parse_auth'][] = "7c (this_word:$this_word) (elapsed_chars:$elapsed_chars)";
}
$elapsed_chars = $this->reduce_spaces( str_replace(' )', ')', $elapsed_chars) );
return trim( $elapsed_chars ) ;
}
 
}
/**
* Function: parse
* Purpose: Produces parsed version of an input string (scientific name components)
* @author Tony Rees (Tony.Rees@csiro.au)
* Date created: June 2007-November 2008
* Inputs: input string as str (this version presumes genus, genus+species, or
* genus+species+authority)
* Outputs: parsed version of input string, for match purposes
* Remarks:
* (1) Removes known text elements e.g.
* 'aff.', 'cf.', 'subsp.', subgenera if enclosed in brackets, etc. as desired
* (2) Removes accented and non A-Z characters other than full stops
* (in scientific name portions)
* (3) Returns uppercase scientific name (genus + species only)
* plus unaltered (presumed) authority
* examples;
* Anabaena cf. flos-aquae Ralfs ex Born. et Flah. => ANABAENA FLOSAQUAE Ralfs
* ex Born. et Flah.
* Abisara lemÈe-pauli => ABISARA LEMEEPAULI
* Fuc/us Vesiculos2us => FUCUS VESICULOSUS
* Buffo ignicolor LacÈpËde, 1788 => BUFFO IGNICOLOR LacÈpËde, 1788
* Barbatia (Mesocibota) bistrigata (Dunker, 1866) => BARBATIA BISTRIGATA (Dunker, 1866)
* (4) Thus version does not handle genus+author, or genus+species+infraspecies
* (second" good" term is presumed to be species epithet, anything after is
* considered to be start of the authority), however could be adapted further as required
* and actually it was done in this version for Tela Botanica
* (5) There is a separate function "parse_auth" for normalizing authorities when required
* (e.g. for authority comparisons)
*
* @param string $str : input string ( genus, genus+species, or genus+species+authority )
* @return string : parsed string
*/
public function parse( $str = NULL ) {
unset($this->debug['parse']);
 
$temp = '';
$first_str_part = NULL;
$second_str_part = NULL;
$temp_genus = '';
$temp_species = '';
$temp_genus_species = '';
$temp_authority = '';
$temp_infra = '';
//$this->debug['parse'][] = "1";
 
if ( ($str == NULL) || ( trim($str) == '') ) {
//$this->debug[] = "N1a<br>";
return '';
} else {
// trim any leading, trailing spaces or line feeds
$temp = trim( $str );
//$this->debug['parse'][] = "1b";
}
 
if ( $temp == NULL || $temp == '') {
//$this->debug['parse'][] = "2a";
return '';
} else {
//$this->debug['parse'][] = "2b";
 
// replace any HTML ampersands
$set = array('%', '&', 'amp;%', 'AMP;%');
$temp = str_replace( $set, '&', $temp );
 
//$this->debug['parse'][] = "2b1 (temp:$temp)";
 
// remove any content in angle brackets (e.g. html tags - <i>, </i>, etc.)
$html_pattern = "(\<(/?[^\>]+)\>)";
//? This should not just handle html tags but all <*>
$temp = preg_replace( $html_pattern, '', $temp);
//$this->debug['parse'][] = "2b2 (temp:$temp)";
 
// if second term (only) is in round brackets, presume it is a subgenus or a comment and remove it
// examples: Barbatia (Mesocibota) bistrigata (Dunker, 1866) => Barbatia bistrigata (Dunker, 1866)
// Barbatia (?) bistrigata (Dunker, 1866) => Barbatia bistrigata (Dunker, 1866)
// (obviously this will not suit genus + author alone, where first part of authorname is in brackets,
// however this is very rare?? and in any case we are not supporting genus+authority in this version)
//if ( $temp like '% (%)%'
$temp = preg_replace( "/ \(\w*\W*\)/", '', $temp, 1 );
//? Not sure if this will catch if
//$this->debug['parse'][] = "2b3 (temp:$temp)";
 
// if second term (only) is in square brackets, presume it is a comment and remove it
// example: Aphis [?] ficus Theobald, [1918] => Aphis ficus Theobald, [1918]
//if ( $temp like '% [%]%'
$temp = preg_replace( "/ \[\w*\W*\]/", '', $temp, 1 );
//? Not sure if this will catch if
//$this->debug['parse'][] = "2b4 (temp:$temp)";
 
// drop indicators of questionable id's - presume all are lowercase for now (could extend as needed)
$temp = preg_replace( "/ cf /", " ", $temp );
$temp = preg_replace( "/ cf\. /", " ", $temp );
$temp = preg_replace( "/ near /", " ", $temp );
$temp = preg_replace( "/ aff\. /", " ", $temp );
$temp = preg_replace( "/ sp\. /", " ", $temp );
$temp = preg_replace( "/ spp\. /", " ", $temp );
$temp = preg_replace( "/ spp /", " ", $temp );
 
//$this->debug['parse'][] = "2b5 (temp:$temp)";
 
// eliminate or close up any stray spaces introduced by the above
$temp = $this->reduce_spaces( $temp );
 
//$this->debug['parse'][] = "2b6 (temp:$temp)";
 
// now presume first element is genus, second (if present) is species, remainder
// (if present) is authority
// look for genus name
$ar = explode( " ", $temp, 2);
if ( count( $ar ) ) {
$temp_genus = $ar[0];
$temp = @$ar[1];
} else {
$temp_genus = $temp;
$temp = '';
}
//$this->debug['parse'][] = "2b7 (temp_genus:$temp_genus) (temp:$temp)";
 
// look for species epithet and authority
$ar = explode( " ", $temp, 2);
if ( count( $ar ) ) {
$temp_species = $ar[0];
$temp_authority = @$ar[1];
} else {
$temp_species = $temp;
$temp_authority = '';
}
// look for subspecies
 
$infras =array('subsp.','var.');
 
$temp_authority = preg_replace( "/ssp./", "subsp.", $temp_authority);
$temp_authority = preg_replace( "/ssp /", "subsp.", $temp_authority);
$temp_authority = preg_replace( "/subsp /", "subsp.", $temp_authority);
$temp_authority = preg_replace( "/var /", "var.", $temp_authority);
 
$temp_infra_authority = '';
$temp_infra_type = '';
foreach ($infras as $infra) {
$pos = strpos($temp_authority, $infra);
if ($pos === false) {
continue;
} else {
$temp_infra=substr($temp_authority,$pos+strlen($infra));
$temp_authority=substr($temp_authority,0,$pos);
$temp_infra=trim($temp_infra);
$temp_infra_type=$infra;
// look for infra epithet and authority
$ar = explode(" ", $temp_infra, 2);
if ( count( $ar ) ) {
$temp_infra = $ar[0];
$temp_infra_authority = @$ar[1];
}
break; // on s'arrete au premier trouve
}
}
 
//$this->debug['parse'][] = "2b8 (temp_genus:$temp_genus) (temp_species:$temp_species) (temp_authority:$temp_authority) (temp_infra:$temp_infra) (temp_infra_authority:$temp_infra_authority) (temp:$temp)";
 
// replace selected ligatures here (Genus names can contain Æ, OE ligature)
$temp_genus = str_replace( 'Æ', 'AE', $temp_genus);
$temp_species = str_replace( 'Æ', 'AE', $temp_species);
$temp_infra = str_replace( 'Æ', 'AE', $temp_infra );
 
//$this->debug['parse'][] = "2b9 (temp_genus:$temp_genus) (temp_species:$temp_species) (temp_authority:$temp_authority) (temp_infra:$temp_infra) (temp_infra_authority:$temp_infra_authority) (temp:$temp)";
 
$temp_genus= trim($temp_genus);
$temp_species= trim($temp_species);
$temp_infra= trim($temp_infra );
 
// reduce any new multiple internal spaces to single space, if present
$temp_genus= $this->reduce_spaces( $temp_genus );
$temp_species= $this->reduce_spaces( $temp_species );
$temp_infra= $this->reduce_spaces( $temp_infra );
 
//$this->debug['parse'][] = "2b10 (temp_genus:$temp_genus) (temp_species:$temp_species) (temp_authority:$temp_authority) (temp_infra:$temp_infra) (temp_infra_authority:$temp_infra_authority) (temp:$temp)";
 
if (isset($temp_authority) && ($temp_authority!='') ) {
$temp_authority=$this->parse_auth($temp_authority);
}
 
if (isset($temp_infra_authority) && ($temp_infra_authority!='') ) {
$temp_infra_authority=$this->parse_auth($temp_infra_authority);
}
//$this->debug['parse'][] = "2b11 (temp_genus:$temp_genus) (temp_species:$temp_species) (temp_authority:$temp_authority) (temp_infra:$temp_infra) (temp_infra_authority:$temp_infra_authority) (temp:$temp)";
return array("genus"=>$temp_genus, "species"=>$temp_species, "authority"=>$temp_authority, "infra"=>$temp_infra, "infra_authority"=>$temp_infra_authority, "infra_type"=>$temp_infra_type);
}
} // End NameParser
} // End Class
?>
/trunk/jrest/bibliotheque/ImageRecreation.php
New file
0,0 → 1,741
<?php
Class ImageRecreation {
 
private $droits = 0755;
private $formats = array('CRX2S','CRXS','CXS','CS','CRS','XS','S','M','L','XL','X2L','X3L');
const MODE_GD = 'gd';
const MODE_IMAGEMAGICK = 'imagemagick';
private $mode;
 
private $verbose = true;
 
public function __construct($config) {
$this->config = $config;
 
if (extension_loaded('imagick')) {
$this->mode = self::MODE_IMAGEMAGICK;
} else {
$this->mode = self::MODE_GD;
}
}
 
public function recreerMiniaturesRecursivement() {
$this->itererRecursivement($this->config['cel']['chemin_images']);
}
 
public function regenererMiniaturesIntervalle($params) {
$id_debut = $params[0];
$id_fin = $params[1];
 
if (is_numeric($id_debut) && is_numeric($id_fin)) {
for ($i = $id_debut; $i <= $id_fin; $i++) {;
$tab_param = array($i);
$this->regenererMiniaturesPourId($tab_param);
}
}
}
 
public function regenererMiniaturesPourId($params) {
$id = $params[0];
 
if (!is_numeric($id)) {
return;
}
 
$dossier_fichier = $this->obtenirDossierPourFormat($id, 'O');
$nom_fichier = $this->convertirIdBddVersNomFichier($id, 'O');
 
$chemin_fichier = $dossier_fichier.'/'.$nom_fichier;
 
if (file_exists($chemin_fichier)) {
$infos_image_originale = $this->obtenirImageEtInfosPourChemin($chemin_fichier);
 
// creation de miniatures pour chacuns des formats définis
foreach ($this->formats as $format) {
$this->creerEtStockerMiniatureFichierImageSelonFormat($id, $infos_image_originale, $format);
};
 
}
}
 
public function itererRecursivement($dossier) {
// on ne parse que le dossier des images originales
$dossiers_a_exclure = $this->getFormats();
 
foreach (new DirectoryIterator($dossier) as $fichier_ou_dossier) {
if ($fichier_ou_dossier->isDot()) {
continue;
}
 
if (in_array($fichier_ou_dossier->getBasename(), $dossiers_a_exclure)) {
continue;
}
 
if ($fichier_ou_dossier->isDir()) {
$this->itererRecursivement($fichier_ou_dossier->getPathname());
} else {
$nom_fichier = $fichier_ou_dossier->getFilename();
 
$infos_image_originale = $this->obtenirImageEtInfosPourChemin($fichier_ou_dossier->getPathname());
$id = $this->convertirBaseNomFichierVersIdBdd($nom_fichier, $this->formats);
 
// creation de miniatures pour chacuns des formats définis
foreach ($this->formats as $format) {
$this->creerEtStockerMiniatureFichierImageSelonFormat($id, $infos_image_originale, $format);
}
}
}
}
public function creerOuRenvoyerImage($id, $format) {
$dossier = $this->obtenirDossierPourFormat($id, $format);
$nom_fichier = $this->convertirIdBddVersNomFichier($id, $format);
$chemin_image = $dossier.'/'.$nom_fichier;
 
$image = false;
if(!file_exists($chemin_image)) {
$chemin_image_originale = $this->obtenirCheminImageOriginale($id);
$infos_image_originale = $this->obtenirImageEtInfosPourChemin($chemin_image_originale);
if($infos_image_originale) {
// le verrou est là dans le (rare) cas où l'image est déjà en train
// d'être générée par le script de création des miniatures ou bien
// un autre instance de cette classe appelée par le web service
$fp = fopen($chemin_image_originale, "r");
// si le fichier est verrouillé, flock attendra qu'il se libère
$verrou = flock($fp, LOCK_EX);
if(!file_exists($chemin_image)) {
// si le fichier a été locké alors l'image était en train d'être générée
// et donc il n'est pas nécéssaire de la créer (d'où le 2eme test sur file exists)
$this->creerEtStockerMiniatureFichierImageSelonFormat($id, $infos_image_originale, $format);
}
$verrou = flock($fp, LOCK_UN);
fclose($fp);
$image = file_get_contents($chemin_image);
}
} else {
$image = file_get_contents($chemin_image);
}
return $image;
}
 
public function creerMiniatureImageSelonFormat($infos_image_originale, $format = 'O') {
$image_redimensionnee = false;
if ($format == 'O') {
// format original : rien à faire
$image_redimensionnee = $infos_image_originale['image'];
 
} else {
if ($this->estUnFormatRogne($format)) {
if ($this->mode == self::MODE_IMAGEMAGICK) {
// si l'on dispose de la librairie imageMagick
// on applique l'algorithme d'auto détection de sujets
// qui centre la miniature sur le sujet de l'image
$image_redimensionnee = $this->opticrop($infos_image_originale, $format);
}
if ($image_redimensionnee === false) {
// si l'on ne dispose que de gd ou bien que Imagick a échoué
// la minature est une image redimensionnée rognée au centre
$image_redimensionnee = $this->creerMiniatureCarreeRognee($infos_image_originale, $format);
}
} else if ($this->estUnFormatCarre($format)) {
// le format carre et une image redimensionnée en gardant son ratio, insérée dans un carré blanc
$image_redimensionnee = $this->creerMiniatureCarree($infos_image_originale, $format);
} else {
$image_redimensionnee = $this->creerMiniature($infos_image_originale, $format);
}
}
 
return $image_redimensionnee;
}
 
/**
* Déplace une image temporaire uploadée vers le répertoire de stockage d'images,
* en enregistrant les métadonnées et tout le tintouin.
* Si $conserverFichiersTemporaires vaut true, l'image est copiée et non déplacée.
*
* @param unknown $fichier
* @param unknown $id
* @param unknown $conserverFichiersTemporaires
* @return Ambigous <multitype:, boolean>|boolean
*/
public function stockerFichierOriginal($fichier, $id, $conserverFichiersTemporaires=false) {
$chemin_fichier_origine = is_array($fichier) ? $fichier['tmp_name'] : $fichier;
$chemin_base_fichier = $this->creerSiNecessaireEtRenvoyerCheminStockageFichierPourIdEtFormat($id, 'O');
$nom_fichier = $this->convertirIdBddVersNomFichier($id, 'O');
$chemin_fichier = $chemin_base_fichier.'/'.$nom_fichier;
$deplacement_fichier = $this->stockerImageExterne($chemin_fichier_origine, $chemin_fichier, $conserverFichiersTemporaires);
if ($deplacement_fichier) {
$infos_image_originale = $this->obtenirImageEtInfosPourChemin($chemin_fichier);
$taux_compression = $this->renvoyerTauxCompressionPourPoids($infos_image_originale['poids_octets']);
if ($taux_compression < 100 && $this->mode == self::MODE_IMAGEMAGICK) {
$this->ecrireImageSurDisqueAvecMeta($chemin_fichier, $taux_compression);
}
 
return $infos_image_originale;
} else {
$erreur = 'ERROR : probleme durant le déplacement du fichier temporaire \n' ;
$this->logger('CEL_bugs',$erreur);
return false ;
}
}
public function stockerFichierEtCreerMiniatures($fichier, $id) {
$infos_image_originale_stockee = $this->stockerFichierOriginal($fichier, $id);
if($infos_image_originale_stockee) {
$formats = $this->getFormats();
// creation de miniatures pour chacuns des formats définis
foreach($formats as $format) {
$this->creerEtStockerMiniatureFichierImageSelonFormat($id, $infos_image_originale_stockee, $format);
}
} else {
$erreur = 'ERROR : impossible d\'obtenir les informations sur l\'image originale \n' ;
$this->logger('CEL_bugs',$erreur);
return false ;
}
return true ;
}
 
public function creerEtStockerMiniatureFichierImageSelonFormat($id ,$infos_image_originale, $format = 'O') {
$image_redimensionnee = $this->creerMiniatureImageSelonFormat($infos_image_originale, $format);
 
$taux_compression = $this->renvoyerTauxCompressionPourPoids($infos_image_originale['poids_octets']);
$this->ecrireImageSurDisque($image_redimensionnee, $id, $format, $taux_compression);
 
return true;
}
 
public function creerImageRedimensionnee($infos_image_originale, $hauteur_redimension, $largeur_redimension) {
$image_redimensionnee = imagecreatetruecolor($largeur_redimension, $hauteur_redimension);
 
imagecopyresampled($image_redimensionnee,
$infos_image_originale['image'],
0, 0,
0, 0,
$largeur_redimension,
$hauteur_redimension,
$infos_image_originale['largeur'],
$infos_image_originale['hauteur']
);
 
return $image_redimensionnee;
}
 
public function creerMiniature($informations_images, $format) {
$taille_reference_pour_format = $this->obtenirDimensionsPourFormat($format);
 
$taille_image_redimensionnee = $this->calculerTailleImage($informations_images, $taille_reference_pour_format['hauteur']);
$image_redimensionnee = $this->creerImageRedimensionnee($informations_images, $taille_image_redimensionnee['hauteur'], $taille_image_redimensionnee['largeur']);
 
return $image_redimensionnee;
}
 
public function creerMiniatureCarree($informations_image, $format) {
$taille_reference_pour_format = $this->obtenirDimensionsPourFormat($format);
$cote_carre = $taille_reference_pour_format['largeur'];
 
$image_redimensionnee_avec_rapport = $this->creerMiniature($informations_image, $format);
$taille_redimensionnee_avec_rapport = $this->calculerTailleImage($informations_image, $taille_reference_pour_format['hauteur']);
 
if ($this->estPaysage($informations_image)) {
$debut_largeur_a_copier = 0 ;
$debut_hauteur_a_copier = ($cote_carre - $taille_redimensionnee_avec_rapport['hauteur'])/2 ;
} else {
$debut_largeur_a_copier = ($cote_carre - $taille_redimensionnee_avec_rapport['largeur'])/2 ;
$debut_hauteur_a_copier = 0 ;
}
 
$image_carre_blanc_cible = $this->renvoyerEtCreerImageCarreeBlancheSelonFormat($cote_carre);
 
imagecopy($image_carre_blanc_cible, $image_redimensionnee_avec_rapport,
$debut_largeur_a_copier ,$debut_hauteur_a_copier, 0, 0,
$taille_redimensionnee_avec_rapport['largeur'], $taille_redimensionnee_avec_rapport['hauteur']
);
 
return $image_carre_blanc_cible;
}
 
public function creerMiniatureCarreeRognee($informations_image, $format) {
$taille_reference_pour_format = $this->obtenirDimensionsPourFormat($format);
$cote_carre = $taille_reference_pour_format['largeur'];
$cote_carre_non_redimensionne = 0;
 
if ($this->estPaysage($informations_image)) {
$cote_carre_non_redimensionne = $informations_image['hauteur'];
$debut_largeur_a_copier = ($informations_image['largeur'] / 2) - ($informations_image['hauteur'] / 2);
$debut_hauteur_a_copier = 0;
 
if($debut_largeur_a_copier <= 0) {
$debut_largeur_a_copier = 0;
}
 
$nb_pixels_largeur_a_copier = $cote_carre_non_redimensionne;
$nb_pixels_hauteur_a_copier = $cote_carre_non_redimensionne;
} else {
$cote_carre_non_redimensionne = $informations_image['largeur'];
$debut_largeur_a_copier = 0 ;
$debut_hauteur_a_copier = ($informations_image['hauteur'] / 2) - ($informations_image['largeur'] / 2);
 
if($debut_hauteur_a_copier <= 0) {
$debut_hauteur_a_copier = 0;
}
 
$nb_pixels_largeur_a_copier = $cote_carre_non_redimensionne;
$nb_pixels_hauteur_a_copier = $cote_carre_non_redimensionne;
}
 
$image_carre_temporaire = imagecreatetruecolor($cote_carre_non_redimensionne, $cote_carre_non_redimensionne);
 
imagecopyresampled($image_carre_temporaire,
$informations_image['image'],
0, 0,
$debut_largeur_a_copier,
$debut_hauteur_a_copier,
$cote_carre_non_redimensionne,
$cote_carre_non_redimensionne,
$nb_pixels_largeur_a_copier,
$nb_pixels_hauteur_a_copier
);
 
$image_redimensionnee = imagecreatetruecolor($cote_carre, $cote_carre);
 
imagecopyresampled($image_redimensionnee,
$image_carre_temporaire,
0, 0,
0, 0,
$cote_carre,
$cote_carre,
$cote_carre_non_redimensionne,
$cote_carre_non_redimensionne
);
 
return $image_redimensionnee;
}
 
/**
* Déplace un fichier temporaire vers une destination donnée. Si
* $conserverFichiersTemporaires vaut true, le fichier est copié et non déplacé.
*
* @param unknown $chemin_fichier_temp
* @param unknown $chemin_destination
* @param string $conserverFichiersTemporaires
* @return boolean
*/
public function stockerImageExterne($chemin_fichier_temp, $chemin_destination, $conserverFichiersTemporaires=false) {
if ($conserverFichiersTemporaires === true) {
// copie du fichier
$deplacement = copy($chemin_fichier_temp, $chemin_destination);
} else {
if (is_uploaded_file($chemin_fichier_temp)) {
$deplacement = move_uploaded_file($chemin_fichier_temp, $chemin_destination);
} else {
$deplacement = rename($chemin_fichier_temp, $chemin_destination);
}
}
 
return $deplacement;
}
 
public function creerSiNecessaireEtRenvoyerCheminStockageFichierPourIdEtFormat($id, $format) {
$chemin_sur_serveur_final = $this->obtenirDossierPourFormat($id, $format);
 
if (!file_exists($chemin_sur_serveur_final)) {
umask(0);
if (!mkdir($chemin_sur_serveur_final, $this->droits, true)) {
$erreur = 'ERROR : probleme durant l\'écriture du dossier '.$format.' \n' ;
$this->logger('CEL_bugs', $erreur);
return false;
}
}
 
return $chemin_sur_serveur_final;
}
 
public function obtenirDossierPourFormat($id, $format) {
$chemin_base = $this->config['cel']['chemin_images'];
 
$chemin_sur_serveur = $chemin_base;
 
$id = sprintf('%09s', $id);
$id = wordwrap($id, 3 , '_', true);
 
list($dossierNiveau1, $dossierNiveau2) = explode('_', $id);
 
$chemin_sur_serveur_final = $chemin_sur_serveur.'/'.$dossierNiveau1.'/'.$dossierNiveau2.'/'.$format;
 
return $chemin_sur_serveur_final;
}
 
public function obtenirCheminImageOriginale($id_image) {
$nom = $this->convertirIdBddVersNomFichier($id_image, 'O');
$dossier = $this->obtenirDossierPourFormat($id_image,'O');
 
return $dossier.'/'.$nom;
}
 
public function obtenirImageEtInfosPourId($id_image) {
$chemin_image_o = $this->obtenirCheminImageOriginale($id_image);
return $this->obtenirImageEtInfosPourChemin($chemin_image_o);
}
 
public function obtenirImageEtInfosPourChemin($chemin_fichier) {
$image_et_infos = false;
 
if (file_exists($chemin_fichier)) {
$image_et_infos = array();
list($image_et_infos['largeur'], $image_et_infos['hauteur']) = getimagesize($chemin_fichier);
$image_et_infos['poids_octets'] = filesize($chemin_fichier);
$image_et_infos['image'] = imagecreatefromjpeg($chemin_fichier);
$image_et_infos['chemin'] = $chemin_fichier;
}
 
return $image_et_infos;
}
 
public function obtenirDimensionsPourFormat($format) {
$dimensions = array('largeur' => 0, 'hauteur' => 0);
 
if (isset($this->config['cel']['format_'.$format])) {
list($dimensions['largeur'], $dimensions['hauteur']) = explode('_', $this->config['cel']['format_'.$format]);
}
 
return $dimensions;
}
 
public function calculerTailleImage($informations_images, $taille_max) {
$HL_redimension = array();
 
if ($this->estPaysage($informations_images)) {
$rapport = $informations_images['hauteur']/$informations_images['largeur'] ;
$HL_redimension['largeur'] = round($taille_max) ;
$HL_redimension['hauteur'] = round($taille_max*$rapport) ;
 
} else {
$rapport = $informations_images['largeur']/$informations_images['hauteur'] ;
$HL_redimension['hauteur'] = round($taille_max) ;
$HL_redimension['largeur'] = round($taille_max*$rapport) ;
}
 
return $HL_redimension;
}
 
public function getFormats() {
return $this->formats;
}
 
public function estUnFormatCarre($format) {
return (strpos($format,'C') === 0);
}
 
public function estUnFormatRogne($format) {
return (strpos($format,'R') === 1);
}
 
public function estPaysage($informations_images) {
return $informations_images['largeur'] > $informations_images['hauteur'];
}
 
public function estPortait($informations_images) {
return $informations_images['largeur'] < $informations_images['hauteur'];
}
 
public function renvoyerTauxCompressionPourPoids($poids_octets) {
$poids_max_octets = $this->config['cel']['taille_max'];
 
$ratio_compression = 100 ;
 
if ($poids_octets >= $poids_max_octets) {
$ratio_compression = 75 ;
}
 
return $ratio_compression;
}
 
public function convertirIdBddVersNomFichier($id, $format, $extension = 'jpg') {
// creation du format original
$id_avec_zeros = sprintf('%09s', $id) ;
$id_avec_zeros_underscores = wordwrap($id_avec_zeros, 3 , '_', true) ;
 
$nom_fichier = $id_avec_zeros_underscores.'_'.$format.'.'.$extension;
 
return $nom_fichier;
}
 
public function convertirBaseNomFichierVersIdBdd($nom_fichier, $formats) {
$nom_fichier_sans_extension = trim($nom_fichier, '.jpg');
 
foreach($formats as $format) {
$nom_fichier_sans_extension = trim($nom_fichier_sans_extension, '_'.$format);
}
 
$id_image = str_replace('_', '', $nom_fichier_sans_extension);
 
// suppression des 0 devant
$id_image += 0;
 
return $id_image;
}
 
public function ecrireImageSurDisque($image_binaire, $id, $format, $compression = 100) {
umask(0);
 
$chemin_sur_serveur_final = $this->creerSiNecessaireEtRenvoyerCheminStockageFichierPourIdEtFormat($id, $format);
$nom_fichier = $this->convertirIdBddVersNomFichier($id, $format);
 
if (file_exists($chemin_sur_serveur_final.'/'.$nom_fichier)) {
unlink($chemin_sur_serveur_final.'/'.$nom_fichier);
}
 
// attention, ceci ne preserve pas les metadonnées
imagejpeg($image_binaire, $chemin_sur_serveur_final.'/'.$nom_fichier, $compression);
chmod($chemin_sur_serveur_final.'/'.$nom_fichier,$this->droits);
}
 
public function ecrireImageSurDisqueAvecMeta($chemin_image_a_stocker, $compression = 100) {
$img = new Imagick($chemin_image_a_stocker);
 
// l'utilisation d'image magick préserve les métadonnées lors d'une recompression
$img->setformat("jpeg");
$img->setImageCompression(imagick::COMPRESSION_JPEG);
$img->setCompressionQuality($compression);
$img->writeImage($chemin_image_a_stocker);
$img->destroy();
 
chmod($chemin_image_a_stocker, $this->droits);
}
 
public function renvoyerEtCreerImageCarreeBlancheSelonFormat($cote) {
$image_blanche = imagecreatetruecolor($cote, $cote);
$blanc = imagecolorallocate($image_blanche, 255, 255, 255);
imagefilledrectangle($image_blanche, 0, 0, $cote, $cote, $blanc);
 
return $image_blanche;
}
 
public function detruireImageEnMemoire($image) {
imagedestroy($image);
}
 
/**
* Supprime une image du disque, ainsi que tous les formats générés
*
* @param Integer $id
* @return boolean true si tous les formats y compris l'original ont été détruits, false s'il en reste au moins un
*/
public function detruireImageSurDisque($id) {
$formats = $this->getFormats();
// on detruit aussi l'image originale
$formats[] = 'O';
 
$destruction_formats_fichier = true;
// destructions de chacun des formats définis
foreach($formats as $format) {
$dossier_format = $this->obtenirDossierPourFormat($id, $format);
$nom_fichier = $this->convertirIdBddVersNomFichier($id, $format);
 
if (file_exists($dossier_format.'/'.$nom_fichier)) {
$detruit = unlink($dossier_format.'/'.$nom_fichier);
$destruction_formats_fichier = ($destruction_formats_fichier && $detruit);
}
}
 
return $destruction_formats_fichier;
}
 
// recopie de Cel->logger() (pas d'extends pour ça)
public function logger($index,$chaine) {
if(!class_exists('Log')) {
Log::getInstance();
}
 
Log::setCheminLog($this->config['log']['cheminlog']);
Log::setTimeZone($this->config['log']['timezone']);
Log::setTailleMax($this->config['log']['taillemax']);
 
Log::ajouterEntree($index,$chaine);
}
 
/*
* edge-maximizing crop
* determines center-of-edginess, then tries different-sized crops around it.
* picks the crop with the highest normalized edginess.
* see documentation on how to tune the algorithm
*
* $informations_image - le tableau d'informations sur l'image tel que renvoyé par la fonction obtenirImageEtInfosPourChemin
* $format - le format (ex. : CS, XS, XL, CRS)
*/
public function opticrop($informations_image, $format) {
umask(0);
$erreur_ecriture = false;
 
$nom_temp = md5(time());
$chemin_temp =
 
$out = $this->config['cel']['chemin_stockage_temp'].'/'.$nom_temp;
 
$dimension_vignettes = $this->obtenirDimensionsPourFormat($format);
 
$largeur_vignette = $dimension_vignettes['largeur'];
$hauteur_vignette = $dimension_vignettes['hauteur'];
 
// source dimensions
$largeur_image_originale = $informations_image['largeur'];
$hauteur_image_originale = $informations_image['hauteur'];
 
$chemin_image = $informations_image['chemin'];
 
//if ($largeur_vignette > $largeur_image_originale || $hauteur_vignette > $hauteur_image_originale)
// die("Target dimensions must be smaller or equal to source dimensions.");
 
// parameters for the edge-maximizing crop algorithm
$r = 1; // radius of edge filter
$nk = 9; // scale count: number of crop sizes to try
$gamma = 0.2; // edge normalization parameter -- see documentation
$ar = $largeur_vignette/$hauteur_vignette; // target aspect ratio (AR)
$ar0 = $largeur_image_originale/$hauteur_image_originale; // original aspect ratio (AR)
 
//echo("$chemin_image: $largeur_image_originale x $hauteur_image_originale => $largeur_vignette x $hauteur_vignette");
$img = new Imagick($chemin_image);
$imgcp = clone $img;
 
// compute center of edginess
$img->edgeImage($r);
$img->modulateImage(100,0,100); // grayscale
$img->blackThresholdImage("#0f0f0f");
$retour_ecriture_img = $img->writeImage($out);
 
if ($retour_ecriture_img !== true) {
error_log("Erreur d'écriture Imagick : [" . $chemin_image . "] vers [" . $out . "]");
$erreur_ecriture = true;
}
// use gd for random pixel access
$im = ImageCreateFromJpeg($out);
 
if ($im === false) {
error_log("GD ne peut pas lire l'image créée par Imagick : [" . $chemin_image . "] vers [" . $out . "]");
$erreur_ecriture = true;
}
if (! $erreur_ecriture) {
$xcenter = 0;
$ycenter = 0;
$sum = 0;
$n = 100000;
for ($k=0; $k<$n; $k++) {
$i = mt_rand(0,$largeur_image_originale-1);
$j = mt_rand(0,$hauteur_image_originale-1);
$val = imagecolorat($im, $i, $j) & 0xFF;
$sum += $val;
$xcenter += ($i+1)*$val;
$ycenter += ($j+1)*$val;
}
$xcenter /= $sum;
$ycenter /= $sum;
// crop source img to target AR
if ($largeur_image_originale/$hauteur_image_originale > $ar) {
// source AR wider than target
// crop width to target AR
$wcrop0 = round($ar*$hauteur_image_originale);
$hcrop0 = $hauteur_image_originale;
} else {
// crop height to target AR
$wcrop0 = $largeur_image_originale;
$hcrop0 = round($largeur_image_originale/$ar);
}
// crop parameters for all scales and translations
$params = array();
// crop at different scales
$hgap = $hcrop0 - $hauteur_vignette;
$hinc = ($nk == 1) ? 0 : $hgap / ($nk - 1);
$wgap = $wcrop0 - $largeur_vignette;
$winc = ($nk == 1) ? 0 : $wgap / ($nk - 1);
// find window with highest normalized edginess
$n = 10000;
$maxbetanorm = 0;
$maxfile = '';
$maxparam = array('w'=>0, 'h'=>0, 'x'=>0, 'y'=>0);
for ($k = 0; $k < $nk; $k++) {
$hcrop = round($hcrop0 - $k*$hinc);
$wcrop = round($wcrop0 - $k*$winc);
$xcrop = $xcenter - $wcrop / 2;
$ycrop = $ycenter - $hcrop / 2;
//echo("crop: $wcrop, $hcrop, $xcrop, $ycrop");
if ($xcrop < 0) $xcrop = 0;
if ($xcrop+$wcrop > $largeur_image_originale) $xcrop = $largeur_image_originale-$wcrop;
if ($ycrop < 0) $ycrop = 0;
if ($ycrop+$hcrop > $hauteur_image_originale) $ycrop = $hauteur_image_originale-$hcrop;
/*if (self::MODE_DEBUG) {
// debug
$currfile = '/home/aurelien/web/file_tmp/'."image$k.jpg";
$currimg = clone $img;
$c= new ImagickDraw();
$c->setFillColor("red");
$c->circle($xcenter, $ycenter, $xcenter, $ycenter+4);
$currimg->drawImage($c);
$currimg->cropImage($wcrop, $hcrop, $xcrop, $ycrop);
$currimg->writeImage($currfile);
$currimg->destroy();
}*/
$beta = 0;
for ($c=0; $c<$n; $c++) {
$i = mt_rand(0,$wcrop-1);
$j = mt_rand(0,$hcrop-1);
$beta += imagecolorat($im, $xcrop+$i, $ycrop+$j) & 0xFF;
}
$area = $wcrop * $hcrop;
$betanorm = $beta / ($n*pow($area, $gamma-1));
// echo("beta: $beta; betan: $betanorm");
// echo("image$k.jpg:<br/>\n<img src=\"$currfile\"/>");
// best image found, save it
if ($betanorm > $maxbetanorm) {
$maxbetanorm = $betanorm;
$maxparam['w'] = $wcrop;
$maxparam['h'] = $hcrop;
$maxparam['x'] = $xcrop;
$maxparam['y'] = $ycrop;
// $maxfile = $currfile;
}
}
// écrasement de l'image par la version "croppée"
$imgcp->cropImage($maxparam['w'], $maxparam['h'], $maxparam['x'], $maxparam['y']);
$imgcp->scaleImage($largeur_vignette, $hauteur_vignette);
$imgcp->writeImage($out);
 
// return image
chmod($out, 0777);
$img->destroy();
$imgcp->destroy();
$image_sortie = ImageCreateFromJpeg($out);
} else {
// image n'a pas pu être croppée - on retourne l'originale
//$image_sortie = ImageCreateFromJpeg($chemin_image);
$image_sortie = false;
}
 
// destruction fichier temporaire dans tous les cas
unlink($out);
 
return $image_sortie;
}
}
?>
/trunk/jrest/bibliotheque/Decoupage.class.php
New file
0,0 → 1,160
<?php
// Encodage : UTF-8
// +-------------------------------------------------------------------------------------------------------------------+
/**
* Découpage
*
* Description : classe abstraite mettant en comun des expressions régulière pour le découpage des noms latins.
*
//Auteur original :
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Tela-Botanica 1999-2009
* @licence GPL v3 & CeCILL v2
* @version $Id: Decoupage.class.php 1873 2009-03-31 10:07:24Z Jean-Pascal MILCENT $
*/
// +-------------------------------------------------------------------------------------------------------------------+
abstract class Decoupage {
protected $min = '[a-z\x{E0}-\x{FF}\x{153}]';// Lettres minuscules : a à z et &#224; à &#255 et &#339;
protected $maj = "[A-Z'\x{C0}-\x{DF}\x{152}]";// Lettres majuscules : A à Z, ' et &#192; à &#223; et &#338;
protected $hyb = '[+xX]';
protected $SupraSp;// Nom de type suprasp.
protected $GenHy;// Hybride intergénérique
protected $Gen;// Genre
protected $Dou = '(?:\(\?\)|\?)';// Doute
protected $Epi_cv;// Epithete de cultivar
protected $Epi_nn_hy = '(?:nsp\.|)';// Epithète non nommé hybride
protected $Epi_nn = '(?:sp\.[1-9]?|spp\.|)';// Epithète non nommé
protected $Epi;// Epithete
//------------------------------------------------------------------------------------------------------------//
protected $Ran_ig = '[Ss]ect\.|subg(?:en|)\.|ser\.|subser\.';// Rang taxonomique infragénérique de type : sous-genre
protected $Ran_ig_gr = 'gr\.';// Rang taxonomique infragénérique de type : groupe
protected $Ran_ig_agg = 'agg\.';// Rang taxonomique infragénérique de type : aggrégat
protected $Ran_bo_i1 = 'subsp\.';// Rang taxonomique infraspécifique de niveau 1
protected $Ran_bo_i2 = 'var\.|subvar\.';// Rang taxonomique infraspécifique de niveau 2
protected $Ran_bo_i3 = 'f\.|fa\.|fa|forma';// Rang taxonomique infraspécifique de niveau 3
protected $Ran_bo_i4 = 'race|prole|proles|prol\.';// Rang taxonomique infraspécifique de niveau 4
protected $Ran_bo;// Rang taxonomique infraspécifique botanique non hybride
protected $Ran_hy_i1 = 'n-subsp\.|\[subsp\.\]|\[n-subsp\.\]';// Rang taxonomique infraspécifique hybride de niveau 1
protected $Ran_hy_i2 = '\[var\.\]|n-var\.|\[n-var\.\]';// Rang taxonomique infraspécifique hybride de niveau 2
protected $Ran_hy_i3 = '';// Rang taxonomique infraspécifique hybride de niveau 3
protected $Ran_hy_i4 = 'n-proles\.';// Rang taxonomique infraspécifique hybride de niveau 4
protected $Ran_hy;// Rang taxonomique infraspécifique hybridre
protected $Ran_ht = 'convar\.|[cC]v\.';// Rang taxonomique horticole
protected $Ran;// Rang taxonomique infraspécifique non hybride, hybride et horticole
//------------------------------------------------------------------------------------------------------------//
protected $Ini;// Initiale de prénoms
protected $Pre;// Prénoms
protected $Par = '(?i:de|des|le|la|de la|von|van|st\.|el)';// Particules
protected $ParSsEs = "(?i:st\.-|d)";// Particules sans espace après
protected $Nom; // Abreviation d'un nom d'auteur. Le "f." c'est pour "filius" et c'est collé au nom
protected $NomSpe = '(?:[A-Z]\. (?:DC\.|St\.-Hil\.))|\(?hort\.\)?|al\.';// Prénom + nom spéciaux : "hort." est utilisé comme un nom d'auteur mais cela signifie "des jardins". "DC." est une exception deux majuscule suivi d'un point.
protected $Int;// Intitulé d'auteurs (Prénom + Nom)
//------------------------------------------------------------------------------------------------------------//
protected $AutNo;// Intitulé auteur sans "ex", ni "&", ni "et", ni parenthèses
protected $AutNoTa;// Intitulé auteur sans "ex", ni "&", ni "et" mais avec parenthèses possible pour la nomenclature
protected $AutEx;// Intitulé auteur avec "ex"
protected $et = '(?:&|et)';
protected $AutExEt;// Intitulé auteur avec "ex" et "&" ou "et"
protected $AutEt;// Intitulé auteur avec "&" ou "et" et sans parenthèse spécifique à la nomenclature
protected $AutEtTa;// Intitulé auteur avec "&" ou "et" et avec ou sans parenthèse spécifique à la nomenclature
protected $AutBib;// Intitulés auteurs pour la biblio
protected $AutInc = 'AUTEUR\?';// Intitulé auteur spéciaux pouvant être trouvés entre parenthèses
protected $AutSpe;// Intitulé auteur spéciaux pouvant être trouvés entre parenthèses
protected $AutSpeSe;// Intitulé auteur spéciaux type "sensu"
protected $AutSpeTa;// Intitulé auteur spéciaux propre à la nomenclature
protected $Aut;// Tous les intitulés auteurs possibles
protected $Auteur;// Tous les intitulés auteurs possibles
//------------------------------------------------------------------------------------------------------------//
protected $ComEmend;// Commentaires nomenclaturaux
protected $ComPp = 'p\.p\.';// Commentaires nomenclaturaux
protected $Com;// Intitulé auteur spéciaux type "sensu"
protected $ComNom = '\(?(?:hort\. non .*|sensu .*|auct\..*|comb\.\s*(?:nov\.|ined\.)|comb?\.\s*nov\.\s*provis\.|stat\.\s*provis\.|nov\.\s*stat\.|stat\.\s*nov\.|p\.p\.|emend\.)\)?';
//------------------------------------------------------------------------------------------------------------//
protected $In;// In auteur
protected $AneMoCo = 'janvier|fevrier|mars|avril|mai|juin|juillet|ao\x{FB}t|septembre|octobre|novembre|decembre'; //Mois devant l'année
protected $AneMoAb = 'janv\.|f[e\x{E9}]v\.|sept\.|oct\.|d\x{E9}c\.'; //Mois devant l'année
protected $AneBaSi = '(?:\d{4}|\d{4} ?\?|DATE \?)';// Date
protected $AneBaCo = '(?:\d{4}-\d{4}|\d{4}-\d{2})';// Date
protected $AneDo = '\?';// Doute
protected $AneBa;// Date
protected $AneSpe;// Date
protected $Ane;// Date
//------------------------------------------------------------------------------------------------------------//
// Spécial BDNFF :
protected $Date = ' \[.*\]';
protected $Num = '[0-9]|3\*|4\*';# Gestion des numéros de flore
protected $NumAuteur;# Gestion des numéros de flore mélangés ou pas au nom d'auteur
//------------------------------------------------------------------------------------------------------------//
protected $BibBa;// Biblio de base : \x{B0} = ° \x{AB}\x{BB} = «» \x{26} = &
protected $Bib;// Biblio de taxon
protected $BibAu = '.+';// Biblio supplémentaire
//------------------------------------------------------------------------------------------------------------//
protected $ErrDet;// Biblio à exclure base
//------------------------------------------------------------------------------------------------------------//
protected $HomNon = 'non';// Homonymes à exclure : négation
protected $HomBa;// Homonymes à exclure base
protected $Hom;// Homonymes à exclure avec non et nec
protected $HomCourt;// Homonymes à exclure avec non et nec avec expression régulière plus courte!
//------------------------------------------------------------------------------------------------------------//
protected $Inf = '.*';// Informations supplémentaires
public function __construct()
{
//mb_internal_encoding('UTF-8');
//mb_regex_encoding('UTF-8');
//setlocale(LC_ALL, 'fr-fr');
$this->SupraSp = '(?:'.$this->maj.$this->min.'+|'.$this->maj.$this->min.'+-'.$this->maj.$this->min.'+)';// Nom de type suprasp.
$this->GenHy = "[Xx] $this->SupraSp";// Hybride intergénérique
$this->Gen = "$this->SupraSp|$this->GenHy";
$this->Epi_cv = "$this->maj.(?:$this->min|-)+";// Epithete de cultivar
$this->Epi_nn = $this->Epi_nn.$this->Epi_nn_hy;
$this->Epi = "(?:(?:$this->min|-|')+|$this->Epi_nn)";// Epithete
$this->Ran_ig = $this->Ran_ig.'|'.$this->Ran_ig_gr;
$this->Ran_bo = "$this->Ran_bo_i1|$this->Ran_bo_i2|$this->Ran_bo_i3|$this->Ran_bo_i4";// Rang taxonomique infraspécifique botanique non hybride
$this->Ran_hy = "$this->Ran_hy_i1|$this->Ran_hy_i2|$this->Ran_hy_i3|$this->Ran_hy_i4";// Rang taxonomique infraspécifique hybridre
$this->Ran = "(?:$this->Ran_ig|$this->Ran_bo|$this->Ran_hy|$this->Ran_ht)";// Rang taxonomique infraspécifique non hybride, hybride et horticole
$this->Ini = '(?:'.$this->maj.'[.]|'.$this->maj.$this->min.'+[.]?)';// Initiale de prénoms
$this->Pre = $this->Ini.'{1,3}|'.$this->Ini.'[\- ]'.$this->Ini;// Prénoms
$this->Nom = '(?:'.$this->maj."'".$this->maj.'|'.$this->maj.'|'.$this->maj.$this->min."+'".$this->min.'+)'.$this->min.'*[.]?(?: ?f\.|)';
$this->Int = "(?:(?:$this->Pre ?|)(?:$this->Par |$this->ParSsEs|)(?:$this->Nom|$this->Nom".'[\- .]'."$this->Nom)|$this->NomSpe)";// Intitulé d'auteurs (Prénom + Nom)
$this->AutNo = "$this->Int";// Intitulé auteur sans "ex", ni "&", ni "et", ni parenthèses
$this->AutNoTa = "$this->AutNo|$this->NomSpe $this->Int|\($this->Int\) $this->Int";// Intitulé auteur sans "ex", ni "&", ni "et" mais avec parenthèses possible pour la nomenclature
$this->AutEx = "\($this->Int\) $this->Int ex $this->Int|\($this->Int ex $this->Int\) $this->Int|$this->Int ex $this->Int";// Intitulé auteur avec "ex"
$this->AutExEt = "$this->Int $this->et $this->Int ex $this->Int|$this->Int $this->et $this->Int ex $this->Int $this->et $this->Int|$this->Int ex $this->Int $this->et $this->Int|\($this->Int ex $this->Int $this->et $this->Int\) $this->Int|\($this->Int ex $this->Int\) $this->Int $this->et $this->Int|\($this->Int $this->et $this->Int\) $this->Int ex $this->Int|$this->NomSpe $this->Int ex $this->Int";// Intitulé auteur avec "ex" et "&" ou "et"
$this->AutEt = "$this->Int $this->et $this->Int";// Intitulé auteur avec "&" ou "et" et sans parenthèse spécifique à la nomenclature
$this->AutEtTa = "\($this->Int\) $this->Int $this->et $this->Int|\($this->Int $this->et $this->Int\) $this->Int|$this->AutEt";// Intitulé auteur avec "&" ou "et" et avec ou sans parenthèse spécifique à la nomenclature
$this->AutBib = "(?:$this->AutNo|$this->AutEt)";// Intitulés auteurs pour la biblio
$this->AutSpe = "(?:sensu |)auct\.|auct\. mult\.|$this->AutInc";// Intitulé auteur spéciaux pouvant être trouvés entre parenthèses
$this->AutSpeSe = "sensu $this->AutBib";// Intitulé auteur spéciaux type "sensu"
$this->AutSpeTa = "$this->AutSpe|\((?:$this->AutSpe)\)|$this->AutSpeSe";// Intitulé auteur spéciaux propre à la nomenclature
$this->Aut = "(?:$this->AutExEt|$this->AutEx|$this->AutEtTa|$this->AutSpeTa|$this->AutNoTa)";// Tous les intitulés auteurs possibles
$this->Auteur = $this->Int.'|'.$this->Int.' '.$this->et.' '.$this->Int.'|(?:'.$this->Int.', )+'.$this->Int.' '.$this->et.' '.$this->Int;// Intitulé auteur avec "&" ou "et";
$this->ComEmend = "emend\. $this->AutBib";// Commentaires nomenclaturaux
$this->Com = "$this->ComEmend|$this->ComPp";// Intitulé auteur spéciaux type "sensu"
$this->In = "[iI]n $this->AutBib";// In auteur
$this->AneBa = "$this->AneBaSi|$this->AneBaCo";// Date
$this->AneSpe = "(?:$this->AneBa ?\[$this->AneBa\]|(?:$this->AneMoCo|$this->AneMoAb) $this->AneBaSi|$this->AneBaSi $this->AneBaSi)";// Date
$this->Ane = "$this->AneBa||$this->AneDo|$this->AneSpe";// Date
$this->BibBa = "(?:$this->maj$this->min*[.]?|in|hort\.)(?:$this->min*[.]?|[\d\/\- ,()'\x{B0}\x{26}\x{AB}\x{BB}[\]?])*";// Biblio de base : \x{B0} = ° \x{AB}\x{BB} = «» \x{26} = &
$this->Bib = "([^:]+):(.+?)\(($this->Ane)\)";// Biblio de taxon
$this->ErrDet = "($this->AutSpe,? non $this->Aut): ($this->Bib;?)+";// Biblio à exclure base
$this->HomBa = "$this->Aut \($this->Ane\)";// Homonymes à exclure base
$this->Hom = "$this->HomNon $this->HomBa(?: nec $this->HomBa)*?";// Homonymes à exclure avec non et nec
$this->HomCourt = "$this->HomNon .+?(?: nec .+?)*?";// Homonymes à exclure avec non et nec avec expression régulière plus courte!
$this->NumAuteur = $this->Num.'|(?:(?:'.$this->Num.'|'.$this->Auteur.'), )+(?:'.$this->Num.'|'.$this->Auteur.')';# Gestion des numéros de flore mélangés ou pas au nom d'auteur
}
}
?>
/trunk/jrest/bibliotheque/RechercheInfosTaxon.php
New file
0,0 → 1,410
<?php
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author David Delon <david@tela-botania.org>
* @author Aurélien Peronnet <aurelien@tela-botania.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
*
* Classe de recherche d'informations sur un taxon donné
* lors de l'évolution d'eflore, devrait être remplacée par
* un appel aux nouveaux web services
*/
class RechercheInfosTaxon extends Cel {
 
public function RechercheInfosTaxon($config) {
 
parent::__construct($config);
// Connection à la base de données spécifique eflore
$this->bdd = $this->connecterPDO($this->config, 'eflore');
}
 
public function rechercherGenreEspeceSurPrefixe($genre = null, $espece = null) {
 
$liste_genre_espece = array();
 
$requete_recherche = '';
// Genre et Espece
if ($espece != null && $genre != null) {
if (strlen($espece) > 0 ) {
$espece = preg_replace('/\*+/','%',$espece);
$requete_recherche = "SELECT DISTINCT en_nom_genre, en_epithete_espece, en_nom_supra_generique, en_epithete_infra_generique,".
" auteur_bex.enaia_intitule_abrege AS abreviation_auteur_basio_ex ".
" , auteur_b.enaia_intitule_abrege AS abreviation_auteur_basio ".
" , auteur_mex.enaia_intitule_abrege AS abreviation_auteur_modif_ex ".
" , auteur_m.enaia_intitule_abrege AS abreviation_auteur_modif ".
" , en_epithete_espece, en_epithete_infra_specifique, enrg_abreviation_rang, en_id_nom, esn_ce_statut" .
" FROM eflore_nom, eflore_nom_rang, " .
" eflore_naturaliste_intitule_abreviation AS auteur_bex ".
" , eflore_naturaliste_intitule_abreviation AS auteur_b ".
" , eflore_naturaliste_intitule_abreviation AS auteur_mex ".
" , eflore_naturaliste_intitule_abreviation AS auteur_m ".
" , eflore_selection_nom".
" WHERE en_id_version_projet_nom = '25' AND en_nom_genre LIKE ".Cel::db()->proteger($genre.'%').
" AND en_ce_rang > 160 " .
" AND en_epithete_espece like ".Cel::db()->proteger($espece.'%')." AND en_ce_rang = enrg_id_rang " .
" AND en_ce_auteur_basio_ex = auteur_bex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_basio = auteur_b.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif_ex = auteur_mex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif = auteur_m.enaia_id_intitule_naturaliste_abrege ".
" AND esn_id_version_projet_taxon=en_id_version_projet_nom " .
" AND esn_id_nom= en_id_nom ".
" ORDER BY esn_ce_statut, en_ce_rang, en_epithete_espece, en_nom_genre LIMIT 50";
}
} else {
if ($genre != null) {
$genre = preg_replace('/\*+/', '%', $genre);
//TODO: comprendre pourquoi à l'origine il y avait : (strlen($genre) >= 1) /*&& ($genre != '%')
// voir avec david
if (strlen($genre) >= 1) {
$requete_recherche = "SELECT DISTINCT en_nom_genre, en_id_nom, 0 as esn_ce_statut FROM eflore_nom WHERE en_id_version_projet_nom = '25'" .
"AND en_ce_rang = 160 " .
"AND en_nom_genre LIKE ".Cel::db()->proteger($genre.'%')." ORDER BY esn_ce_statut, en_nom_genre LIMIT 50";
}
}
}
 
if ($requete_recherche != '') {
$resultat_recherche = Cel::db()->requeter($requete_recherche);
 
if (is_array($resultat_recherche)) {
foreach ($resultat_recherche as $ligne) {
$liste_genre_espece[] = array($this->formaterNom($ligne),
$ligne['en_id_nom'],
$ligne['esn_ce_statut']
);
}
}
}
 
return $liste_genre_espece;
}
 
function rechercherInformationsComplementairesSurNumNom($numNom) {
$resultat_infos_complementaires = $this->effectuerRequeteInfosComplementairesSurNumNom($numNom);
 
// Nom retenu, Num Nomen nom retenu, Num Taxon, Famille
$value=array('Nom_Retenu'=>"",'Num_Nom_Retenu'=>"0",'Num_Taxon'=>"0",'Famille'=>"");
if (is_array($resultat_infos_complementaires)) {
foreach ($resultat_infos_complementaires as $row) {
$fam = $this->rechercherFamille($row['esn_id_taxon']);
while ($fam['en_ce_rang'] != 'fin' && $fam['en_ce_rang'] != 120) {
$fam = $this->rechercherFamille($fam['etr_id_taxon_2']);
}
if ($fam['en_ce_rang'] == 120) {
$famille = $fam['en_nom_supra_generique'];
} else {
$famille = "Famille inconnue";
}
$value = array('Nom_Retenu'=>$this->formaterNom($row),'Num_Nom_Retenu'=>$row['esn_id_nom'],'Num_Taxon'=>$row['esn_id_taxon'],'Famille'=>$famille);
}
}
return $value;
}
 
public function effectuerRequeteInfosComplementairesEtFormaterNom($numNom) {
$resultat = $this->effectuerRequeteInfosComplementairesSurNumNom($numNom);
 
$infos = array();
foreach ($resultat as $info) {
$infos = array($this->formaterNom($info));
}
return $infos;
}
 
public function effectuerRequeteInfosComplementairesSurNumNom($numNom) {
$requete_infos_complementaires = "SELECT DISTINCT en_nom_genre, en_epithete_espece, en_nom_supra_generique, en_epithete_infra_generique,".
" auteur_bex.enaia_intitule_abrege AS abreviation_auteur_basio_ex ".
" , auteur_b.enaia_intitule_abrege AS abreviation_auteur_basio ".
" , auteur_mex.enaia_intitule_abrege AS abreviation_auteur_modif_ex ".
" , auteur_m.enaia_intitule_abrege AS abreviation_auteur_modif ".
" , en_epithete_espece, en_epithete_infra_specifique, enrg_abreviation_rang, b.esn_id_taxon, b.esn_id_nom" .
" FROM eflore_nom, eflore_nom_rang, " .
" eflore_naturaliste_intitule_abreviation AS auteur_bex, ".
" eflore_naturaliste_intitule_abreviation AS auteur_b, ".
" eflore_naturaliste_intitule_abreviation AS auteur_mex, ".
" eflore_naturaliste_intitule_abreviation AS auteur_m, ".
" eflore_selection_nom a, eflore_selection_nom b ".
" WHERE a.esn_id_nom= ".Cel::db()->proteger($numNom).
" AND a.esn_id_version_projet_taxon = 25 ".
" AND a.esn_id_taxon=b.esn_id_taxon ".
" AND b.esn_ce_statut=3 ".
" AND a.esn_id_version_projet_taxon=b.esn_id_version_projet_taxon" .
" AND en_ce_rang = enrg_id_rang" .
" AND en_id_nom = b.esn_id_nom" .
" AND en_ce_auteur_basio_ex = auteur_bex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_basio = auteur_b.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif_ex = auteur_mex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif = auteur_m.enaia_id_intitule_naturaliste_abrege ".
" AND a.esn_id_version_projet_taxon=en_id_version_projet_nom ";
 
 
$resultat_infos_complementaires = Cel::db()->requeter($requete_infos_complementaires);
return $resultat_infos_complementaires;
}
 
public function effectuerRequeteInfosComplementairesSurNumTax($numTax) {
 
$requete_infos_complementaires = "SELECT DISTINCT en_nom_genre, en_epithete_espece, en_nom_supra_generique, en_epithete_infra_generique,".
" auteur_bex.enaia_intitule_abrege AS abreviation_auteur_basio_ex ".
" , auteur_b.enaia_intitule_abrege AS abreviation_auteur_basio ".
" , auteur_mex.enaia_intitule_abrege AS abreviation_auteur_modif_ex ".
" , auteur_m.enaia_intitule_abrege AS abreviation_auteur_modif ".
" , en_epithete_espece, en_epithete_infra_specifique, enrg_abreviation_rang, en_id_nom" .
" FROM eflore_nom, eflore_nom_rang, " .
"eflore_naturaliste_intitule_abreviation AS auteur_bex ".
" , eflore_naturaliste_intitule_abreviation AS auteur_b ".
" , eflore_naturaliste_intitule_abreviation AS auteur_mex ".
" , eflore_naturaliste_intitule_abreviation AS auteur_m ".
" , eflore_selection_nom ".
" WHERE esn_id_taxon = '".$numTax. "'".
" AND esn_id_version_projet_taxon = 25 ".
" AND esn_ce_statut=3 ".
" AND en_id_nom = esn_id_nom" .
" AND en_ce_rang = enrg_id_rang" .
" AND en_ce_auteur_basio_ex = auteur_bex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_basio = auteur_b.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif_ex = auteur_mex.enaia_id_intitule_naturaliste_abrege ".
" AND en_ce_auteur_modif = auteur_m.enaia_id_intitule_naturaliste_abrege ".
" AND esn_id_version_projet_taxon=en_id_version_projet_nom ";
 
$resultat_infos_complementaires = Cel::db()->requeter($requete_infos_complementaires);
 
return $resultat_infos_complementaires;
}
 
public function rechercherInformationsComplementairesSurNom($nom_saisi) {
 
$value = array();
 
if ($nom_saisi != null && $nom_saisi != "") {
$requete_infos_comp_sur_nom = 'SELECT * FROM eflore_nom_intitule '.
'WHERE eni_id_categorie_format = 3 AND '.
'eni_id_version_projet_nom = 25 AND '.
'(eni_id_valeur_format = 3 OR eni_id_valeur_format = 4) AND '.
'eni_intitule_nom LIKE "'.$nom_saisi.'%" '.
'ORDER BY LENGTH(eni_intitule_nom)';
 
$resultat_infos_comp_sur_nom = Cel::db()->requeter($requete_infos_comp_sur_nom);
 
if (is_array($resultat_infos_comp_sur_nom)) {
foreach ($resultat_infos_comp_sur_nom as $ligne) {
$value[]=array($ligne['eni_id_nom'], $ligne['eni_intitule_nom']);
}
}
}
 
return $value;
}
 
public function rechercherFamille($taxon) {
$row = array();
 
$requete_famille = "SELECT DISTINCT en_ce_rang, etr_id_taxon_2, en_id_nom, en_nom_supra_generique ".
" FROM eflore_taxon_relation, ".
" eflore_selection_nom, ".
" eflore_nom ".
" WHERE etr_id_taxon_1 = ".$taxon.
" AND etr_id_version_projet_taxon_1 = 25 ".
" AND etr_id_categorie_taxon = 3 ".
" AND etr_id_valeur_taxon = 3 ".
" AND esn_id_taxon = etr_id_taxon_2 ".
" AND esn_ce_statut = 3 ".
" AND esn_id_version_projet_taxon = etr_id_version_projet_taxon_1 ".
" AND en_id_nom = esn_id_nom ".
" AND esn_id_version_projet_taxon=en_id_version_projet_nom ";
 
$resultat_recherche_famille = Cel::db()->requeter($requete_famille);
 
if (!is_array($resultat_recherche_famille) || count($resultat_recherche_famille) == 0) {
$resultat_recherche_famille = array('en_ce_rang' => 'fin');
} else {
$resultat_recherche_famille = $resultat_recherche_famille[0];
}
 
return $resultat_recherche_famille;
}
 
public function rechercherNumTaxSurNumNom($num_nom) {
$requete_num_tax = "SELECT DISTINCT b.esn_id_taxon FROM eflore_nom, eflore_nom_rang," .
" eflore_selection_nom a, eflore_selection_nom b".
" WHERE a.esn_id_nom= ".Cel::db()->proteger($num_nom).
" AND a.esn_id_version_projet_taxon = 25 ".
" AND a.esn_id_taxon=b.esn_id_taxon ".
" AND b.esn_ce_statut=3 ".
" AND a.esn_id_version_projet_taxon=b.esn_id_version_projet_taxon" .
" AND en_ce_rang = enrg_id_rang" .
" AND en_id_nom = b.esn_id_nom" .
" AND a.esn_id_version_projet_taxon=en_id_version_projet_nom ";
 
$res_num_nom = Cel::db()->requeter($requete_num_tax);
 
$nt = null;
if (is_array($res_num_nom) && count($res_num_nom) > 0) {
$nt=$res_num_nom[0]['esn_id_taxon'];
}
 
return $nt;
}
 
public function taxonEstPresentDansDepartement($num_taxon,$code_departement) {
 
$requete_presence_taxon = "SELECT ecd_ce_taxon FROM eflore_zg, eflore_chorologie_donnee ".
"WHERE ecd_ce_taxon = ".Cel::db()->proteger($num_taxon)." ".
"AND ezg_code = ".Cel::db()->proteger($code_departement)." ".
"AND ecd_ce_zone_geo = ezg_id_zone_geo ".
"AND ezg_id_projet_zg = ecd_ce_version_projet_zg ".
"AND ecd_ce_version_projet_taxon=25";
 
$resultat_presence_taxon = Cel::db()->requeter($requete_presence_taxon);
 
$presence_taxon = (is_array($resultat_presence_taxon) && count($resultat_presence_taxon) > 0);
 
return $presence_taxon;
}
 
private function decouperNomEtRechercheEspeceOuSousEspece($identifiant_espece) {
$nameparser=new NameParser();
$nom_latin_decoupe=$nameparser->parse($identifiant_espece);
// requete sous espece (on privilegie les noms retenu cf tri par esn_ce_statut)
if (isset($nom_latin_decoupe['infra']) && $nom_latin_decoupe['infra']!="") {
$requete="SELECT DISTINCT en_id_nom, esn_ce_statut" .
" FROM eflore_nom, eflore_nom_rang, eflore_selection_nom " .
" WHERE en_id_version_projet_nom = '25' AND en_nom_genre = ".Cel::db()->proteger($nom_latin_decoupe['genus'])." " .
" AND enrg_abreviation_rang = ".Cel::db()->proteger($nom_latin_decoupe['infra_type'])." " .
" AND en_epithete_infra_specifique = ".Cel::db()->proteger($nom_latin_decoupe['infra'])." " .
" AND esn_id_nom= en_id_nom ".
" AND esn_id_version_projet_taxon=en_id_version_projet_nom " .
" AND en_epithete_espece = ".Cel::db()->proteger($nom_latin_decoupe['species'])." AND en_ce_rang = enrg_id_rang " .
" ORDER BY esn_ce_statut ".
" LIMIT 1";
}
else { // espece (on privilegie les noms retenu cf tri par esn_ce_statut)
$requete="SELECT DISTINCT en_id_nom, esn_ce_statut" .
" FROM eflore_nom, eflore_nom_rang, eflore_selection_nom " .
" WHERE en_id_version_projet_nom = '25' AND en_nom_genre = ".Cel::db()->proteger($nom_latin_decoupe['genus'])." " .
" AND enrg_abreviation_rang = 'sp.' " .
" AND esn_id_nom= en_id_nom ".
" AND esn_id_version_projet_taxon=en_id_version_projet_nom " .
" AND en_epithete_espece = ".Cel::db()->proteger($nom_latin_decoupe['species'])." AND en_ce_rang = enrg_id_rang " .
" ORDER BY esn_ce_statut ".
" LIMIT 1";
 
}
 
$resultat = Cel::db()->requeter($requete);
 
$retour = array();
if (is_array($resultat) && count($resultat) > 0) {
$retour = $resultat[0];
}
 
return $retour;
}
 
private function formaterNom($rawnom) {
// Constitution du nom:
$nom = '';
 
if (isset($rawnom['en_nom_supra_generique']) && $rawnom['en_nom_supra_generique'] != '') {
$nom .= $rawnom['en_nom_supra_generique'];
} else if (isset($rawnom['en_epithete_infra_generique']) && $rawnom['en_epithete_infra_generique'] != '') {
$nom .= $rawnom['en_epithete_infra_generique'];
} else {
if (isset($rawnom['en_nom_genre']) && $rawnom['en_nom_genre'] != '') {
$nom .= $rawnom['en_nom_genre'];
}
if (isset($rawnom['en_epithete_espece']) && $rawnom['en_epithete_espece']!= '') {
$nom .= ' '.$rawnom['en_epithete_espece'];
}
if (isset($rawnom['en_epithete_infra_specifique']) && $rawnom['en_epithete_infra_specifique'] != '') {
if (!empty($rawnom['enrg_abreviation_rang'])) {
$nom .= ' '.$rawnom['enrg_abreviation_rang'].'';
}
$nom .= ' '.$rawnom['en_epithete_infra_specifique'];
}
}
 
return $nom.$this->retournerAuteur($rawnom) ;
}
 
private function retournerAuteur($rawnom) {
$auteurs = '';
$auteur_basio = '';
$auteur_modif = '';
if (!empty($rawnom['abreviation_auteur_basio_ex']) && $rawnom['abreviation_auteur_basio_ex']!='-' ) {
$auteur_basio .= $rawnom['abreviation_auteur_basio_ex'];
if (!empty($rawnom['abreviation_auteur_basio']) && $rawnom['abreviation_auteur_basio']!='-') {
$auteur_basio .= ' ex '.$rawnom['abreviation_auteur_basio'];
}
} else if (!empty($rawnom['abreviation_auteur_basio']) && $rawnom['abreviation_auteur_basio']!='-') {
$auteur_basio .= $rawnom['abreviation_auteur_basio'];
}
 
if (!empty($rawnom['abreviation_auteur_modif_ex']) && $rawnom['abreviation_auteur_modif_ex']!='-') {
$auteur_modif .= $rawnom['abreviation_auteur_modif_ex'];
if (!empty($rawnom['abreviation_auteur_modif']) && $rawnom['abreviation_auteur_modif']!='-') {
$auteur_modif .= ' ex '.$rawnom['abreviation_auteur_modif'];
}
} else if (!empty($rawnom['abreviation_auteur_modif']) && $rawnom['abreviation_auteur_modif']!='-') {
$auteur_modif .= $rawnom['abreviation_auteur_modif'];
}
 
if (!empty($auteur_modif)) {
$auteurs = ' ('.$auteur_basio.') '.$auteur_modif;
} elseif (!empty($auteur_basio)) {
$auteurs = ' '.$auteur_basio;
}
 
return $auteurs ;
}
 
function rechercherInfosSurTexteCodeOuNumTax($identifiant_espece) {
// texte libre, nom scientifique,
// ou code nomenclatural (format BDNFFnn999999)
// ou code taxonomique (format BDNFFnt999999)
$identifiant_espece = trim($identifiant_espece);
$identifiant_espece = utf8_encode($identifiant_espece);
 
$retour = array();
 
preg_match('/BDNFFnn([0-9][0-9]*)/',$identifiant_espece, $elements);
if (isset($elements[1])) {
// Numero nomenclatural
$infos_taxon = $this->rechercherInformationsComplementairesSurNumNom($elements[1]);
$retour = array("nom_sel" => $this->formaterNom($infos_taxon), "en_id_nom" => $elements[1]);
} else {
// Numero taxonomique ou nom scientifique
preg_match('/BDNFFnt([0-9][0-9]*)/', $identifiant_espece, $elements);
 
if (isset($elements[1])) {
// Numero taxonomique
$infos_taxon = $this->effectuerRequeteInfosComplementairesSurNumTax($elements[1]);
$infos_taxon = $infos_taxon[0];
$retour = array("nom_sel" => $this->formaterNom($infos_taxon), "en_id_nom" => $infos_taxon['en_id_nom']);
} else {
// Nom scientifique
$id_nom = $this->decouperNomEtRechercheEspeceOuSousEspece($identifiant_espece);
// Recherche du nom associe
$retour = array("nom_sel" => $identifiant_espece);
if(is_array($id_nom) && isset($id_nom['en_id_nom'])) {
$infos_nom = $this->effectuerRequeteInfosComplementairesSurNumNom($id_nom['en_id_nom']);
if (is_array($infos_nom) && !empty($infos_nom)) {
$infos_nom = $infos_nom[0];
$retour = array("nom_sel" => $this->formaterNom($infos_nom), "en_id_nom" => $id_nom['en_id_nom']);
}
}
}
}
 
return $retour;
}
}
/trunk/jrest/bibliotheque/CartoGroupage.php
New file
0,0 → 1,278
<?php
class CartoGroupage {
const MARQUEUR_GROUPE = 'GROUPE';
const MARQUEUR_COMMUNE = 'COMMUNE';
const MARQUEUR_STATION = 'STATION';
 
private static $seuilClusterisation = 200;
private static $zoomDefaut = 3;
private static $zoomMaxClustering = 12;
private static $pasZoomDefaut = 1;
private static $pasZoomMaxClustering = 0.05;
private static $profondeurMin = 0;
private static $profondeurMax = 8;
 
private static $pasCorrectionCentre = null;
private static $coefficientReductionPas = null;
private static $coefficientProfondeurMax = null;
 
private static $nbElements = array('stations' => 0,'communes' => 0, 'points' => 0);
private static $listeNoeudsSelectionnes = array();
private static $bornesMax = array('latMin' => null, 'lngMin' => null, 'latMax' => null, 'lngMax' => null);
private static $id_traites = array();
/*
+---------+---------+
| | |
| A | B |
| | |
+---------*---------+
| | |
| D | C |
| | |
+---------+---------+
 
Quatres cadrans sont considérés par le quad tree
* = centre de la fenetre
*/
public static function creerGroupesQuadtree(&$markers, $neLat, $neLng, $swLat, $swLng, $zoom = 3) {
if (count($markers) > self::$seuilClusterisation) {
 
self::calculerProfondeurMax($zoom);
self::calculerPasCorrectionCentre($zoom);
 
$noeudRacine = array('nbrePoints' => count($markers), 'points' => $markers);
self::attribuerAuCadran($noeudRacine, $neLat, $neLng, $swLat, $swLng);
 
} else {
foreach($markers as &$marker) {
if(!self::estUnPointAExclure($marker)) {
$emplacement = self::formaterPointPourAjout($marker);
self::mettreAJourBornes($marker);
$points = array($marker);
$noeudSimple = array('points' => $points, 'nbrePoints' => 1);
self::$nbElements[$emplacement]++;
self::$listeNoeudsSelectionnes[] = self::ajouterGroupeOuPoint($noeudSimple);
}
self::$nbElements['points']++;
}
}
return self::$listeNoeudsSelectionnes;
}
 
private function calculerCoefficientReductionPas() {
if(self::$coefficientReductionPas == null) {
self::$coefficientReductionPas = (self::$pasZoomMaxClustering - self::$pasZoomDefaut)/(self::$zoomMaxClustering - self::$zoomDefaut);
}
 
return self::$coefficientReductionPas;
}
 
private function calculerPasCorrectionCentre($zoom) {
self::$pasCorrectionCentre = ($zoom - self::$zoomDefaut) * self::calculerCoefficientReductionPas() + self::$pasZoomDefaut;
}
 
private function calculerCoefficientProfondeurMax() {
if(self::$coefficientProfondeurMax == null) {
self::$coefficientProfondeurMax = (self::$profondeurMax - self::$profondeurMin)/(self::$zoomMaxClustering - self::$zoomDefaut);
}
 
return self::$coefficientProfondeurMax;
}
 
private function calculerProfondeurMax($zoom) {
if($zoom > self::$zoomDefaut) {
self::$profondeurMax = round(($zoom - self::$zoomDefaut) * self::calculerCoefficientProfondeurMax() + self::$profondeurMin,0);
} else {
self::$profondeurMax = 1;
}
}
 
public static function getNbElements() {
return self::$nbElements;
}
 
public static function mettreAJourBornes(&$point) {
if (!is_numeric($point['lat']) || abs($point['lat']) > 90) return;
if (!is_numeric($point['lng']) || abs($point['lng']) > 180) return;
 
self::$bornesMax['latMin'] = ($point['lat'] < self::$bornesMax['latMin'] || self::$bornesMax['latMin'] == null) ? $point['lat'] : self::$bornesMax['latMin'] ;
self::$bornesMax['lngMin'] = ($point['lng'] < self::$bornesMax['lngMin'] || self::$bornesMax['lngMin'] == null) ? $point['lng'] : self::$bornesMax['lngMin'] ;
self::$bornesMax['latMax'] = ($point['lat'] > self::$bornesMax['latMax'] || self::$bornesMax['latMax'] == null) ? $point['lat'] : self::$bornesMax['latMax'] ;
self::$bornesMax['lngMax'] = ($point['lng'] > self::$bornesMax['lngMax'] || self::$bornesMax['lngMax'] == null) ? $point['lng'] : self::$bornesMax['lngMax'] ;
}
 
public static function getBornes() {
return self::$bornesMax;
}
 
/**
*
* @param mixed $noeud Le noeud à traiter par le quadtree
* @param float $neLat Latitude du coin nord est de la fenetre
* @param float $neLng Longitude du coin nord est de la fenetre
* @param float $swLat Latitude du coin sud ouest de la fenetre
* @param float $swLng Longitude du coin sud ouest de la fenetre
* @param int $profondeur profondeur courante de l'arbre
*/
private static function attribuerAuCadran(&$noeud, $neLat, $neLng, $swLat, $swLng, $profondeur = 0) {
$latCentre = round((($neLat+$swLat)/2)/self::$pasCorrectionCentre,0)*self::$pasCorrectionCentre;
$lngCentre = round((($neLng+$swLng)/2)/self::$pasCorrectionCentre,0)*self::$pasCorrectionCentre;
 
foreach ($noeud['points'] as &$point) {
if(!self::estUnPointAExclure($point)) {
$emplacement = self::formaterPointPourAjout($point);
self::mettreAJourBornes($point);
$cadran = self::obtenirCadranPourPoint($latCentre, $lngCentre, $point);
self::ajouterFils($noeud,$cadran,$point);
self::$nbElements[$emplacement]++;
}
self::$nbElements['points']++;
}
 
$profondeur++;
 
if($profondeur <= self::$profondeurMax) {
(isset($noeud['A']) && $noeud['A'] != null) ? self::attribuerAuCadran($noeud['A'], $neLat, $lngCentre , $latCentre, $swLng, $profondeur) : '';
(isset($noeud['B']) && $noeud['B'] != null) ? self::attribuerAuCadran($noeud['B'], $neLat, $neLng, $latCentre, $lngCentre, $profondeur) : '';
(isset($noeud['C']) && $noeud['C'] != null) ? self::attribuerAuCadran($noeud['C'], $latCentre, $neLng, $swLat, $lngCentre, $profondeur) : '';
(isset($noeud['D']) && $noeud['D'] != null) ? self::attribuerAuCadran($noeud['D'], $latCentre, $lngCentre, $swLat, $swLng, $profondeur) : '';
}
 
if(self::estUnParentFeuilles($noeud)) {
self::$listeNoeudsSelectionnes[] = self::ajouterGroupeOuPoint($noeud);
}
}
 
private static function estUnPointAExclure(&$point) {
return self::estSensible($point) &&
self::coordonneesCommuneSontNulles($point);
}
 
private static function coordonneesCommuneSontNulles(&$point) {
$coord_nulles = ($point['wgs84_latitude'] == null ||
$point['wgs84_latitude'] == '' ||
$point['wgs84_longitude'] == null ||
$point['wgs84_longitude'] == '');
return $coord_nulles;
}
 
private static function coordonneesSontNulles(&$point) {
$coord_nulles = ($point['latitude'] == '000null' ||
$point['latitude'] == 0 ||
$point['latitude'] == '' ||
$point['longitude'] == '000null' ||
$point['longitude'] == 0 ||
$point['longitude'] == '');
return $coord_nulles;
}
 
private static function estSensible(&$point) {
$sensible = isset($point['mots_cles_texte']) && substr_count(strtolower($point['mots_cles_texte']), 'sensible') != 0;
return $sensible;
}
 
private static function formaterNomStation(&$point, $type_emplacement) {
$station = '';
if($type_emplacement == 'stations' && $point['station'] != '' && $point['station'] != '000null') {
$station = $point['station'];
} else {
$id_zone_geo = $point['ce_zone_geo'];
$station = $point['zone_geo'].(($id_zone_geo != '' && $id_zone_geo != '000null') ? ' ('.self::formaterNomZoneGeo($id_zone_geo).')' : '');
}
 
return $station;
}
 
private static function formaterNomZoneGeo($zone_geo) {
$zone_geo = str_replace('INSEE-C:', '', $zone_geo);
$zone_geo = strlen($zone_geo) >= 2 ? substr($zone_geo, 0, 2) : $zone_geo;
return $zone_geo;
}
 
private static function formaterPointPourAjout(&$point) {
if(isset($point['type_emplacement'])) {
return $point['type_emplacement'];
}
 
if(self::coordonneesSontNulles($point) || self::estSensible($point)) {
$point_allege = array();
$point_allege['id'] = self::MARQUEUR_COMMUNE.':'.$point['wgs84_latitude'].'|'.$point['wgs84_longitude'];
$point_allege['type_emplacement'] = 'communes';
$point_allege['nom'] = self::formaterNomStation($point, 'communes');
$point_allege['lat'] = (float)$point['wgs84_latitude'];
$point_allege['lng'] = (float)$point['wgs84_longitude'];
$point_allege['zonegeo'] = $point['ce_zone_geo'];
 
$point = $point_allege;
} else {
$point_allege = array();
$point_allege['id'] = self::MARQUEUR_STATION.':'.$point['latitude'].'|'.$point['longitude'];
$point_allege['type_emplacement'] = 'stations';
$point_allege['nom'] = self::formaterNomStation($point, 'stations');
$point_allege['lat'] = (float)$point['latitude'];
$point_allege['lng'] = (float)$point['longitude'];
$point_allege['zonegeo'] = $point['ce_zone_geo'];
 
$point = $point_allege;
}
 
return $point['type_emplacement'];
}
 
private function obtenirCadranPourPoint($latCentre,$lngCentre, &$point) {
if ($point['lng'] < $lngCentre) {
if ($point['lat'] > $latCentre) {
$cadran = 'A';
} else {
$cadran = 'D';
}
} else {
if ($point['lat'] > $latCentre) {
$cadran = 'B';
} else {
$cadran = 'C';
}
}
return $cadran;
}
 
private static function ajouterFils(&$noeud, $cadran, &$point) {
if(!isset($noeud[$cadran])) {
$noeud[$cadran] = array('points' => array(),'nbrePoints' => 0, 'latMoyenne' => 0, 'lngMoyenne' => 0);
}
$noeud[$cadran]['points'][] = $point;
$noeud[$cadran]['nbrePoints']++;
$noeud[$cadran]['latMoyenne'] += $point['lat'];
$noeud[$cadran]['lngMoyenne'] += $point['lng'];
}
 
private static function ajouterGroupeOuPoint(&$noeud) {
$groupe = array();
if ($noeud['nbrePoints'] > 1 && isset($noeud['latMoyenne']) && isset($noeud['lngMoyenne'])) {
$groupe['lat'] = $noeud['latMoyenne']/$noeud['nbrePoints'];
$groupe['lng'] = $noeud['lngMoyenne']/$noeud['nbrePoints'];
$groupe['id'] = 'GROUPE:'.$groupe['lat'].';'.$groupe['lng'];
$groupe['nbreMarqueur'] = $noeud['nbrePoints'];
} else {
$groupe = $noeud['points'][0];
}
return $groupe;
}
 
 
private static function estUnParentFeuilles(&$noeud) {
return self::estUneFeuille($noeud['A']) &&
self::estUneFeuille($noeud['B']) &&
self::estUneFeuille($noeud['C']) &&
self::estUneFeuille($noeud['D']);
}
 
private static function estUneFeuille(&$noeud) {
return $noeud == null ||
(!isset($noeud['A']) || $noeud['A'] == null) &&
(!isset($noeud['B']) || $noeud['B'] == null) &&
(!isset($noeud['C']) || $noeud['C'] == null) &&
(!isset($noeud['D']) || $noeud['D'] == null);
}
}
?>
/trunk/jrest/bibliotheque/DecoupageNomLatin.class.php
New file
0,0 → 1,378
<?php
// Encodage : UTF-8
// +-------------------------------------------------------------------------------------------------------------------+
/**
* Découpage des noms latins
*
* Description : classe permettant de découper les noms latins.
*
//Auteur original :
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Tela-Botanica 1999-2009
* @licence GPL v3 & CeCILL v2
* @version $Id: DecoupageNomLatin.class.php 1873 2009-03-31 10:07:24Z Jean-Pascal MILCENT $
*/
// +-------------------------------------------------------------------------------------------------------------------+
class DecoupageNomLatin extends Decoupage {
 
private $expression_principale = array();
private $expression_complement = array();
 
function DecoupageNomLatin()
{
parent::__construct();
 
// Genre et nom supragénérique
$this->expression_principale[1] = "/^((?:$this->hyb |)$this->Gen)(?:( $this->Inf)|)$/u";
// Sp
$this->expression_principale[2] = "/^((?:$this->hyb |)$this->Gen) ((?:($this->hyb) |$this->Dou|)(?:$this->Epi|$this->Dou))(?:((?:,| $this->Ran) $this->Inf)| agg\.|)$/u";
// Rang infragénérique et supraspécifique
$this->expression_principale[3] = '/^('.$this->Gen.') ('.$this->Ran.') ('.$this->Gen.'|.'.$this->Epi.')(?:(, '.$this->Inf.')|)$/u';
// Hybride interspécifique
$this->expression_principale[4] = "/^((?:$this->Gen) $this->Epi (?:($this->Ran) $this->Epi )?x $this->Epi(?: ($this->Ran) $this->Epi)?)$/u";
// Aggrégat
$this->expression_principale[5] = "/^($this->Gen) ($this->Epi) (agg\.)(?:( $this->Inf)|)$/u";//
// Epithète infra-spécifique
$this->expression_complement[1] = "/^ ($this->Ran) ((?:($this->hyb) |$this->Dou|)(?:$this->Epi|$this->Dou))(?:((?:,| $this->Ran) $this->Inf)|)$/Uu";
// Cultivar
$this->expression_complement[5] = "/^ ($this->Ran_ht) ((?:(?:$this->Epi_cv) ?)+)$/u";
}
public function decouper($nom_latin)
{
$aso_nom_decompo = array( 'nom_genre' => '', 'nom_sp' => '', 'auteur_sp' => '', 'nom_complement' => '',
'type_infrasp' => '', 'nom_infrasp' => '',
'num_nomenc' => '', 'num_taxo' => '', 'rang_taxonomique' => '',
'nom_courant' => '', 'nom_superieur' => '', 'agg' => '');
$aso_nom_decompo['nom_complet'] = $nom_latin;
while ($nom_latin != '') {
$morceau = array();
if (preg_match($this->expression_principale[4], $nom_latin, $morceau)) {// Formule d'hybridation
// Nous tentons de déterminer le rang de l'hybride
if (isset($morceau[2]) && isset($morceau[3]) && $morceau[2] == $morceau[3]) {
$aso_nom_decompo['rang_taxonomique'] = $this->attribuerCodeRang('n-'.$morceau[2]);
} else {
$aso_nom_decompo['rang_taxonomique'] = 260;// Hybride instersp.
}
$aso_nom_decompo['mark_hybride_interspecifique'] = 'x';
$aso_nom_decompo['formule_hybridation'] = $morceau[0];
$nom_latin = '';
} else if (preg_match($this->expression_principale[5], $nom_latin, $morceau)) {// agg.
$aso_nom_decompo['rang_taxonomique'] = 240;// agg.
$aso_nom_decompo['nom_genre'] = $morceau[1];
$aso_nom_decompo['nom_sp'] = $morceau[2];
$aso_nom_decompo['agg'] = $morceau[3];
$nom_latin = $morceau[4];
$aso_nom_decompo['nom_superieur'] = $morceau[1];
$aso_nom_decompo['nom_courant'] = $morceau[2];
} else if (preg_match($this->expression_principale[2], $nom_latin, $morceau)) {// Nom d'sp.
// Nous regardons si nous avons à faire à un hybride
if (preg_match('/^'.$this->hyb.'$/', $morceau[3])) {
$aso_nom_decompo['rang_taxonomique'] = 260;// hybride intersp.
$aso_nom_decompo['mark_hybride_interspecifique'] = strtolower($morceau[3]);
} else if (preg_match('/^'.$this->Epi_nn_hy.'$/', $morceau[2])) {
$aso_nom_decompo['rang_taxonomique'] = 260;// hybride intersp.
$aso_nom_decompo['mark_hybride_interspecifique'] = 'x';
} else {
$aso_nom_decompo['rang_taxonomique'] = 250;// sp.
}
// Nous atribuons le genre
$aso_nom_decompo['nom_genre'] = $morceau[1];
// Nous regardons si nous avons à faire à une phrase non nommé (ex : sp.1, spp., nsp.)
if (preg_match('/^'.$this->Epi_nn.'$/', $morceau[2])) {
$aso_nom_decompo['phrase_nom_non_nomme'] = $morceau[2];// hybride intersp.
$aso_nom_decompo['nom_sp'] = '';
} else {
$aso_nom_decompo['nom_sp'] = $morceau[2];
}
$nom_latin = $morceau[4];
$aso_nom_decompo['nom_superieur'] = $morceau[1];
$aso_nom_decompo['nom_courant'] = $morceau[2];
} else if (preg_match($this->expression_principale[3], $nom_latin, $morceau)) {// Nom infragénérique et supraspécifique
$aso_nom_decompo['nom_genre'] = $morceau[1];
$aso_nom_decompo['rang_taxonomique'] = $this->attribuerCodeRang($morceau[2]);
// Nous regardons si nous avons à faire à un groupe
if (preg_match('/^'.$this->Ran_ig_gr.'$/', $morceau[2])) {
$aso_nom_decompo['nom_sp'] = $morceau[3];
} else {
$aso_nom_decompo['nom_infra_genre'] = $morceau[3];
}
$nom_latin = $morceau[4];
$aso_nom_decompo['nom_superieur'] = $morceau[1];
$aso_nom_decompo['nom_courant'] = $morceau[3];
} else if (preg_match($this->expression_principale[1], $nom_latin, $morceau)) {// Nom de genre et supragénérique
$aso_nom_decompo['rang_taxonomique'] = $this->verifierTerminaisonLatine($nom_latin);
$aso_nom_decompo['nom_suprasp'] = $morceau[1];
$nom_latin = $morceau[2];
$aso_nom_decompo['nom_superieur'] = null;
$aso_nom_decompo['nom_courant'] = $morceau[1];
} else if (preg_match($this->expression_complement[5], $nom_latin, $morceau)) {// Cultivar
$aso_nom_decompo['rang_cultivar'] = $this->attribuerCodeRang($morceau[1]);
// Nous vérifions si nous avons à faire à un cultivar d'hybride
if ($aso_nom_decompo['mark_hybride_interspecifique'] == 'x' && $aso_nom_decompo['rang_cultivar'] == 460) {
$aso_nom_decompo['rang_cultivar'] = 470;
}
$aso_nom_decompo['cultivar'] = $morceau[2];
$nom_latin = '';
} else if (preg_match($this->expression_complement[1], $nom_latin, $morceau)) {// Nom infrasp.
if (preg_match('/^'.$this->hyb.'$/', $morceau[3])) {
$aso_nom_decompo['mark_hybride_interspecifique'] = strtolower($morceau[3]);
}
$aso_nom_decompo['rang_taxonomique'] = $this->attribuerCodeRang($morceau[1]);
$aso_nom_decompo['type_infrasp'] = $morceau[1];
$aso_nom_decompo['nom_infrasp'] = $morceau[2];
$nom_latin = $morceau[4];
$aso_nom_decompo['nom_superieur'] = $aso_nom_decompo['nom_courant'];
$aso_nom_decompo['nom_courant'] = $morceau[2];
} else {// Erreurs
$aso_nom_decompo['erreur_mark'] = 'erreur';
$aso_nom_decompo['erreur_notes'] = $nom_latin;
$nom_latin = '';
}
}
return $aso_nom_decompo;
}
public function verifierTerminaisonLatine($nom_latin)
{
if (preg_match('/^Plantae$/', $nom_latin)) {// Règne
return 10;
} else if (preg_match('/phyta$/', $nom_latin)) {// Embranchement ou Division
return 30;
} else if (preg_match('/phytina$/', $nom_latin)) {// Sous-Embranchement ou Sous-Division
return 40;
} if (preg_match('/opsida$/', $nom_latin)) {// Classe
return 70;
} else if (preg_match('/idae$/', $nom_latin)) {// Sous-Classe
return 80;
} else if (preg_match('/ales$/', $nom_latin)) {// Ordre
return 100;
} else if (preg_match('/ineae$/', $nom_latin)) {// Sous-Ordre
return 110;
} else if (preg_match('/aceae$/', $nom_latin)) {// Famille
return 120;
} else if (preg_match('/oideae$/', $nom_latin)) {// Sous-Famille
return 130;
} else if (preg_match('/eae$/', $nom_latin)) {// Tribu
return 140;
} else if (preg_match('/inae$/', $nom_latin)) {// Sous-Tribu
return 150;
} else if (preg_match('/^[A-Z]/', $nom_latin)) {// Genre
return 160;
} else {
return 1;
}
}
static function fournirTableauAbreviationRang($type = 'tout')
{
$rang_supra_sp = array('subgen.', 'subg.', 'sect.');// l'abréviation du rang est suivi par un nom supra spécifique commençant par une majuscule
$rang_supra_gr = array('gr.');// l'abréviation du rang est suivi par un nom ne commençant pas par une majuscule
$rang_supra_agg = array('agg.');// le nom latin est terminé par l'abréviation du rang
$rang_infra_sp = array( 'subsp.', 'n-subsp.', '[subsp.]', '[n-subsp.]',
'var.', 'nvar.', '[var.]',
'prol.', 'proles', 'n-proles.',
'f.', 'fa', 'fa.', 'forma',
'subvar.', 'convar.',
'cv.', 'Cv.',
'n-f.', 'n-fa', 'n-fa.',
'subf.', 'subfa', 'subfa.');
if ($type == 'supra') {
return $rang_supra_sp;
} else if ($type == 'supra-gr') {
return $rang_supra_gr;
} else if ($type == 'supra-agg') {
return $rang_supra_agg;
} else if ($type == 'infra') {
return $rang_infra_sp;
} else if ($type == 'tout') {
return array_merge($rang_supra_sp, $rang_supra_gr, $rang_supra_agg, $rang_infra_sp);
}
}
 
static function actualiserCodeRang($code_rang)
{
$aso_rang = array( '1' => '10', // Règne
'3' => '20', // Sous-Règne
'5' => '30', // Phylum
'7' => '40', // Sub-Phylum
'9' => '50', // division
'15' => '60', // sous-division
'20' => '70', // classe
'25' => '80', // sous-classe
'30' => '100', // ordre
'35' => '110', // sous-ordre
'40' => '120', // famille
'45' => '130', // sous-famille
'50' => '140', // tribu
'55' => '150', // sous-tribu
'60' => '160', // genre
'62' => '170', // genre hybride (nouveau compatibilité flore Réunion)
'65' => '180', // sous-genre
'65' => '190', // section
'75' => '200', // sous-section
'80' => '210', // série
'85' => '220', // sous-série
'90' => '230', // groupe
'95' => '240', // aggrégat
'100' => '250', // espèce
'102' => '260', // espèce hybride intragénérique
'104' => '260', // espèce hybride intergénérique
'110' => '280', // sous-espèce
'112' => '300', // sous-espèce hybride : hybride entre deux sous-espèces d'une espèce non hybride ; exemple : Polypodium vulgare L. nsubsp. mantoniae (Rothm.) Schidlay (Polypodium vulgare L. subsp. vulgare x Polypodium vulgare L. subsp. prionodes (Aschers.) Rothm.).
'113' => '310', // sous-espèce hybride : sous-espèce d'espèce hybride sans spécification du rang parental (subspecies) (voir ICBN, art. H.12.1).
'114' => '300', // sous-espèce hybride : sous-espèce hybride d'espèce hybride (nothosubspecies) (voir ICBN, art. H.12.1) ; exemple : Mentha x piperita L. nsubsp. piperita (Mentha aquatica L. x Mentha spicata L. subsp. glabrata (Lej. et Court.) Lebeau).
'115' => '300', // sous-espèce hybride
'120' => '1', // infra2
'122' => '330', // prole, race : peu employé souvent issu de nom ancien (antérieur au code).
'124' => '340', // prole, race hybride : peu employé souvent issu de nom ancien (antérieur au code).
'132' => '350', // convarietas : si on le conscidère comme un rang intermédiaire entre la sous-espèce et la variété. Voir aussi n°200.
'130' => '1', // infra3 : niveau infra-spécifique de troisième niveau, sans plus de précision.
'140' => '360', // variété
'142' => '380', // variété : hybride entre deux variétés d'une espèce non hybride.
'143' => '390', // variété : variété d'espèce hybride sans spécification du rang parental (varietas) (voir ICBN, art. H.12.1); exemple : Populus x canadensis Moench var. marilandica (Poir.) Rehder.
'144' => '380', // variété : variété hybride d'espèce hybride (nothovarietas) ; exemple : Salix x sepulcralis Simonk. nvar. chrysocoma (Dode) Meikle.
'145' => '380', // variété hybride
'150' => '410', // sous-variété
'160' => '420', // forme
'162' => '430', // forme : hybride entre deux formes d'une espèce non hybride.
'163' => '430', // forme : forme d'espèce hybride sans spécification du rang parental (forma) (voir ICBN, art. H.12.1); exemple : Mentha x piperita L. f. hirsuta Sole.
'164' => '430', // forme : forme hybride d'espèce hybride (nothoforma).
'170' => '440', // sous-forme
'200' => '450', // groupe de cultivar
'210' => '460', // cultivar
'220' => '470', // cultivar d'hybride
'0' => '480' // clade
);
return $aso_rang[$code_rang];
}
 
public function attribuerCodeInfra($str_abreviation_type_infra)
{
$aso_code_infra = array('type' => '', 'code' => 0, 'rang' => 2 );
switch ($str_abreviation_type_infra) {
case 'subgen.' :
case 'subg.' :
$aso_code_infra['rang'] = 180;
break;
case 'sect.' :
$aso_code_infra['rang'] = 190;
break;
case 'gr.' :
$aso_code_infra['rang'] = 230;
break;
case 'subsp.' :
$aso_code_infra['type'] = 'infra1';
$aso_code_infra['code'] = 1;
$aso_code_infra['rang'] = 280;
break;
case 'n-subsp.' :
$aso_code_infra['type'] = 'infra1';
$aso_code_infra['code'] = 2;
$aso_code_infra['rang'] = 300;
break;
case '[subsp.]' :
$aso_code_infra['type'] = 'infra1';
$aso_code_infra['code'] = 3;
$aso_code_infra['rang'] = 290;
break;
case '[n-subsp.]' :
$aso_code_infra['type'] = 'infra1';
$aso_code_infra['code'] = 4;
$aso_code_infra['rang'] = 310;
break;
case 'var.' :
$aso_code_infra['type'] = 'infra2';
$aso_code_infra['code'] = 1;
$aso_code_infra['rang'] = 360;
break;
case '[var.]' :
$aso_code_infra['type'] = 'infra2';
$aso_code_infra['code'] = 2;
$aso_code_infra['rang'] = 370;
break;
case 'n-var.' :
$aso_code_infra['type'] = 'infra2';
$aso_code_infra['code'] = 3;
$aso_code_infra['rang'] = 380;
break;
case 'nvar.' :
$aso_code_infra['type'] = 'infra2';
$aso_code_infra['code'] = 3;
$aso_code_infra['rang'] = 384;
break;
case '[n-var.]' :
$aso_code_infra['type'] = 'infra2';
$aso_code_infra['code'] = 5;
$aso_code_infra['rang'] = 390;
break;
case 'prol.' :
case 'proles' :
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 2;
$aso_code_infra['rang'] = 330;
break;
case 'n-proles' :
case 'n-proles.' :
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 1;
$aso_code_infra['rang'] = 340;
break;
case 'f.':
case 'fa':
case 'fa.':
case 'forma':
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 3;
$aso_code_infra['rang'] = 420;
break;
case 'subvar.' :
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 4;
$aso_code_infra['rang'] = 410;
break;
case 'convar.' :
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 5;
$aso_code_infra['rang'] = 350;
break;
case 'cv.':
case 'Cv.':
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 6;
$aso_code_infra['rang'] = 460;
break;
case 'n-f.':
case 'n-fa':
case 'n-fa.':
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 7;
$aso_code_infra['rang'] = 430;
break;
case 'subf.':
case 'subfa':
case 'subfa.':
$aso_code_infra['type'] = 'infra3';
$aso_code_infra['code'] = 8;
$aso_code_infra['rang'] = 440;
break;
default:
$aso_code_infra['erreur_mark'] = 'erreur';
$aso_code_infra['erreur_notes'] = $str_abreviation_type_infra;
$aso_code_infra['rang'] = 2;
}
return $aso_code_infra;
}
public function attribuerCodeRang($str_abreviation_type_infra)
{
$aso_code_infra = $this->attribuerCodeInfra($str_abreviation_type_infra);
return $aso_code_infra['rang'];
}
}
?>
/trunk/jrest/bibliotheque/Cel.php
New file
0,0 → 1,709
<?php
/**
* Classe mère abstraite contenant les méthodes génériques des services.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright © 2006-2014 Tela Botanica
*/
require_once('Bdd2.php');
 
abstract class Cel {
const ARRET_SERVICE = false;
 
const TYPE_OBS = 'observation';
const TYPE_IMG = 'image';
 
// TODO: delete wrappers
const SQL_MODE_ASSOC = Bdd2::SQL_MODE_ASSOC;
const SQL_MODE_OBJET = Bdd2::SQL_MODE_OBJET;
const SQL_RETOUR_COMPLET = Bdd2::SQL_RETOUR_COMPLET;
const SQL_RETOUR_LIGNE = Bdd2::SQL_RETOUR_LIGNE;
const SQL_RETOUR_COLONNE = Bdd2::SQL_RETOUR_COLONNE;
const SQL_RETOUR_BRUT = Bdd2::SQL_RETOUR_BRUT;
 
public $config;
private $ressources;
protected $parametres = array();
 
public static $bdd = null;
 
protected $messages = array();
protected $debug = array();
protected $start;
protected $limit;
 
static $referentiels_valides = array('bdtfx', 'bdtxa', 'isfan', 'apd');
static $default_referentiel = 'bdtfx';
static $fallback_referentiel = 'autre';
 
public function __construct($config) {
@session_start();
// Tableau contenant la config de Jrest
$this->config = $config;
 
// Réglages de PHP
setlocale(LC_ALL, $this->config['settings']['locale']);
date_default_timezone_set($this->config['settings']['fuseauHoraire']);
 
// Connection à la base de données
if (self::$bdd === null) { // singleton à l'arrache
self::$bdd = new Bdd2($this->config, 'database_cel');
}
 
// Nettoyage du _GET (sécurité)
$this->collecterParametres();// Récupération de tous les parametres de _GET, nettoyage et mise dans $this->parametres
$this->recupererParametresUrl();// Vidage de _GET et création d'attribut de la classe
$this->definirParametresUrlParDefaut();
 
// Définition de variable générale dans la config
$this->config['settings']['baseURLAbsoluDyn'] = 'http://'.$_SERVER['SERVER_NAME'].$this->config['settings']['baseURL'].'%s';
}
 
//+----------------------------------------------------------------------------------------------------------------+
protected function connecterPDO($config, $base = 'database_cel') {
return new Bdd2($config, $base);
}
 
public static function db() {
if (! self::$bdd) {
die('ERREUR: aucune base de données de disponible.');
}
return self::$bdd;
}
 
// TODO: delete wrappers, en attendant que $this->bdd soit remplacé par Cel::db() partout.
public function __get($prop) {
$retour = $this->$prop;
if ($prop == 'bdd') {
$retour = self::$bdd;
}
return $retour;
}
 
protected function protegerTableau(Array $tableau) {
foreach ($tableau as $id => $val) {
$tableau[$id] = Cel::db()->proteger($val);
}
return $tableau;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// TRAITEMENT des URLs et des PARAMÊTRES
 
private function collecterParametres() {
foreach ($_GET as $cle => $valeur) {
$p = $this->verifierSecuriteParametreUrl($valeur);
$this->parametres[$cle] = is_string($p) ? rawurldecode($p) : $p;
}
}
 
private function recupererParametresUrl() {
$get_params = array('orderby', 'distinct', 'start', 'limit', 'formatRetour');
foreach ($get_params as $get) {
if (!isset($_GET[$get])) continue;
$_GET[$get] = $this->verifierSecuriteParametreUrl($_GET[$get]);
 
if (empty($_GET[$get])) {
$_GET[$get] = null;
continue;
}
 
if (!isset($this->$get)) {
$this->$get = $_GET[$get];
} else {
$e = "Impossible d'ajouter l'attribut $get à la classe du service car elle possède déjà un attribut nommé : $get";
trigger_error($e, E_USER_WARNING);
}
}
}
 
 
protected function verifierSecuriteParametreUrl($param) {
//$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';');
return is_string($param) ? strip_tags($param) : $param;
}
 
private function definirParametresUrlParDefaut() {
if (!isset($this->start)) {
$this->start = 0;
}
if (!isset($this->limit)) {
$this->limit = 150;
}
}
 
protected function traiterParametres($params_attendu, $params, $pourBDD = true) {
$sortie = array();
foreach ($params_attendu as $num => $nom) {
if (isset($params[$num]) && $params[$num] != '*') {
if ($pourBDD) {
$params[$num] = self::$bdd->quote($params[$num]);
}
$sortie[$nom] = $params[$num];
}
}
return $sortie;
}
 
protected function traiterNomMethodeGet($nom) {
$methode = 'get';
$methode .= str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($nom))));
return $methode;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// GESTION de l'ENVOI au NAVIGATEUR
 
protected function envoyerMessageErreur($code, $msg) {
http_response_code($code);
header("Content-Type: text/plain; charset=utf-8");
die($msg);
}
 
protected function envoyerJson($donnees, $encodage = 'utf-8') {
$encodage_json = true;
$this->envoyer($donnees, 'application/json', $encodage, $encodage_json);
}
 
protected function envoyerJsonVar($variable, $donnees = null, $encodage = 'utf-8') {
$contenu = "var $variable = ".json_encode($donnees);
$this->envoyer($contenu, 'text/html', $encodage);
}
 
protected function envoyerJsonp($donnees = null, $encodage = 'utf-8') {
$contenu = $this->parametres['callback'].'('.json_encode($donnees).');';
$this->envoyer($contenu, 'text/html', $encodage);
}
 
protected function envoyer($donnees = null, $mime = 'text/html', $encodage = 'utf-8', $json = false) {
// Traitements des messages d'erreurs et données
$sortie = $donnees;
if (count($this->messages) != 0) {
$code_http = 500; // Internal Server Error
$mime = 'application/json';
$json = true;
$donnees->cause = $this->messages;
} else {
$code_http = 200; // OK
if (is_null($donnees)) {
$sortie = 'OK';
}
}
 
// Gestion de l'envoie du déboguage
$this->envoyerDebogage();
 
// Encodage au format et JSON et envoie sur la sortie standard
$contenu = $json ? json_encode($sortie) : $sortie;
 
$this->envoyerContenu($encodage, $mime, $contenu, $code_http);
}
 
private function envoyerDebogage() {
if (!is_array($this->debug)) {
$this->debug[] = $this->debug;
}
if (count($this->debug) != 0) {
foreach ($this->debug as $cle => $val) {
if (is_array($val)) {
$this->debug[$cle] = print_r($val, true);
}
}
header('X-DebugJrest-Data:'.json_encode($this->debug));
}
}
 
private function envoyerContenu($encodage, $mime, $contenu, $code_http=200) {
if (!is_null($mime) && !is_null($encodage)) {
header("Content-Type: $mime; charset=$encodage");
} else if (!is_null($mime) && is_null($encodage)) {
header("Content-Type: $mime");
}
http_response_code($code_http);
print $contenu;
}
 
static function envoyerAuth($message_accueil, $message_echec) {
http_response_code(401);// Unauthorized
header('WWW-Authenticate: Basic realm="'.mb_convert_encoding($message_accueil, 'ISO-8859-1', 'UTF-8').'"');
header('Content-type: text/plain; charset=UTF-8');
print $message_echec;
exit;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// GESTION DES CLASSES CHARGÉES À LA DEMANDE
 
protected function getRestClient() {
if (!isset($this->restClient)) {
$this->restClient = new CelRestClient();
}
return $this->restClient;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// GESTION DE L'IDENTIFICATION
 
static function getAuthIdentifiant() {
return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
}
 
static function getAuthMotDePasse() {
return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
}
 
public function authentifierAdmin() {
$message_accueil = "Veuillez vous identifier avec votre compte Tela Botanica.";
$message_echec = "Accès limité aux administrateurs du CEL.\n".
"Votre tentative d'identification a échoué.\n".
"Actualiser la page pour essayer à nouveau si vous êtes bien inscrit comme administrateur.";
return $this->authentifier($message_accueil, $message_echec, 'Admin');
}
 
public function authentifierUtilisateur() {
$message_accueil = "Veuillez vous identifier avec votre compte Tela Botanica.";
$message_echec = "Accès limité aux utilisateur du CEL.\n".
"Inscrivez vous http://www.tela-botanica.org/page:inscription pour le devenir.\n".
"Votre tentative d'identification a échoué.\n".
"Actualiser la page pour essayer à nouveau si vous êtes déjà inscrit ou contacter 'accueil@tela-botanica.org'.";
return $this->authentifier($message_accueil, $message_echec, 'Utilisateur');
}
 
public function controleUtilisateur($id) {
if (@array_key_exists('id_utilisateur', $_SESSION['user']) && empty($_SESSION['user']['id_utilisateur'])) {
//cas de la session temporaire, on ne fait rien de particulier
} else {
if (isset($_SESSION['user']) && isset($_SESSION['user']['id_utilisateur']) && !$this->etreAdminCelParId($_SESSION['user']['id_utilisateur']) && $_SESSION['user']['id_utilisateur'] != $id) {
// cas d'usurpation d'identité
print 'Accès interdit';
exit();
}
}
}
 
public function controleAppelIpAutorisee() {
$ipsAutorisees = explode(',', $this->config['jrest_admin']['ip_autorisees']);
$remoteIp = filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP);
$serverIp = filter_input(INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP);
if (in_array($remoteIp, $ipsAutorisees) == false) {
if ($remoteIp != $serverIp) {// ATTENTION : maintenir ce test à l'intérieur du précédent
$msg = "Accès interdit. \n".
"Vous n'êtes pas autorisé à accéder à ce service depuis '$remoteIp' !\n";
$this->envoyerMessageErreur(401, $msg);
}
}
return true;
}
 
public function logger($index,$chaine = 'err') {
if (!class_exists('Log')) {
Log::getInstance();
}
Log::setCheminLog($this->config['log']['cheminlog']);
Log::setTimeZone($this->config['log']['timezone']);
Log::setTailleMax($this->config['log']['taillemax']);
Log::ajouterEntree($index,$chaine);
}
 
private function authentifier($message_accueil, $message_echec, $type) {
if (!isset($_SERVER['PHP_AUTH_USER'])) {
self::envoyerAuth($message_accueil, $message_echec); // exit
}
 
if ($type == 'Utilisateur' && self::getAuthMotDePasse() == 'debug') {
$autorisation = true;
} else {
$methodeAutorisation = "etre{$type}Autorise";
$autorisation = $this->$methodeAutorisation();
}
if ($autorisation == false) {
self::envoyerAuth($message_accueil, $message_echec);
}
 
return true;
}
 
public function etreUtilisateurAutorise() {
$identifiant = self::getAuthIdentifiant();
$mdp = md5(self::getAuthMotDePasse());
$service = "TestLoginMdp/$identifiant/$mdp";
$url = sprintf($this->config['settings']['baseURLServicesAnnuaireTpl'], $service);
$json = $this->getRestClient()->consulter($url);
$existe = json_decode($json);
 
$autorisation = (isset($existe) && $existe) ? true :false;
return $autorisation;
}
 
public function etreAdminAutorise() {
$identifiant = self::getAuthIdentifiant();
$autorisation = ($this->etreUtilisateurAutorise() && $this->etreAdminCel($identifiant)) ? true : false;
return $autorisation;
}
 
public function etreAdminCelParId($id) {
// si l'utilisateur s'est déjà identifié, alors les infos
// sur le fait qu'il est admin ou non sont déjà disponibles
if(isset($_SESSION['user']) && isset($_SESSION['user']['admin']) && $_SESSION['user']['id_utilisateur'] == $id) {
$admin = $_SESSION['user']['admin'];
} else {
$requete = "SELECT admin FROM cel_utilisateurs WHERE id_utilisateur = ".Cel::db()->proteger($id);
$resultat = Cel::db()->requeter($requete);
 
$admin = false;
if ($resultat && count($resultat) > 0) {
$admin = ($resultat[0]['admin'] == 1);
}
}
return $admin;
}
 
public function etreAdminCel($courriel) {
// si l'utilisateur s'est déjà identifié, alors les infos
// sur le fait qu'il est admin ou non sont déjà disponibles
if(isset($_SESSION['user']) && isset($_SESSION['user']['admin']) && $_SESSION['user']['courriel'] == $courriel) {
$admin = $_SESSION['user']['admin'];
} else {
$requete = "SELECT admin FROM cel_utilisateurs WHERE courriel = ".Cel::db()->proteger($courriel);
$resultat = Cel::db()->requeter($requete);
 
$admin = false;
if ($resultat && count($resultat) > 0) {
$admin = ($resultat[0]['admin'] == 1);
}
}
return $admin;
}
 
public function getInfosComplementairesUtilisateur($id_utilisateur) {
$infos = array('prenom' => '', 'nom' => '', 'courriel' => '');
if (is_numeric($id_utilisateur)) {
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$requete = 'SELECT prenom, nom, courriel '.
'FROM cel_utilisateurs '.
"WHERE id_utilisateur = $idUtilisateurP ".
' -- ' . __FILE__ . ':' . __LINE__;
$resultat = Cel::db()->requeter($requete);
 
if ($resultat && count($resultat)) {
$infos = $resultat[0];
}
}
return $infos;
}
 
public function getInfosComplementairesUtilisateurPourMail($mail_utilisateur) {
$infos = array('prenom' => '', 'nom' => '', 'courriel' => $mail_utilisateur);
 
$mailUtilisateurP = Cel::db()->proteger($mail_utilisateur);
$requete = 'SELECT id_utilisateur as id, prenom, nom '.
'FROM cel_utilisateurs '.
"WHERE courriel = $mailUtilisateurP ".
' -- ' . __FILE__ . ':' . __LINE__;
 
$resultat = Cel::db()->requeter($requete);
 
if ($resultat && is_array($resultat) && count($resultat) > 0) {
$infos = $resultat;
}
 
return $infos;
}
 
protected function controlerAccessibiliteWs() {
if (self::ARRET_SERVICE) {
$msg = "Les services du CEL sont temporairement désactivées.";
$this->envoyerMessageErreur(503, $msg);
}
return true;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// GESTION DE MÉTHODES COMMUNES ENTRE LES SERVICES
 
protected function denullifierTableauValeurCel(&$tableau) {
foreach ($tableau as $k => $v) {
if (($v == 'null') || ($v == '000null')) {
$row[$k] = '';
} else {
$row[$k] = utf8_decode($v);
}
}
return $tableau;
}
 
protected function getUrlImage($id, $format = 'L') {
$url_tpl = $this->config['settings']['celImgUrlTpl'];
$id = sprintf('%09s', $id).$format;
$url = sprintf($url_tpl, $id);
return $url;
}
 
/**
* Prend en paramêtre un tableau de courriels et retourne après avoir interrogé un service de l'annuaire
* une tableau avec en clé le courriel et en valeur l'intitulé de la personne à afficher.
*
* @param array $courriels un tableau de courriels pour lesquels il faut rechercher les infos d'identité
*/
protected function creerAuteurs(Array $courriels) {
$auteurs = array();
$identites = $this->recupererUtilisateursIdentite($courriels);
if ($identites) {
foreach ($identites as $courriel => $infos) {
$auteurs[$courriel] = $infos['intitule'];
}
}
return $auteurs;
}
 
protected function recupererUtilisateursIdentite(Array $courriels) {
// Récupération des données au format Json
$service = "utilisateur/identite-par-courriel/".implode(',', $courriels);
$url = sprintf($this->config['settings']['baseURLServicesAnnuaireTpl'], $service);
$json = file_get_contents($url);
$utilisateurs = json_decode($json, true);
$noms = array();
foreach ($courriels as $courriel) {
$courriel = strtolower($courriel);
$info = array('id' => null, 'intitule' => '');
if (isset($utilisateurs[$courriel])) {
$info['intitule'] = $utilisateurs[$courriel]['intitule'];
$info['id'] = $utilisateurs[$courriel]['id'];
} else {
$info['intitule'] = $this->tronquerCourriel($courriel);
}
$noms[$courriel] = $info;
}
return $noms;
}
 
protected function tronquerCourriel($courriel) {
return preg_replace('/[^@]+$/i', '...', $courriel);
}
 
protected function nettoyerTableau(Array $tableau) {
foreach ($tableau as $cle => $valeur) {
if (is_array($valeur)) {
$valeur = $this->nettoyerTableau($valeur);
} else {
$valeur = $this->nettoyerTexte($valeur);
}
$tableau[$cle] = $valeur;
}
return $tableau;
}
 
/**
* Fonction nettoyant les caractères spéciaux (&,<) et les valeurs nulles du CEL dans un texte comprenant du HTML.
*/
protected function nettoyerTexte($txt) {
$txt = preg_replace('/&(?!([a-z]+|#[0-9]+|#x[0-9][a-f]+);)/i', '&amp;', $txt);
// TODO : trouver une regexp qui permet de remplacer les symboles < et > isolés
//$txt = preg_replace('/<(?!([a-z][a-z0-9]*)\b[^>]*>(.*?)<\/\1>|\/\s*([a-z][a-z0-9]*)\s*>)/i', '&lt;', $txt);
//$txt = preg_replace('/(?!<([a-z][a-z0-9]*)\b[^>]*)>(?!(.*?)<\/\1>)/i', '&gt;', $txt);
$txt = preg_replace('/(?:000null|null)/i', '', $txt);
return $txt;
}
 
/**
* Fonction nettoyant les caractères spéciaux HTML pour les champs de saisie libre du CEL.
*/
static function protegerCaracteresHtmlDansChamps($donnees) {
$champs = array('mots_cles_texte', 'commentaire',
'zone_geo', 'lieudit', 'station', 'milieu', 'commentaire', 'nom_sel');
foreach ($champs as $champ) {
if (isset($donnees[$champ])) {
$donnees[$champ] = htmlspecialchars($donnees[$champ]);
}
}
return $donnees;
}
 
protected function convertirDateHeureMysqlEnTimestamp($date_heure_mysql){
$val = explode(' ', $date_heure_mysql);
$date = explode('-', $val[0]);
$heure = explode(':', $val[1]);
return mktime((int) $heure[0], (int) $heure[1], (int) $heure[2], (int) $date[1], (int) $date[2], (int) $date[0]);
}
 
protected function etreNull($valeur) {
$etre_null = false;
if ($valeur == '' || $valeur == null || $valeur == '000null' || $valeur == 'null' || $valeur == '*') {
$etre_null = true;
}
return $etre_null;
}
 
protected function formaterDate($date_heure_mysql, $format = '%A %d %B %Y à %H:%M') {
$date_formatee = '';
if (!$this->etreNull($date_heure_mysql)) {
$timestamp = $this->convertirDateHeureMysqlEnTimestamp($date_heure_mysql);
$date_formatee = strftime($format, $timestamp);
}
return $date_formatee;
}
 
protected function convertirCodeZoneGeoVersDepartement($code_zone_geo) {
$code_departement = '';
if ($this->estUnCodeInseeDepartement($code_zone_geo)) {
$code_departement = substr(ltrim($code_zone_geo,'INSEE-C:'),0,2);
}
return $code_departement;
}
 
protected function estUnCodeInseeDepartement($code_a_tester) {
return preg_match('/^INSEE-C:[0-9]{5}/',$code_a_tester);
}
 
protected function convertirCodeZoneGeoVersCodeInsee($code_zone_geo) {
$code_departement = '';
if ($this->estUnCodeInseeDepartement($code_zone_geo)) {
$code_departement = ltrim($code_zone_geo,'INSEE-C:');
}
return $code_departement;
}
 
static function obtenirCodeInseeCommunePourNomEtDepartement($nom_commune, $code_insee) {
$nomCommuneP = Cel::db()->proteger($nom_commune);
$codeInseeP = Cel::db()->proteger("INSEE-C:$code_insee%");
$requete = 'SELECT id_zone_geo '.
'FROM cel_zones_geo '.
"WHERE nom LIKE $nomCommuneP AND id_zone_geo LIKE $codeInseeP ".
' -- ' . __FILE__ . ':' . __LINE__;
$resultat = Cel::db()->requeter($requete);
 
$infos = $code_insee; // Par défaut retourne l'original
if ($resultat && count($resultat)) {
$infos = $resultat[0]['id_zone_geo'];
}
return $infos;
}
 
protected function encoderMotCle($mot_cle) {
return md5(mb_strtolower(trim($mot_cle)));
}
 
private function protegerMotsCles($mots_cles, $type) {
$separateur = ($type == self::TYPE_IMG) ? ',' : ';' ;
$mots_cles = $this->traiterValeursMultiples($mots_cles, $separateur);
return $mots_cles;
}
 
protected function traiterValeursMultiples($valeurs, $separateur_entree = ',' , $separateur_sortie = ',') {
if (! $this->etreNull($valeurs)) {
$valeurs_a_proteger = explode($separateur_entree,trim(trim($valeurs), $separateur_entree));
foreach ($valeurs_a_proteger as $valeur) {
$valeurs_protegees[] = self::$bdd->quote($valeur);
}
$valeurs = implode($separateur_sortie, $valeurs_protegees);
}
return ($this->etreNull($valeurs)) ? null : $valeurs;
}
 
protected function obtenirSousTaxonsPourNt($referentiel, $nt) {
return $this->obtenirSousTaxons($referentiel, 'nt:'.$nt);
}
 
protected function obtenirSousTaxonsPourNn($referentiel, $nn) {
return $this->obtenirSousTaxons($referentiel, $nn);
}
 
private function obtenirSousTaxons($referentiel, $requete) {
$sous_taxons = array();
$url_service_taxon = str_replace('{referentiel}', $referentiel, $this->config['eflore']['url_service_taxon']);
$url = $url_service_taxon.'/'.$requete.'/relations/hierarchie';
$res = json_decode(file_get_contents($url),true);
if($res && !empty($res)) {
$sous_taxons = array_pop($res);
}
return $sous_taxons;
}
 
protected function supprimerVersionDuReferentiel($referentiel) {
$referentiel_parties = explode(':', $referentiel);
$referentiel_parties = explode('_', $referentiel_parties[0]);
return $referentiel_parties[0];
}
 
protected function getUrlEflore($referentiel, $nn) {
$urlEflore = null;
if (! $this->etreNull($nn)) {
$code_referentiel = $this->supprimerVersionDuReferentiel($referentiel);
if($code_referentiel == '') {
$code_referentiel = 'bdtfx';
}
$urlEflore = sprintf($this->config['settings']['efloreUrlTpl'], $code_referentiel, $nn, 'illustration');
}
return $urlEflore;
}
 
protected function nePasInterpreterXml($txt) {
return '<![CDATA['.$txt.']]>';
}
 
//+----------------------------------------------------------------------------------------------------------------+
// GESTION DES SQUELETTES PHP
 
/**
* Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données,
* en extrait les variables, charge le squelette et retourne le résultat des deux combinés.
*
* @param String $fichier le chemin du fichier du squelette
* @param Array $donnees un tableau associatif contenant les variables a injecter dans le squelette.
*
* @return boolean false si le squelette n'existe pas, sinon la chaine résultat.
*/
public static function traiterSquelettePhp($fichier, Array $donnees = array()) {
$sortie = false;
if (file_exists($fichier)) {
// Extraction des variables du tableau de données
extract($donnees);
// Démarage de la bufferisation de sortie
ob_start();
// Si les tags courts sont activés
if ((bool) @ini_get('short_open_tag') === true) {
// Simple inclusion du squelette
include $fichier;
} else {
// Sinon, remplacement des tags courts par la syntaxe classique avec echo
$html_et_code_php = self::traiterTagsCourts($fichier);
// Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval
$html_et_code_php = '?>'.$html_et_code_php;
// Interprétation du html et du php dans le buffer
echo eval($html_et_code_php);
}
// Récupèration du contenu du buffer
$sortie = ob_get_contents();
// Suppression du buffer
@ob_end_clean();
} else {
$msg = "Le fichier du squelette '$fichier' n'existe pas.";
trigger_error($msg, E_USER_WARNING);
}
// Retourne le contenu
return $sortie;
}
 
/**
* Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo.
*
* @param String $chemin_squelette le chemin du fichier du squelette
*
* @return string le contenu du fichier du squelette php avec les tags courts remplacés.
*/
private static function traiterTagsCourts($chemin_squelette) {
$contenu = file_get_contents($chemin_squelette);
// Remplacement de tags courts par un tag long avec echo
$contenu = str_replace('<?=', '<?php echo ', $contenu);
// Ajout systématique d'un point virgule avant la fermeture php
$contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu);
return $contenu;
}
}
?>
/trunk/jrest/bibliotheque/FormateurGroupeColonne.php
New file
0,0 → 1,857
<?php
 
/**
* @category PHP
* @package jrest
* @author Raphaël Droz <raphael@tela-botania.org>
* @copyright 2013 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
*/
define('SEPARATEUR_IMAGES', " / ");
define('PREFIX_CHAMPS_ETENDUS', "ext:");
// utilisé par formaterUrlUser() [ nécessaire pour le widget d'export)
define('USER_BASEURL', 'http://www.tela-botanica.org/profil:%d');
 
Class FormateurGroupeColonne {
 
// cache pour les données des fonctions
static $cache = Array();
 
// test sur la table cel_references, mis à TRUE si la table existe
static $is_table = false;
 
// les groupes de champs utilisables
static $fieldGroups = array(
'standard',
'avance',
'etendu',
'baseflor',
'auteur'
);
 
// les données baseflor à récupérer: colonnes présentes dans cel_references
// et intitulés associés
static $baseflor_col = array(
"ve_lumiere" => "Lumière",
"ve_temperature" => "Température",
"ve_continentalite" => "Continentalité",
"ve_humidite_atmos" => "Humidité Atmosphérique",
"ve_humidite_edaph" => "Humidité",
"ve_reaction_sol" => "Réaction (pH)",
"ve_nutriments_sol" => "Nutriments",
"ve_salinite" => "Salinité",
"ve_texture_sol" => "Texture" ,
"ve_mat_org_sol" => "Matière Organique",
"catminat_code" => "Code Catminat",
"syntaxon" => "Syntaxon",
);
 
// TODO: dirty, ordre des champs étendus... souhaité pour florilèges:
static $ordre_champ_etendus_Florileges = array(
"personneStructure",
"personneService",
"personneFonction",
"adresse",
"latitudeDebutRue",
"longitudeDebutRue",
"latitudeFinRue",
"longitudeFinRue",
"typoUrbaine",
"revetementSol",
"presenceZoneVegetalise",
"hauteurBatimentAvoisinant",
"intensiteGestion",
"periodiciteTraitementPhyto",
"dateArretTraitementPhyto",
"itineraireGestion",
"dateDerniereIntervention",
"hauteurPlante",
"resistanceTraitementPhyto",
"vitesseCroissance",
"perceptionTechnicien",
"perceptionRiverainMauvaise",
);
 
static function colGroupsValidation($groupe_de_champs = 'standard,avance') {
if(! $groupe_de_champs) return FALSE;
if(is_string($groupe_de_champs)) {
$groupe_de_champs = array_flip(explode(',', $groupe_de_champs));
}
elseif(is_array($groupe_de_champs)) {
$groupe_de_champs = array_flip($groupe_de_champs);
}
else {
return NULL;
}
$groupe_de_champs = array_intersect_key(array_flip(self::$fieldGroups),
$groupe_de_champs);
if(!$groupe_de_champs) return FALSE;
// toujours ajouter standard
$groupe_de_champs['standard'] = TRUE;
return implode(',', array_keys($groupe_de_champs));
}
 
/*
* @param $fieldSets: un liste de noms de colonnes ou de sets de colonnes
* séparés par des virgules
* eg: "espece" ou "champs-etendus", ...
*
* @return: un tableau associatif déjà ordonné
* clé: abbrev [machine-name] de la colonne (eg: "espece" ou "mot-clef")
* valeur: des données relative à cette colonne, cf GenColInfo
*
* Si la colonne n'utilise pas de fonction de récupération particulière
* (ie: si le champ exportés [ou importé] correspond exactement au champ dans la base de donnée)
* Alors 'abbrev' doit avoir la même valeur que le nom de la colonne dans la table mysql `cel_obs`.
*
*/
static function nomEnsembleVersListeColonnes($groupe_de_champs = 'standard') {
if(! $groupe_de_champs) $groupe_de_champs = 'standard';
if(is_string($groupe_de_champs)) {
$groupe_de_champs = array_flip(explode(',', $groupe_de_champs));
}
elseif(is_array($groupe_de_champs)) {
$groupe_de_champs = array_flip($groupe_de_champs);
}
else {
return NULL;
}
$groupe_de_champs = array_intersect_key(array_flip(self::$fieldGroups),
$groupe_de_champs);
if(!$groupe_de_champs) return NULL;
 
$colonnes = Array();
 
if(isset($groupe_de_champs['standard'])) {
$colonnes += Array(
'nom_sel' => self::GenColInfo(Array('abbrev' => 'nom_sel',
'nom' => 'Espèce')),
'nom_sel_nn' => self::GenColInfo(Array('abbrev' => 'nom_sel_nn',
'nom' => 'Numéro nomenclatural',
'importable' => FALSE)),
'nom_ret' => self::GenColInfo(Array('abbrev' => 'nom_ret',
'nom' => 'Nom retenu',
'importable' => FALSE)),
'nom_ret_nn' => self::GenColInfo(Array('abbrev' => 'nom_ret_nn',
'nom' => 'Numéro nomenclatural nom retenu',
'importable' => FALSE)),
'nt' => self::GenColInfo(Array('abbrev' => 'nt',
'nom' => 'Numéro taxonomique',
'importable' => FALSE)),
'famille' => self::GenColInfo(Array('abbrev' => 'famille',
'nom' => 'Famille',
'importable' => FALSE)),
'nom_referentiel' => self::GenColInfo(Array('abbrev' => 'nom_referentiel',
'nom' => 'Referentiel taxonomique')),
'zone_geo' => self::GenColInfo(Array('abbrev' => 'zone_geo',
'nom' => 'Commune')),
'ce_zone_geo' => self::GenColInfo(Array('abbrev' => 'ce_zone_geo',
'nom' => 'Identifiant Commune',
'fonction' => 'convertirCodeZoneGeoVersDepartement')),
'date_observation' => self::GenColInfo(Array('abbrev' => 'date_observation',
'nom' => 'Date',
'fonction' => 'formaterDate')),
'lieudit' => self::GenColInfo(Array('abbrev' => 'lieudit',
'nom' => 'Lieu-dit')),
'station' => self::GenColInfo(Array('abbrev' => 'station',
'nom' => 'Station')),
'milieu' => self::GenColInfo(Array('abbrev' => 'milieu',
'nom' => 'Milieu')),
'commentaire' => self::GenColInfo(Array('abbrev' => 'commentaire',
'nom' => 'Notes')),
'latitude' => self::GenColInfo(Array('abbrev' => 'latitude',
'nom' => 'Latitude',
'extra' => 1,
'fonction' => 'trim0')),
'longitude' => self::GenColInfo(Array('abbrev' => 'longitude',
'nom' => 'Longitude',
'extra' => 1,
'fonction' => 'trim0')),
'altitude' => self::GenColInfo(Array('abbrev' => 'altitude',
'nom' => 'Altitude',
'extra' => 1)), // pas de trim0 car INT(5) en DB
'geodatum' => self::GenColInfo(Array('abbrev' => 'geodatum',
'nom' => 'Référentiel Géographique',
'extra' => 1,
'importable' => FALSE)),
);
}
 
if(isset($groupe_de_champs['avance'])) {
$colonnes += array(
// TODO: importable = FALSE car pas de merge de données importées
'ordre' => self::GenColInfo(Array('abbrev' => 'ordre',
'nom' => 'Ordre',
'extra' => 1,
'importable' => FALSE)),
'id_observation' => self::GenColInfo(Array('abbrev' => 'id_observation',
'nom' => 'Identifiant',
'extra' => 1,
'importable' => FALSE)),
 
'mots_cles_texte' => self::GenColInfo(Array('abbrev' => 'mots_cles_texte',
'nom' => 'Mots Clés',
'extra' => 1)),
'date_creation' => self::GenColInfo(Array('abbrev' => 'date_creation',
'nom' => 'Date Création',
'extra' => 1,
'importable' => FALSE)),
'date_modification' => self::GenColInfo(Array('abbrev' => 'date_modification',
'nom' => 'Date Modification',
'extra' => 1,
'importable' => FALSE)),
 
// rappel transmission = 1, signifie simplement "public"
// des données importées peuvent être d'emblée "publiques"
// "importable" = TRUE
'transmission' => self::GenColInfo(Array('abbrev' => 'transmission',
'nom' => 'Transmis',
'extra' => 1,
'fonction' => 'boolOuiNon')),
'date_transmission' => self::GenColInfo(Array('abbrev' => 'date_transmission',
'nom' => 'Date Transmission',
'extra' => 1,
'importable' => FALSE)),
'abondance' => self::GenColInfo(Array('abbrev' => 'abondance',
'nom' => 'Abondance',
'extra' => 1)),
'certitude' => self::GenColInfo(Array('abbrev' => 'certitude',
'nom' => 'Certitude',
'extra' => 1)),
'phenologie' => self::GenColInfo(Array('abbrev' => 'phenologie',
'nom' => 'Phénologie',
'extra' => 1)),
 
// XXX: getImages() dépend du contexte de Cel, et doit être appelée comme cas particulier
// cf ExportXLS::traiterLigneObservation()
'images' => self::GenColInfo(Array('abbrev' => 'images',
'nom' => 'Image(s)',
'extra' => 1,
'fonction_data' => NULL /* cas particulier 'getImages' */,
'importable' => TRUE,
//'preload' => array(__CLASS__, 'getImages_preload')//TODO
)),
 
/* 'nom_commun' => self::GenColInfo(Array('abbrev' => 'nom_commun',
'nom' => 'Nom Commun',
'extra' => 1,
'fonction_data' => 'getNomCommun',
'importable' => FALSE),
 
'nom-commun' => self::GenColInfo(Array('abbrev' => 'nom-commun',
'nom' => 'Nom Commun',
'extra' => 1,
'fonction_data' => 'getNomCommun_v2'),
 
'nom-commun' => self::GenColInfo(Array('abbrev' => 'nom-commun',
'nom' => 'Nom Commun',
'extra' => 1,
'fonction_data' => 'getNomCommun_v3'),
'importable' => FALSE), */
'nom-commun' => self::GenColInfo(Array('abbrev' => 'nom-commun',
'nom' => 'Nom Commun',
'extra' => 1,
'fonction_data' => NULL /* cas particu 'getNomCommun_v4' */,
'preload' => array(__CLASS__, 'getNomCommun_preload')))
);
}
 
if(isset($groupe_de_champs['baseflor'])) {
$colonnes += array(
// champ dynamique
'baseflor' => self::GenColInfo(Array('abbrev' => 'baseflor',
'nom' => '',
'extra' => 1,
'importable' => FALSE,
'preload' => array(__CLASS__, 'baseflor_preload'),
'dyna' => array(__CLASS__, 'baseflor_ligne'))),
);
}
 
if(isset($groupe_de_champs['etendu'])) {
$colonnes += array(
// champ dynamique
'etendu' => self::GenColInfo(Array('abbrev' => 'etendu',
'nom' => '',
'extra' => 1,
'importable' => FALSE,
'preload' => array(__CLASS__, 'champsEtendus_preload'),
'dyna' => array(__CLASS__, 'champsEtendus_ligne'))),
);
}
 
if(isset($groupe_de_champs['auteur'])) {
$colonnes += array(
'observateur' => self::GenColInfo(Array('abbrev' => 'observateur',
'nom' => 'Observateur',
'extra' => 1,
'fonction_data' => 'formaterUrlUser',
'importable' => FALSE)),
);
}
 
return $colonnes;
}
 
static function preload($colonnes, $cel, $ids) {
$result = array();
foreach($colonnes as $abbrev => $colonne) {
if(!$colonne['preload']) continue;
$result[$abbrev] = call_user_func($colonne['preload'], $cel, $ids);
}
return $result;
}
 
public static function getIntitulesColonnes($colonnes) {
// array_filter pour supprimer les colonnes "dynamique" n'ayant pas défini $nom (cf GenColInfo())
return array_filter(array_map(array('FormateurGroupeColonne', 'retournerNomItem'), $colonnes));
}
 
public static function retournerNomItem(&$item) {
return $item['nom'];
}
 
public static function getLigneObservation(&$obs, &$colonnes, $cel = false) {
$ligne_formatee = array();
foreach($colonnes as $abbrev => $colonne) {
$valeur = null;
if($colonne['extra'] == 2 || ! is_null($colonne['dyna'])) continue;
 
// valeur directe depuis cel_obs ?
if(isset($obs[$abbrev])) $valeur = $obs[$abbrev];
 
// pré-processeur des champs
if(function_exists($colonne['fonction'])) {
$valeur = $colonne['fonction']($valeur);
} elseif(method_exists(__CLASS__, $colonne['fonction'])) {
$valeur = call_user_func(array(__CLASS__, $colonne['fonction']), $valeur);
} elseif($colonne['fonction']) {
die("méthode {$colonne['fonction']} introuvable");
}
// fonction pour obtenir des champs (étendus)
elseif(function_exists($colonne['fonction_data'])) {
$valeur = $colonne['fonction_data']($obs);
}
elseif(method_exists(__CLASS__, $colonne['fonction_data'])) {
$valeur = call_user_func(array(__CLASS__, $colonne['fonction_data']), $obs);
}
 
// // cette section devrait être vide:
// // cas particuliers ingérable avec l'architecture actuelle:
if(false && $abbrev == 'date_observation' && $valeur == "0000-00-00") {
/* blah */
}
// ici à cause du passage de $cel ($this->utilisateur)
if($abbrev == 'images') {
$valeur = FormateurGroupeColonne::getImages($obs, $cel->id_utilisateur);
}
if($abbrev == 'nom-commun') {
$valeur = FormateurGroupeColonne::getNomCommun_v4($obs);
}
 
if($valeur == null) {
$valeur = "";
}
 
// // fin de section "cas particuliers"
$ligne_formatee[] = $valeur;
}
 
// uniquement les champs dynamiques
foreach($colonnes as $abbrev => $colonne) {
$valeur = null;
if(is_null($colonne['dyna'])) continue;
// XXX: PHP-5.3
call_user_func_array($colonne['dyna'],
array($obs, &$ligne_formatee));
}
 
return $ligne_formatee;
}
 
/*
* Wrapper générant un tableau associatif:
* Ne pas changer les valeurs par défaut du prototype sans réflexion sur l'implication pour nomEnsembleVersListeColonnes()
 
* @param $abbrev (obligatoire): nom court de colonne, largement utilisé lors de l'import.
* En effet chaque ligne importée est accessible à l'aide du `define` de $abbrev en majuscule, préfixé de "C_"
* Exemple: $ligne[C_LONGITUDE] pour "longitude".
* cf: ImportXLS::detectionEntete()
 
* @param $nom (obligatoire): nom complet de colonne (utilisé pour la ligne d'en-tête)
* Les définition de champs dynamique (correspondant à de multiples colonnes) doivent laisser cette valeur
* vide afin de ne pas créer une colonne supplémentaire erronée.
 
* @param $is_extra:
* Si 0, la colonne est une colonne standard
* Si 1, la colonne est extra [le plus souvent générée automatiquement]
* (auquel cas une bordure bleue entoure son nom dans la ligne d'entête)
* Si 2, la colonne n'est pas traité à l'export, mais une définition peut lui être donnée
* qui pourra être utilisée à l'import, exemple: "image"
 
* @param $fonction (optionnel): un nom d'un fonction de préprocessing
* $fonction doit prendre comme seul argument la valeur d'origine et retourner la valeur transformée
 
* @param $fonction_data (optionnel): une *méthode* d'obtention de donnée
* $fonction_data doit prendre comme premier argument le tableau des champs de l'enregistrement existant
* $fonction_data doit retourner une valeur
 
* @param $importable (optionnel): défini si la colonne est traitée (ou absolument ignorée par PHPExcel) lors de
* l'import.
 
* @param $preload (optionnel): défini une fonction de préchargement massif de donnée potentiellement utilisable par $fonction_data.
* Utile, notamment, dans le cadre de l'export
 
* @param $fonction_dynamique (optionnel): défini une fonction ajoutant un nombre arbitraire de colonnes à une ligne donnée
* Utile, notamment, dans le cadre de l'export des champs étendus ou des données baseflor
* La fonction doit TOUJOURS alterer la ligne en lui ajoutant une nombre CONSTANT d'éléments (NULL ou non)
* La fonction doit prendre comme arguments ($obs, &$ligne_formatee)
*/
static function GenColInfo($args) {
$default = Array('abbrev' => NULL,
'nom' => NULL,
'extra' => 0,
'fonction' => NULL,
'fonction_data' => NULL,
'importable' => TRUE,
'preload' => NULL,
'dyna' => NULL);
$ret = array_intersect_key($args, $default);
return array_merge($default, $ret);
}
 
static function formaterDate($date_heure_mysql) {
//return "";
if (!$date_heure_mysql || $date_heure_mysql == "0000-00-00 00:00:00") return NULL;
// malheureusement pas disponible en php < 5.3
//$date_format = DateTime::createFromFormat("Y-m-d H:i:s", $date_heure_mysql);
$val = explode(' ', $date_heure_mysql);
$date = explode('-', $val[0]);
$heure = explode(':', $val[1]);
$timestamp = mktime((int) $heure[0], (int) $heure[1], (int) $heure[2], (int) $date[1], (int) $date[2], (int) $date[0]);
if(!$timestamp) return NULL;
// TODO: les widgets ne font malheureusement pas usage de l'heure dans le CEL
// TODO: si modification, ne pas oublier de modifier le format d'import correspondant
// dans ImportXLS, traiterDateObs() (actuellement: "Y/m/d" car utilisation de strtotime() qui ne lit pas tout)
// $date_formatee = strftime('%d/%m/%Y', $timestamp);
$date_formatee = strftime('%Y/%m/%d', $timestamp);
if(!$date_formatee) return "00/00/0000";
return $date_formatee;
}
 
static function formaterUrlUser($obs) {
$is_id = is_numeric($obs['ce_utilisateur']);
return sprintf("%s %s <%s>%s",
$obs['prenom_utilisateur'],
$obs['nom_utilisateur'],
preg_replace(';@.*;', '@...', $obs['courriel_utilisateur']),
$is_id ? sprintf(' (' . USER_BASEURL . ')', $obs['ce_utilisateur']) : '');
}
 
static function getImages_preload($cel, $obsids) {
if(!$obsids) return;
$rec = Cel::db()->requeter(
sprintf("SELECT o.id_observation, GROUP_CONCAT(nom_original ORDER BY nom_original ASC SEPARATOR '%s') AS i " .
"FROM cel_images i LEFT JOIN cel_obs o ON (i.ce_observation = o.id_observation) " .
"WHERE o.ce_utilisateur = %d AND o.id_observation IN (%s) " .
"GROUP BY id_observation",
SEPARATEUR_IMAGES,
$cel->id_utilisateur,
implode(',', $obsids)));
foreach($rec as $v) {
self::$cache['getImages'][$v['id_observation']] = $v['i'];
}
return NULL;
}
 
static function getImages($obs, $id_utilisateur) {
if(! $id_utilisateur) return NULL;
if(isset(self::$cache['getImages'][$obs['id_observation']]))
return self::$cache['getImages'][$obs['id_observation']];
 
$rec = Cel::db()->requeter(
sprintf("SELECT GROUP_CONCAT(nom_original ORDER BY nom_original ASC SEPARATOR '%s') AS i ".
"FROM cel_images i ".
" LEFT JOIN cel_obs o ON (i.ce_observation = o.id_observation) ".
"WHERE o.ce_utilisateur = %d ".
" AND o.id_observation = %d ".
'LIMIT 1',
SEPARATEUR_IMAGES,
$id_utilisateur,
$obs['id_observation']));
return $rec ? $rec[0]['i'] : NULL;
}
 
public static function convertirCodeZoneGeoVersDepartement($code_zone_geo) {
$code_departement = '';
if(self::estUnCodeInseeDepartement($code_zone_geo)) {
$code_departement = substr(ltrim($code_zone_geo,'INSEE-C:'),0,2);
}
 
return $code_departement;
}
 
public static function trim0($lonlat) {
return trim(trim($lonlat, "0"), ".");
}
 
public static function boolOuiNon($transmission) {
return $transmission ? 'oui' : '';
}
 
public static function estUnCodeInseeDepartement($code_a_tester) {
return preg_match('/^INSEE-C:[0-9]{5}/',$code_a_tester);
}
 
 
// TODO: référentiel ne devrait pas être généré au moment d'un Config::get,
// comme dans Config::get('nomsVernaRechercheLimiteeTpl')
// Par exemple, la variable pour "nva" ?
function getNomCommun($obs) {
$langue = 'fra';
list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
else return '';
 
$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
if(isset(self::$cache['getNomCommun'][$cache_id])) {
//debug: error_log("require url_service_nom_attribution: OK ! (pour \"{$obs['nom_ret']}\")");
return self::$cache['getNomCommun'][$cache_id];
}
// pas de cache:
//debug: error_log("require url_service_nom_attribution pour \"{$obs['nom_ret']}\"");
 
// pour bdtfx:
// /service:eflore:0.1/nvjfl/noms-vernaculaires/attributions?masque.nt=X&masque.lg=fra&retour.champs=num_statut
// /projet/services/modules/0.1/nvjfl/NomsVernaculaires.php
$url = str_replace(Array('{referentiel}', '{valeur}', '{langue}'),
Array($referentiel, $obs['nt'], $langue),
self::$config['eflore']['url_service_nom_attribution']) . // TODO !
"&retour.champs=num_statut";
$noms = @json_decode(file_get_contents($url));
if(! $noms) return '';
$noms = array_filter((array)($noms->resultat), array($this, retournerNumStatutUn)); // XXX: php 5.3
$nom = array_pop($noms)->nom_vernaculaire;
 
// cache
self::$cache['getNomCommun'][$cache_id] = $nom;
return $nom;
}
 
private function retournerNumStatutUn(&$item) {
return ($item->num_statut == 1);
}
 
private function retournerNumStatutUnArr(&$item) {
return ($item['num_statut'] == 1);
}
 
// si getNomCommun_v2 ou getNomCommun_v3 sont utilisés
/* require_once('/home/raphael/eflore/framework/framework/Framework.php');
Framework::setCheminAppli("/home/raphael/eflore/projets/services/index.php");
Framework::setInfoAppli(Config::get('info'));
require_once('/home/raphael/eflore/projets/services/modules/0.1/Projets.php');*/
 
/* Tente de bootstraper le framework au plus court et d'initialiser une instance de
NomsVernaculaires pour obtenir le nom commun */
function getNomCommun_v2($obs) {
static $service;
$service = new Projets();
 
$langue = 'fra';
list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
else return '';
 
$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
if(isset(self::$cache['getNomCommun'][$cache_id])) {
error_log("require NomsVernaculaires.php: OK ! (pour \"{$obs['nom_ret']}\")");
return self::$cache['getNomCommun'][$cache_id];
}
// pas de cache:
error_log("require NomsVernaculaires.php pour \"{$obs['nom_ret']}\"");
 
$donnees = Array('masque.nt' => $obs['nt'],
'masque.lg' => $langue,
'retour.champs' => 'num_statut');
$noms = $service->consulter(Array('nvjfl', 'noms-vernaculaires'), $donnees);
 
if(! $noms) return '';
$noms = array_filter((array)($noms->resultat), array($this, retournerNumStatutUn)); // XXX: php 5.3
$nom = array_pop($noms)->nom_vernaculaire;
 
// cache
self::$cache['getNomCommun'][$cache_id] = $nom;
return $nom;
}
 
 
/* Effectue un bootstraping plus sage que ci-dessus, mais le gain d'efficacité
n'est pas aussi retentissant qu'espéré */
static $service;
function getNomCommun_v3($obs) {
if(! $this->service) $this->service = new Projets();
 
$langue = 'fra';
list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
else return '';
 
$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
if(isset(self::$cache['getNomCommun'][$cache_id])) {
error_log("require NomsVernaculaires.php: OK ! (pour \"{$obs['nom_ret']}\")");
return self::$cache['getNomCommun'][$cache_id];
}
// pas de cache:
error_log("require NomsVernaculaires.php pour \"{$obs['nom_ret']}\"");
 
$donnees = Array('masque.nt' => $obs['nt'],
'masque.lg' => $langue,
'retour.champs' => 'conseil_emploi');
$this->service->initialiserRessourcesEtParametres(Array('nvjfl', 'noms-vernaculaires', 'attributions'), $donnees);
try {
$noms = $this->service->traiterRessources();
} catch(Exception $e) {
return '';
}
if(! $noms) return '';
$noms = array_filter($noms['resultat'], array($this, retournerNumStatutUnArr)); // XXX: php 5.3
$premier_nom = array_pop($noms);
$nom = $premier_nom['nom_vernaculaire'];
 
// cache
self::$cache['getNomCommun'][$cache_id] = $nom;
return $nom;
}
 
/* Cette fonction initialise le cache des noms communs en 1 fois, sur la liste des observations à exporter.
Ainsi, les appels successifs à getNomCommun_v4() ne sont pas couteux (pas de requête SQL) */
static function getNomCommun_preload($cel, $obsids) {
if(!$obsids) return;
if(!self::referenceTableExiste()) return NULL;
 
// CREATE INDEX i_nom_referentiel ON cel_obs (nom_referentiel(5));
$req = sprintf("SELECT r.referentiel, r.num_taxon, r.nom_commun FROM cel_references r" .
" INNER JOIN cel_obs c ON (r.referentiel = substring_index(c.nom_referentiel, ':', 1) and r.num_taxon = c.nt)" .
" WHERE c.id_observation IN (%s)",
implode(',', $obsids));
$res = Cel::db()->requeter($req);
foreach($res as $v) {
self::$cache['getNomCommun'][$v['referentiel'] . '-' . $v['num_taxon'] . '-' . 'fra'] = $v['nom_commun'];
}
return NULL;
}
 
static function referenceTableExiste() {
if (!self::$is_table) {
// une seule fois
if (! Cel::db()->requeterLigne("SHOW TABLES LIKE 'cel_references'")) return FALSE;
self::$is_table = TRUE;
}
return TRUE;
}
 
static function getNomCommun_v4($obs) {
// Attention la fonction suppose que l'on ait fait appel à getNomCommun_preload avant
// d'être appelée
if(! $obs['nt']) return NULL;
if(! self::referenceTableExiste()) return NULL;
 
$langue = 'fra';
list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
 
$nom = null;
// cache:
if(isset(self::$cache['getNomCommun'])) {
if(isset(self::$cache['getNomCommun'][$cache_id])) return self::$cache['getNomCommun'][$cache_id];
// XXX: problème de valeurs NULL ?
if(array_key_exists($cache_id, self::$cache['getNomCommun'])) {
$nom = self::$cache['getNomCommun'][$cache_id];
}
}
 
return $nom;
}
 
 
/* Cette fonction initialise le cache des données baseflor en 1 fois, sur la liste des observations à exporter.
Ainsi, les appels successifs à baseflor_ligne() ne sont pas couteux (pas de requête SQL) */
static function baseflor_preload($cel, $obsids) {
if(!$obsids) return;
if(!self::referenceTableExiste()) return NULL;
 
// Attention (en attendant de faire une meilleure table et une meilleure requete) le distinct est très important
$req = sprintf("SELECT DISTINCT referentiel, num_nom_retenu, %s FROM cel_references r" .
" INNER JOIN cel_obs c ON (r.num_nom_retenu = c.nom_ret_nn)" .
" AND r.referentiel = IF(LOCATE(':', c.nom_referentiel) = 0, c.nom_referentiel, SUBSTR(c.nom_referentiel FROM 1 FOR LOCATE(':', c.nom_referentiel) - 1))" .
" WHERE c.id_observation IN (%s)",
//" AND catminat_code IS NOT NULL", // TODO: suppression des NULL ici signifie que le cache sera partiel...
implode(',', array_keys(self::$baseflor_col)),
implode(',', $obsids));
$res = Cel::db()->requeter($req);
if(!$res) return NULL;
 
foreach($res as $v) {
$data = $v;
unset($data['referentiel']); // non nécessaire
unset($data['num_nom_retenu']); // non nécessaire
 
// Des fois les synonymes ont des valeurs pour baseflor et pas le nom retenu et vice versa
// on les fusionne pour avoir le maximum d'infos, en attendant de repenser la table référence
if(isset(self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']])) {
$orig = array_filter(self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']], 'strlen');
$data = array_filter($data , 'strlen');
$data = array_merge($orig, $data);
}
 
self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']] = $data;
}
 
return NULL;
}
 
/**
* Attention la fonction suppose que l'on ait fait appel à baseflor_preload avant
* d'être appelée
* @CASSECOUILLES elle pourrait le détecter et le faire elle-même
*/
static function baseflor_ligne($obs, &$ligne) {
$clefsBF = array_keys(self::$baseflor_col);
// par défaut des colonnes vides pour ne pas décaler le bousin
$donneesBF = array_fill_keys($clefsBF, "");
 
// s'il y a des données baseflor
if ($obs['nom_ret_nn'] && self::referenceTableExiste() && count(self::$cache['getBaseflor']) > 0) {
// l'astuce à un franc vingt
list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
$cache_id = $referentiel . '-' . $obs['nom_ret_nn'];
 
// si les données baseflor existent dans le cache pour ce nom_ret_nn
if (array_key_exists($cache_id, self::$cache['getBaseflor'])) {
$donneesBFATrous = self::$cache['getBaseflor'][$cache_id];
foreach($clefsBF as $colbf) { // remplit les trous tout en préservant l'ordre
if(array_key_exists($colbf, $donneesBFATrous)) {
$donneesBF[$colbf] = $donneesBFATrous[$colbf];
} else {
$donneesBF[$colbf] = "";
}
}
}
}
 
// Quand les données sont prêtes, on les fusionne
$ligne = array_merge($ligne, $donneesBF);
}
 
static function champsEtendus_preload($cel, $obsids) {
$gestion_champs_etendus = new GestionChampsEtendus($cel->config, 'obs');
$colonnes_champs_supp_par_obs = $gestion_champs_etendus->consulterClesParLots($obsids);
 
// Supprime les champs étendus considérés comme privés dans le cas de l'export public en chargeant
// le catalogue et en excluant ceux qui sont explicitement privés
if(!$cel->export_prive) {
$indices_a_supprimer = array();
$catalogue_champs_etendus = $gestion_champs_etendus->consulterCatalogueChampsEtendusPredefinis();
foreach($catalogue_champs_etendus as $champ_catalogue) {
if($champ_catalogue['options']['prive'] == 1) {
// Les champs étendus peuvent avoir des variantes lorsqu'ils apparaissent de multiples fois.
// Vont donc matcher monChamp mais aussi monChamp:1, monChamp:2 ou bien monChamp1, monChamp: etc...
// pour plus de sécurité (ce filtra n'est affectué qu'une fois au début de l'export donc on ne s'en prive pas)
$entrees = preg_grep("/".$champ_catalogue['cle']."(?::?\d*)?$/", $colonnes_champs_supp_par_obs);
$indices_a_supprimer = array_merge($indices_a_supprimer, array_keys($entrees));
}
}
// les champs étendus sont renvoyés dans l'export suivant les colonnes présentes dans ce tableau
// les éliminer de la liste des colonnes suffit à les faire ignorer par l'export
foreach($indices_a_supprimer as $indice_supp) {
unset($colonnes_champs_supp_par_obs[$indice_supp]);
}
}
 
// ces deux lignes réordonnent l'ordre des colonnes des champs étendus en fonction de l'ordre (très spécifique)
// de self::$ordre_champ_etendus_Florileges, les champs non-mentionnés sont ajoutés à la fin.
$colonnes_champs_supp_par_obs = self::sortArrayByArray(array_flip($colonnes_champs_supp_par_obs),
self::$ordre_champ_etendus_Florileges);
$colonnes_champs_supp_par_obs = array_keys($colonnes_champs_supp_par_obs);
 
// si le SELECT des clefs ne retourne rien, une autre requêtes est inutile
// TODO: optimize, 1 seule requête
if(!$colonnes_champs_supp_par_obs) return Array('header' => array(), 'data' => array());
 
$champs_supp_par_obs = $gestion_champs_etendus->consulterParLots($obsids);
 
self::$cache['champsEtendus']['header'] = self::champsEtendus_prefixHeader($colonnes_champs_supp_par_obs);
 
foreach($champs_supp_par_obs as &$v) {
$v = self::champsEtendus_aplatir($v);
}
self::$cache['champsEtendus']['data'] = $champs_supp_par_obs;
// ce return est temporaire,
// le temps que toutes les fonctions bougent ici et utilise plutôt le cache statique
// encore utilisé pour les entêtes (self::$cache['champsEtendus']['header'])
return self::$cache['champsEtendus'];
}
 
// XXX: PHP-5.3, fonction anonyme + array_map
static function champsEtendus_prefixHeader($array) {
return array_map(create_function('$v', 'return "' . PREFIX_CHAMPS_ETENDUS . '".$v;'), $array);
}
 
// XXX: PHP-5.3, fonction anonyme + array_map
static function champsEtendus_aplatir($ligne_champs_etendus) {
$champs_etendus_fmt = array();
if(!$ligne_champs_etendus) return $champs_etendus_fmt;
foreach($ligne_champs_etendus as $champ) {
$champs_etendus_fmt[PREFIX_CHAMPS_ETENDUS . $champ->cle] = $champ->valeur;
}
return $champs_etendus_fmt;
}
 
static function champsEtendus_ligne($obs, &$ligne) {
// si header n'est pas défini, aucune observation ne possède de champ étendu
// et nous n'ajoutons ni colonnes, ni valeurs.
if(! isset(self::$cache['champsEtendus']['header'])) return;
$ligne_etendue_aplatie = @self::$cache['champsEtendus']['data'][$obs['id_observation']];
 
$ligne_supp = array_fill(0, count(self::$cache['champsEtendus']['header']), '');
$ligne_etendue_fmt = array();
 
// si, cependant cette seule observation n'a pas de champs étendus,
// nous devons rajouter des blancs (notamment dans le cas ou d'autres
// champs viennent à être ajoutés en aval à l'avenir
// cf: $fonction_dynamique dans FormateurGroupeColonne::GenColInfo()
if(! $ligne_etendue_aplatie) {
$ligne = array_merge($ligne, $ligne_supp);
return;
}
 
foreach(self::$cache['champsEtendus']['header'] as $colonne) {
if(!isset($ligne_etendue_aplatie[$colonne])) {
$ligne_etendue_fmt[$colonne] = '';
} else {
$ligne_etendue_fmt[$colonne] = $ligne_etendue_aplatie[$colonne];
}
}
 
// XXX/ array_merge() ?
$ligne += $ligne_etendue_fmt;
}
 
/* HELPERS */
 
// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
// XXX; redéfinition, utilisé aussi par ExportXLS
static function sortArrayByArray($array, $orderArray) {
$ordered = array();
foreach($orderArray as $key) {
if(array_key_exists($key, $array)) {
$ordered[$key] = $array[$key];
unset($array[$key]);
}
}
return $ordered + $array;
}
 
}
/trunk/jrest/bibliotheque/RechercheObservation.php
New file
0,0 → 1,373
<?php
// declare(encoding='UTF-8');
/**
* Librairie de recherche d'observations à partir de divers critères.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class RechercheObservation extends Cel {
 
public $requete_selection_observations;
public static $tris_possibles = array(
'nom_referentiel',
'nom_sel',
'certitude',
'nom_ret',
'famille',
'abondance',
'phenologie',
'transmission',
'ce_zone_geo',
'altitude',
'date_observation',
'ordre'
);
 
public function obtenirIdObservationsPourOrdre($id_utilisateur, $ordre) {
if (is_array($ordre)) {
$ordre = array_map(array(Cel::db(), 'proteger'), $ordre);
$portionOrdre = ' IN ('.implode(',', $ordre).') ';
} else {
$portionOrdre .= ' = '.Cel::db()->proteger($ordre).' ';
}
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$requete = 'SELECT id_observation '.
'FROM cel_obs '.
"WHERE ordre $portionOrdre ".
"AND ce_utilisateur = $idUtilisateurP ".
'ORDER BY id_observation '.
' -- '.__FILE__.':'.__LINE__;
$resultats = Cel::db()->requeter($requete);
 
$ids = array();
if (is_array($resultats)) {
foreach ($resultats as $resultat) {
$ids[] = $resultat['id_observation'];
}
}
return $ids;
}
 
public function rechercherObservations($id_utilisateur = null, $criteres = array(), $debut = 0, $limite = 50, $autoriser_sql_brut = FALSE) {
$requete_selection_observations = 'SELECT * FROM cel_obs ';
 
if ($id_utilisateur != null) {
$requete_selection_observations .= 'WHERE ce_utilisateur = '.Cel::db()->proteger($id_utilisateur).' AND ';
} else if(count($criteres) > 0) {
$requete_selection_observations .= 'WHERE ';
}
 
$tri = (isset($criteres['tri']) && in_array($criteres['tri'], self::$tris_possibles)) ? $criteres['tri'] : 'ordre';
unset($criteres['tri']);
$tri_dir = (isset($criteres['tri_dir']) && in_array($criteres['tri_dir'], array('ASC', 'DESC'))) ? $criteres['tri_dir'] : 'ASC';
unset($criteres['tri_dir']);
 
// très mauvaise solution, mais qui permet au moins d'effectuer des requêtes complexes, sans modifier l'API
// et sans pour autant introduire de problème de sécurité majeur dans toutes les fonctions appelantes qui
// effectue $criteres = $_GET sans nettoyage préalable.
if(isset($criteres['sql_brut']) && !$autoriser_sql_brut) unset($criteres['sql_brut']);
$sous_requete_recherche = $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
 
$requete_selection_observations .= $sous_requete_recherche;
$requete_selection_observations = rtrim($requete_selection_observations, 'AND ');
$requete_selection_observations .= $id_utilisateur == null ? ' ORDER BY id_observation, ordre ' : ' ORDER BY '.$tri.' '.$tri_dir;
$requete_selection_observations .= ($debut == 0 && $limite == 0) ? '' : ' LIMIT '.$debut.','.$limite ;
 
$this->requete_selection_observations = $requete_selection_observations;
return $this;
}
 
public function get() {
if (!$this->requete_selection_observations) {
return false;
}
return Cel::db()->requeter($this->requete_selection_observations);
}
 
public function compterObservations($id_utilisateur = null, $criteres = array()) {
$requete = 'SELECT COUNT(*) AS nb_obs FROM cel_obs ';
if ($id_utilisateur != null) {
$requete .= 'WHERE ce_utilisateur = '.Cel::db()->proteger($id_utilisateur).' AND ';
} else if(count($criteres) > 0) {
$requete .= 'WHERE ';
}
$requete .= $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
$requete = rtrim($requete, 'AND ');
$resultat = Cel::db()->requeter($requete);
 
$nb_obs = ($resultat && is_array($resultat) && count($resultat) > 0) ? $resultat[0]['nb_obs'] : '0';
return $nb_obs;
}
 
public function formaterPourEnvoiCel(&$tableau_observations) {
$ids = array();
foreach ($tableau_observations as &$observation) {
$observation['ce_zone_geo'] = $this->convertirCodeZoneGeoVersCodeInsee($observation['ce_zone_geo']);
$ids_mots_cles = $this->getIdsMotsClesObservation($observation['id_observation']);
 
$ids[] = $observation['id_observation'];
$mots_cles_chaine = '';
foreach ($ids_mots_cles as $id_mot_cle) {
$mots_cles_chaine .= $id_mot_cle['id_mot_cle'].';';
}
$mots_cles_chaine = rtrim($mots_cles_chaine,';');
$observation['mots_cles'] = $mots_cles_chaine;
 
foreach ($observation as $champ => $valeur) {
if ($valeur == 'NULL') {
$observation[$champ] = '';
}
}
}
 
$gestion_champs_etendus = new GestionChampsEtendus($this->config, 'obs');
$champs_supp = $gestion_champs_etendus->consulterParLots($ids);
 
foreach ($tableau_observations as &$obs) {
if (isset($champs_supp[$obs['id_observation']])) {
$obs['obs_etendue'] = $champs_supp[$obs['id_observation']];
}
}
return $tableau_observations;
}
 
public function obtenirIdUtilisateurPourIdObs($id_obs) {
$idObsP = Cel::db()->proteger($id_obs);
$requete = 'SELECT ce_utilisateur '.
'FROM cel_obs '.
"WHERE id_observation = $idObsP ".
' -- '.__FILE__.':'.__LINE__;
$utilisateur_id = Cel::db()->requeter($requete);
 
$retour = false;
if (!empty($utilisateur_id) && isset($utilisateur_id[0]['ce_utilisateur'])) {
$retour = $utilisateur_id[0]['ce_utilisateur'];
}
return $retour;
}
 
public function obtenirCourrielUtilisateurPourIdObs($id_obs) {
$id_obs = Cel::db()->proteger($id_obs);
$requete = "SELECT courriel_utilisateur FROM cel_obs WHERE id_observation = $id_obs";
 
$utilisateur_courriel = Cel::db()->requeter($requete . ' -- ' . __FILE__ . ':' . __LINE__);
 
$retour = false;
if (!empty($utilisateur_courriel) && isset($utilisateur_courriel[0]['courriel_utilisateur'])) {
$retour = $utilisateur_courriel[0]['courriel_utilisateur'];
}
return $retour;
}
 
private function getIdsMotsClesObservation($id_observation) {
$requete_selection_mots_cles = 'SELECT DISTINCT id_mot_cle '.
'FROM cel_mots_cles_obs_liaison '.
"WHERE id_element_lie = $id_observation ";
return Cel::db()->requeter($requete_selection_mots_cles);
}
 
// TODO: fonction temporaire
public function parserRequeteCriteres($chaine_criteres) {
$criteres_parses = array();
$criteres = explode('&', $chaine_criteres) ;
foreach ($criteres as &$critere) {
$nom_valeur = explode('=', $critere) ;
if (count($nom_valeur) >= 2) {
$criteres_parses[$nom_valeur[0]] = $nom_valeur[1];
}
}
return $criteres_parses;
}
 
private function fabriquerSousRequeteRecherche($id_utilisateur, $criteres) {
$sous_requete = '';
foreach ($criteres as $nom => $valeur) {
if ($valeur == null || trim($nom) == '' || trim($valeur) == '') {
continue;
}
 
switch ($nom) {
case "mots_cles";
$sous_requete .= $this->creerSousRequeteMotsCles($valeur);
$sous_requete .= ' AND ';
break;
case 'annee':
if ($valeur == "NULL") {
$sous_requete .= "(date_observation IS NULL OR year(date_observation) = 0000)" ;
} else {
$sous_requete .= "(year(date_observation) = ".Cel::db()->proteger($valeur).")" ;
}
$sous_requete .= ' AND ' ;
break;
case 'mois':
if ($valeur == "NULL") {
$sous_requete .= "date_observation IS NULL OR month(date_observation) = 00" ;
} else {
$sous_requete .= "month(date_observation) = ".Cel::db()->proteger($valeur) ;
}
$sous_requete .= ' AND ' ;
break;
case 'jour':
if ($valeur == "NULL") {
$sous_requete .= "date_observation IS NULL OR day(date_observation) = 00" ;
} else {
$sous_requete .= "day(date_observation) = ".Cel::db()->proteger($valeur) ;
}
$sous_requete .= ' AND ' ;
break;
case 'departement':
if ($valeur == "NULL") {
$sous_requete .= "(ce_zone_geo IS NULL OR ce_zone_geo = '')";
} else {
if (strpos($valeur,',') !== false) {
$dpts = explode(',',$valeur);
$sous_requete .= '(';
foreach($dpts as $dpt) {
$sous_requete .= "ce_zone_geo LIKE ".Cel::db()->proteger('INSEE-C:'.$dpt.'___').' OR ';
}
$sous_requete = rtrim($sous_requete,' OR ').') ';
} else {
$sous_requete .= "(ce_zone_geo LIKE ".Cel::db()->proteger('INSEE-C:'.$valeur.'___').')';
}
}
$sous_requete .= ' AND ' ;
break;
case 'commune':
if ($valeur == "NULL") {
$sous_requete .= "(zone_geo IS NULL OR zone_geo = '')";
} else {
$sous_requete .= "(zone_geo = ".Cel::db()->proteger($valeur).')';
}
$sous_requete .= ' AND ' ;
break;
case 'id_mots_cles':
$liste_mc = '"'.str_replace(';','","',$valeur).'"';
$tpl_sous_requete = GestionMotsClesChemin::obtenirTemplateRequeteMotsClesIds('obs');
$sous_requete .= 'id_observation IN ('.sprintf($tpl_sous_requete, $liste_mc).')';
$sous_requete .= ' AND ' ;
break;
case 'recherche':
$sous_requete .= $this->fabriquerSousRequeteRechercheGenerale($id_utilisateur, $valeur);
$sous_requete .= ' AND ';
break;
case 'date_debut':
$sous_requete .= 'date_observation >= '.Cel::db()->proteger($this->formaterEnDateMysql($valeur));
$sous_requete .= ' AND ';
break;
case 'date_fin':
$sous_requete .= 'date_observation <= '.Cel::db()->proteger($this->formaterEnDateMysql($valeur));
$sous_requete .= ' AND ';
break;
case 'taxon':
$valeur_protegee = Cel::db()->proteger($valeur."%");
$sous_requete .= "( nom_sel LIKE ".$valeur_protegee." OR".
" nom_ret LIKE ".$valeur_protegee." OR".
" famille LIKE ".$valeur_protegee.
" ) AND ";
break;
case 'sql_brut':
$sous_requete .= $valeur;
$sous_requete .= ' AND ';
break;
default:
if(!preg_match('/^[a-zA-Z0-9_-]+$/', $nom)) break;
$valeur = rtrim($valeur);
// TODO: pour de nombreux champs, et lorsque les webservices d'update/insert
// trim() + NULLify'ront les données vides, alors nous pourrons omettre ce pénible
// double-test pour la plupart des champs
if ($valeur && $valeur != 'NULL') {
$sous_requete .= $nom." = ".Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ';
}
else {
$sous_requete .= "($nom IS NULL OR $nom = '')";
$sous_requete .= ' AND ';
}
break;
}
}
$sous_requete = rtrim($sous_requete,' AND ');
return $sous_requete;
}
 
private function formaterEnDateMysql($date) {
$annee = substr($date, 6, 4);
$mois = substr($date, 3, 2);
$jour = substr($date, 0, 2);
$date = $annee . '-' . $mois . '-' . $jour;
return $date;
}
 
private function fabriquerSousRequeteRechercheGenerale($id_utilisateur, $valeur) {
$valeur = str_replace('*', '%', $valeur);
$valeur = Cel::db()->proteger("%$valeur%");
$sous_requete = "(nom_sel LIKE $valeur ".
"OR courriel_utilisateur LIKE $valeur ".
"OR prenom_utilisateur LIKE $valeur ".
"OR nom_utilisateur LIKE $valeur ".
"OR nom_sel LIKE $valeur ".
"OR nom_sel_nn LIKE $valeur ".
"OR nom_ret LIKE $valeur ".
"OR nom_ret_nn LIKE $valeur ".
"OR nt LIKE $valeur ".
"OR famille LIKE $valeur ".
"OR zone_geo LIKE $valeur ".
"OR ce_zone_geo LIKE $valeur ".
"OR date_observation LIKE $valeur ".
"OR lieudit LIKE $valeur ".
"OR station LIKE $valeur ".
"OR milieu LIKE $valeur ".
"OR commentaire LIKE $valeur ".
"OR transmission LIKE $valeur ".
"OR latitude LIKE $valeur ".
"OR longitude LIKE $valeur ".
"OR mots_cles_texte LIKE $valeur ".
")";
return $sous_requete;
}
 
private function creerSousRequeteMotsCles($mot_cle) {
//TODO: une requête plus efficace serait possible en utilisant
// les vraies tables de mots clés et en faisant disparaitre ce champ maudit
$requete = '';
if (preg_match('/.*OU.*/', $mot_cle)) {
$mots_cles_tab = explode('OU',$mot_cle);
foreach($mots_cles_tab as $mot_cle_item) {
$requete .= '(mots_cles_texte LIKE '.Cel::db()->proteger('%'.$mot_cle_item.'%').') OR ';
}
$requete = '('.rtrim($requete,'OR ').')';
} else if (preg_match('/.*ET.*/', $mot_cle)) {
$mots_cles_tab = explode('ET',$mot_cle);
foreach($mots_cles_tab as $mot_cle_item) {
$requete .= '(mots_cles_texte LIKE '.Cel::db()->proteger('%'.$mot_cle_item.'%').') AND ';
}
$requete = '('.rtrim($requete, 'AND ').') ';
} else {
$requete = "(mots_cles_texte LIKE ".Cel::db()->proteger('%'.$mot_cle.'%').') ';
}
return $requete;
}
 
private function estUnvaleurZeroNulle($valeur) {
return $valeur == '000null';
}
 
private function traiterRequeteValeurZeroNulle($valeur) {
$champs = array('annee' => 'date_observation',
'mois' => 'date_observation',
'jour' => 'date_observation',
'departement' => 'ce_zone_geo',
'commune' => 'zone_geo');
return $sous_requete .= $champs[$valeur]." = ".Cel::db()->proteger("");
}
}
/trunk/jrest/bibliotheque/GestionMotsClesChemin.php
New file
0,0 → 1,719
<?php
/**
* @package jrest
* @author Aurélien Peronnet <aurelien@tela-botania.org>
* @copyright 2010, 2013 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
*
* Librairie de liaison d'images et d'observation à des mots clés en utilisant la méthode
* path enumeration
*/
 
class GestionMotsClesChemin {
 
private $config;
private $mode;
 
private $table_liaison;
private $table_mots_cles;
 
//TODO: trigger pour les tables liaisons
 
public function GestionMotsClesChemin($config, $mode = 'obs') {
$this->config = $config;
//TODO: switch suivant mode
$this->mode = $mode;
 
list($this->table_liaison, $this->table_mots_cles) = self::getTablesMotsClesEtLiaisons($mode);
}
 
public function obtenirArbre($id_utilisateur, $chemin = '/') {
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$cheminP = Cel::db()->proteger($chemin.'%');
 
$requete = 'SELECT * '.
"FROM {$this->table_mots_cles} ".
"WHERE id_utilisateur = $idUtilisateurP ".
"AND chemin LIKE $cheminP ".
'ORDER BY chemin '.
' -- '.__FILE__.':'.__LINE__;
 
$arbre = Cel::db()->requeter($requete);
foreach ($arbre as &$noeud) {
$noeud['chemin'] = strtolower($noeud['chemin']);
}
usort($arbre, array('GestionMotsClesChemin', 'comparerProfNoeuds'));
return $arbre;
}
 
public function obtenirIdsMotsClesParIdParent($id_utilisateur, $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$sousRequete = "SELECT chemin FROM {$this->table_mots_cles} WHERE id_mot_cle = $idMotCleP ";
$requete = 'SELECT id_mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE chemin LIKE CONCAT(($sousRequete), '%') ".
"AND id_utilisateur = ".$idUtilisateurP." ".
' -- '.__FILE__.':'.__LINE__;
 
$ids_enfants = Cel::db()->requeter($requete);
 
return $ids_enfants;
}
 
/**
* Retourne la liste des mots clés pour l'élément lié fourni. Ne tient pas compte de l'utilisateur
* car seul le propriétaire d'un élément lié peut y lier des mots clés
*/
public function obtenirIdsMotsClesParIdElementLie($id_element_lie) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
 
$requete = "SELECT id_mot_cle FROM {$this->table_liaison} WHERE id_element_lie = $idElementLieP" .
' -- '.__FILE__.':'.__LINE__;
 
$ids_mots_cles = Cel::db()->requeter($requete);
 
return $ids_mots_cles;
}
 
public function insererParCheminSiInexistant($mot_cle, $chemin_parent, $id_utilisateur) {
$mot_cle = self::simplifier($mot_cle);
$cheminMotCle = self::getCheminHarmonise($chemin_parent, $mot_cle);
$cheminMotCleP = Cel::db()->proteger($cheminMotCle);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$requete = 'SELECT id_mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE chemin = $cheminMotCleP ".
"AND id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
 
$infosMotCle = Cel::db()->requeter($requete);
 
if (!empty($infosMotCle)) {
$idMotCle = $infosMotCle[0]['id_mot_cle'];
} else {
$idMotCle = $this->insererParChemin($mot_cle, $chemin_parent, $id_utilisateur);
}
return $idMotCle;
}
 
public function insererParChemin($mot_cle, $chemin_parent, $id_utilisateur) {
$mot_cle = self::simplifier($mot_cle);
$cheminMotCle = self::getCheminHarmonise($chemin_parent, $mot_cle);
$cheminMotCleP = Cel::db()->proteger($cheminMotCle);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$motCleP = Cel::db()->proteger($mot_cle);
 
$requete = "INSERT INTO {$this->table_mots_cles} ".
'(chemin, id_utilisateur, mot_cle) '.
"VALUES ($cheminMotCleP, $idUtilisateurP, $motCleP ) ".
' -- '.__FILE__.':'.__LINE__;
 
$insertion = Cel::db()->executer($requete);
$resultat = false;
if ($insertion !== false) {
$resultat = Cel::db()->obtenirDernierId();
}
return $resultat;
}
 
public function insererParIdParent($mot_cle, $id_parent, $id_utilisateur) {
$motCleSimple = self::simplifier($mot_cle);
$motCleSimpleP = Cel::db()->proteger(strtolower(self::supprimerAccents($mot_cle)));
$idParentP = Cel::db()->proteger($id_parent);
$racineP = Cel::db()->proteger('/');
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$motCleP = Cel::db()->proteger($mot_cle);
 
$sousRequete = $racineP;
if ($id_parent != '') {
$sousRequete = '(SELECT chemin '.
"FROM {$this->table_mots_cles} AS ctp ".
"WHERE ctp.id_mot_cle = $idParentP) ";
}
 
$requete = "INSERT INTO {$this->table_mots_cles} (chemin, id_utilisateur, mot_cle) ".
"VALUES (CONCAT($sousRequete, $motCleSimpleP, '/'), $idUtilisateurP, $motCleP ) ".
' -- '.__FILE__.':'.__LINE__;
 
$insertion = Cel::db()->executer($requete);
if ($insertion !== false) {
$resultat = Cel::db()->obtenirDernierId();
}
return $resultat;
}
 
public function lierParId($id_mot_cle, $id_element_lie) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
$idMotCleP = Cel::db()->proteger($id_mot_cle);
 
$requete = "INSERT INTO {$this->table_liaison} (id_element_lie, id_mot_cle) ".
"VALUES ($idElementLieP, $idMotCleP) ".
'ON DUPLICATE KEY UPDATE id_element_lie = id_element_lie '.
' -- '.__FILE__.':'.__LINE__;
 
$liaison = Cel::db()->executer($requete);
return $liaison;
}
 
public function lierParChemin($chemin, $id_element_lie, $id_utilisateur) {
$cheminP = Cel::db()->proteger(self::harmoniserChemin($chemin));
$idElementLieP = Cel::db()->proteger($id_element_lie);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$sousRequete = '(SELECT id_mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE chemin = $cheminP ".
"AND id_utilisateur = $idUtilisateurP ".
')';
$requete = "INSERT INTO {$this->table_liaison} (id_element_lie, id_mot_cle) ".
"VALUES ($idElementLieP, $sousRequete) ".
'ON DUPLICATE KEY UPDATE id_element_lie = id_element_lie '.
' -- '.__FILE__.':'.__LINE__;
 
$liaison = Cel::db()->executer($requete);
return $liaison;
}
 
public function lierParTableaux($ids_mots_cles, $ids_elements_lies) {
$combinaisons = array();
foreach ($ids_mots_cles as $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
foreach ($ids_elements_lies as $id_element_lie) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
$combinaisons[] = "($idElementLieP, $idMotCleP)";
}
}
 
$valeursGroupees = implode(', ', $combinaisons);
$requete = "INSERT INTO {$this->table_liaison} (id_element_lie, id_mot_cle) ".
"VALUES $valeursGroupees ".
"ON DUPLICATE KEY UPDATE id_element_lie = id_element_lie ".
' -- '.__FILE__.':'.__LINE__;
$liaison = Cel::db()->executer($requete);
 
return $liaison;
}
 
/**
* Modifie les liaisons aux mots clés pour chaque element lié, en supprimant et ajoutant seulement les
* mots clés qui ont changé, sans toucher à ceux qui sont conservés. Ça évite de tout supprimer avant,
* et ainsi de perdre la date de liaison des mots clés conservés.
* Si $supprimer est true, les mots clefs existant mais non spécifiés dans le POST seront supprimés,
* sinon ils seront laissés en l'état.
*/
public function modifierLiaisonParTableaux($ids_mots_cles, $ids_elements_lies, $supprimer) {
$reussi = true;
 
foreach ($ids_elements_lies as $id_element_lie) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
// trouver les mots clés actuels
$ids_mots_cles_actuels = $this->obtenirIdsMotsClesParIdElementLie($id_element_lie);
if (! is_array($ids_mots_cles_actuels)) {
$ids_mots_cles_actuels = array();
}
// remise en forme
foreach ($ids_mots_cles_actuels as $k => $v) {
if (isset($v['id_mot_cle'])) { // je vois mal comment ça pourrait ne pas être set
$ids_mots_cles_actuels[$k] = $v['id_mot_cle'];
}
}
 
// changements
$ids_mots_cles_ajoutes = array_diff($ids_mots_cles, $ids_mots_cles_actuels);
if ($supprimer === true) {
$ids_mots_cles_supprimes = array_diff($ids_mots_cles_actuels, $ids_mots_cles);
}
 
// insérer
if (count($ids_mots_cles_ajoutes) > 0) {
$combinaisons = array();
foreach ($ids_mots_cles_ajoutes as $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$combinaisons[] = "($idElementLieP, $idMotCleP)";
}
$valeursGroupees = implode(', ', $combinaisons);
$requete = "INSERT INTO {$this->table_liaison} (id_element_lie, id_mot_cle) ".
"VALUES $valeursGroupees ".
"ON DUPLICATE KEY UPDATE id_element_lie = id_element_lie ". // normalement pas nécessaire
' -- '.__FILE__.':'.__LINE__;
$resultat = Cel::db()->executer($requete);
$reussi = ($reussi && $resultat);
}
 
// supprimer
if ($supprimer === true && count($ids_mots_cles_supprimes) > 0) {
$clauses = array();
foreach ($ids_mots_cles_supprimes as $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$clauses[] = "(id_element_lie = $idElementLieP AND id_mot_cle = $idMotCleP)";
}
$clause = implode(' OR ', $clauses);
$requete = "DELETE FROM {$this->table_liaison} WHERE $clause" .
' -- '.__FILE__.':'.__LINE__;
$resultat = Cel::db()->executer($requete);
$reussi = ($reussi && $resultat);
}
}
 
return $reussi;
}
 
public function supprimerLiaisonsMotsCles($ids_mots_cles, $ids_elements_lies, $id_utilisateur) {
$combinaisons = array();
foreach ($ids_mots_cles as $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
foreach ($ids_elements_lies as $id_element_lie) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
$combinaisons[] = "(id_element_lie = $idElementLieP AND id_mot_cle = $idMotCleP)";
}
}
$clauseWhere = implode(' OR ', $combinaisons);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE $clauseWhere ".
' -- '.__FILE__.':'.__LINE__;
 
$suppression = Cel::db()->executer($requete);
return $suppression;
}
 
public function supprimerToutesLiaisonsPourIdsElementsLies($ids_elements_lies) {
$idsElementsLiesP = Cel::db()->proteger($ids_elements_lies);
$listeIds = implode(',', $idsElementsLiesP);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE id_element_lie IN ($listeIds) ".
' -- '.__FILE__.':'.__LINE__;
 
$suppression = Cel::db()->executer($requete);
$suppression = ($suppression !== false) ? true : false;
return $suppression;
}
 
public function supprimerToutesLiaisonsIdsMotsCles($ids_mots_cles, $id_utilisateur) {
$suppression = true;
if (!empty($ids_mots_cles)) {
$idsMotsClesP = Cel::db()->proteger($ids_mots_cles);
$listeIds = implode(',', $idsMotsClesP);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE id_mot_cle IN ($listeIds) ".
' -- '.__FILE__.':'.__LINE__;
 
$suppression = Cel::db()->executer($requete);
$suppression = ($suppression !== false) ? true : false;
}
return $suppression;
}
 
/**
* Supprime toutes les laisons pour un utilisateur et un mot clé (au sens textuel) donnés.
*
*/
public function supprimerLiaisonPourMotCleEtIdElementLie($mot_cle, $id_element_lie, $id_utilisateur) {
$mot_cle = self::simplifier($mot_cle);
$idElementLieP = Cel::db()->proteger($id_element_lie);
$motCleP = Cel::db()->proteger($mot_cle);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$sousRequete = "SELECT id_mot_cle FROM {$this->table_mots_cles} ".
"WHERE mot_cle = $motCleP ".
"AND id_utilisateur = $idUtilisateurP ";
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE id_element_lie = $idElementLieP ".
"AND id_mot_cle IN ($sousRequete) ".
' -- '.__FILE__.':'.__LINE__;
 
$suppression_liaison = Cel::db()->executer($requete);
$suppression_liaison = ($suppression_liaison !== false);
 
return $suppression_liaison;
}
 
public function renommerMotCle($id_mot_cle, $nouveau_nom) {
$nouveauNomSimple = self::simplifier($nouveau_nom);
$nouveauNomSimpleP = Cel::db()->proteger($nouveauNomSimple);
$idMotCleP = Cel::db()->proteger($id_mot_cle);
 
$requete = 'SELECT chemin, id_utilisateur '.
"FROM {$this->table_mots_cles} ".
"WHERE id_mot_cle = $idMotCleP ".
' -- '.__FILE__.':'.__LINE__;
$ancienCheminInfos = Cel::db()->requeter($requete);
 
$ancienChemin = $ancienCheminInfos[0]['chemin'];
$id_utilisateur = $ancienCheminInfos[0]['id_utilisateur'];
 
$cheminDecompo = explode('/', $ancienChemin);
// le dernier élément du tableau est vide (à cause du / terminal)
// c'est également le cas pour le premier (à cause du / qui commence le chemin)
$cheminDecompo[count($cheminDecompo) - 2] = $nouveauNomSimple;
$nouveauChemin = implode('/', $cheminDecompo);
 
$requete = "UPDATE {$this->table_mots_cles} ".
"SET mot_cle = $nouveauNomSimpleP ".
"WHERE id_mot_cle = $idMotCleP ".
' -- '.__FILE__.':'.__LINE__;
 
$renommage = Cel::db()->executer($requete);
$this->renommerChemin($ancienChemin, $nouveauChemin, $id_utilisateur);
 
$idsElementsLies = $this->obtenirIdElementsLiesPourIds(array($id_mot_cle));
foreach ($idsElementsLies as $idElementLie) {
self::regenererIndexTexteMotCle($idElementLie['id_element_lie'], $this->mode);
}
 
return $renommage;
}
 
/**
* Si aucun id_père n'est mentionné, c'est un déplacement vers la racine de l'arbre (qui n'existe pas).
*/
public function deplacerMotCle($id_mot_cle, $id_pere, $id_utilisateur) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$idPereP = Cel::db()->proteger($id_pere);
$cheminPere = '';
 
if ($id_pere != '') {
$requete = 'SELECT chemin '.
"FROM {$this->table_mots_cles} ".
"WHERE id_mot_cle = $idPereP ".
' -- '.__FILE__.':'.__LINE__;
 
$cheminPereInfos = Cel::db()->requeter($requete);
if (!empty($cheminPereInfos)) {
$cheminPere = $cheminPereInfos[0]['chemin'];
}
}
 
$requete = 'SELECT chemin, mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE id_mot_cle = $idMotCleP ".
' -- '.__FILE__.':'.__LINE__;
$infosMotCle = Cel::db()->requeter($requete);
$ancienChemin = $infosMotCle[0]['chemin'];
$ancienMotCle = $infosMotCle[0]['mot_cle'];
 
$nouveauChemin = $cheminPere.'/'.$ancienMotCle;
 
return $this->renommerChemin($ancienChemin, $nouveauChemin, $id_utilisateur);
}
 
public function renommerChemin($ancien_chemin, $nouveau_chemin, $id_utilisateur) {
$ancienCheminHarmonise = self::harmoniserChemin($ancien_chemin);
$nouveauCheminHarmonise = self::harmoniserChemin($nouveau_chemin);
 
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$ancienCheminP = Cel::db()->proteger($ancienCheminHarmonise);
$nouveauCheminP = Cel::db()->proteger($nouveauCheminHarmonise);
$conditionCheminP = Cel::db()->proteger($ancienCheminHarmonise.'%');
 
$requete = "UPDATE {$this->table_mots_cles} ".
"SET chemin = REPLACE(chemin, $ancienCheminP, $nouveauCheminP) ".
"WHERE chemin LIKE $conditionCheminP ".
"AND id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
 
return Cel::db()->executer($requete);
}
 
public function supprimerChemin($chemin, $id_utilisateur) {
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$cheminP = Cel::db()->proteger($chemin.'%');
// TODO : triggers pour les tables liées ?
$requete = "DELETE FROM {$this->$table_mots_cles} ".
"WHERE chemin LIKE $cheminP ".
"AND id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
 
return Cel::db()->executer($requete);
}
 
/**
* suppression des associations du mots clé aux images ou obs, mais aussi des associations de ses enfants
* (car ceux-ci seront supprimés aussi dans le processus)
* même s'il n'a pas d'enfants, le tableau contient au moins l'id du mot clé lui même
*/
public function supprimerMotCleParId($id_mot_cle, $id_utilisateur) {
//TODO: simplifier cette fonction
$ids_mot_cle_et_enfants = $this->obtenirIdsMotsClesParIdParent($id_utilisateur, $id_mot_cle);
 
// obtention des ids des éléments liés au mot clé ainsi qu'à ces enfants (afin de pouvoir
// régénérer les index texte de mots clés sur les éléments liés)
$ids_a_delier = array();
foreach ($ids_mot_cle_et_enfants as $id) {
$ids_a_delier[] = $id['id_mot_cle'];
}
 
$ids_elements_lies = $this->obtenirIdElementsLiesPourIds($ids_a_delier);
$suppression_liaison = $this->supprimerToutesLiaisonsIdsMotsCles($ids_a_delier, $id_utilisateur);
 
foreach ($ids_elements_lies as $id_element_lie) {
self::regenererIndexTexteMotCle($id_element_lie['id_element_lie'], $this->mode);
}
 
// suppression du mot clé proprement dit ainsi que de ses enfants
$suppression = $this->supprimerMotCleEtEnfantsParId($id_mot_cle, $id_utilisateur);
 
return $suppression && $suppression_liaison;
}
 
public function supprimerMotCleEtEnfantsParId($id_mot_cle, $id_utilisateur) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$requete = 'SELECT chemin '.
"FROM {$this->table_mots_cles} ".
"WHERE id_mot_cle = $idMotCleP ".
' -- '.__FILE__.':'.__LINE__;
$chemin = Cel::db()->requeter($requete);
 
$suppression = true;
// vérification pour empecher la suppression accidentelle de tout l'arbre,
// cas qui ne devrait jamais arriver normalement
if (!empty($chemin) && $chemin != '/') {
$chemin = $chemin[0]['chemin'];
$cheminP = Cel::db()->proteger($chemin.'%');
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$requete = "DELETE FROM {$this->table_mots_cles} ".
"WHERE chemin LIKE $cheminP ".
"AND id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;;
 
$suppression = Cel::db()->executer($requete);
}
 
return ($suppression !== false);
 
}
 
public function obtenirIdsMotClesPourMotsCles($mots_cles, $id_utilisateur) {
$motsClesP = array();
foreach ($mots_cles as $mot_cle) {
$motsClesP[] = Cel::db()->proteger(self::simplifier($mot_cle));
}
$listeMotsClesP = implode(',', $motsClesP);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$requete = 'SELECT id_mot_cle, mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE mot_cle IN ($listeMotsClesP) ".
"AND id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
 
$resultat = Cel::db()->executer($requete);
return $resultat;
}
 
public function obtenirIdElementsLiesPourChemins($chemins, $id_utilisateur) {
foreach ($chemins as &$chemin) {
$chemin = Cel::db()->proteger(self::harmoniserChemin($chemin));
}
$listeChemins = implode(',', $chemin);
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$requete = 'SELECT id_element_lie '.
"FROM {$this->table_liaison} AS cl INNER JOIN {$this->table_mots_cles} AS cm ".
"ON (".
"cm.id_mot_cle = cl.id_mot_cle ".
"AND chemin IN ($listeChemins) ".
"AND cm.id_utilisateur = $idUtilisateurP ".
") ".
' -- '.__FILE__.':'.__LINE__;
 
$idsPourChemin = Cel::db()->executer($requete);
 
return $idsPourChemin;
}
 
public function obtenirIdElementsLiesPourIds($ids_mots_cles) {
$idsElementsLies = array();
if (!empty($ids_mots_cles)) {
$idsMotsClesP = Cel::db()->proteger($ids_mots_cles);
$listeIdsMotsCles = implode(',', $idsMotsClesP);
 
$requete = 'SELECT id_element_lie '.
"FROM {$this->table_liaison} ".
"WHERE id_mot_cle IN ($listeIdsMotsCles) ".
' -- '.__FILE__.':'.__LINE__;
 
 
$idsElementsLies = Cel::db()->requeter($requete);
}
return $idsElementsLies;
}
 
/**
*
* Fonctions statiques utilitaires
* (Dans l'idéal toute la classe pourrait être statique car elle n'a
* pas d'état (mais il faudrait passer $mode à toutes les fonctions)
*
*/
public static function getTablesMotsClesEtLiaisons($mode) {
if ($mode == 'obs') {
$table_liaison = 'cel_mots_cles_obs_liaison';
$table_mots_cles = 'cel_arbre_mots_cles_obs';
} else {
$table_liaison = 'cel_mots_cles_images_liaison';
$table_mots_cles = 'cel_arbre_mots_cles_images';
}
return array($table_liaison, $table_mots_cles);
}
 
public static function regenererIndexTexteMotCle($id_element_lie, $mode) {
$idElementLieP = Cel::db()->proteger($id_element_lie);
$sqlTpl = self::obtenirTemplateRequeteMotsClesTexte($mode);
$sousRequete = sprintf($sqlTpl, $idElementLieP);
 
list($table, $champId) = self::getNomTablesEtChampsElementsLies($mode);
$requete = "UPDATE $table ".
"SET mots_cles_texte = ($sousRequete) ".
"WHERE $champId = $idElementLieP ".
' -- '.__FILE__.':'.__LINE__;
 
return Cel::db()->executer($requete);
}
 
private static function getNomTablesEtChampsElementsLies($mode) {
$tables = array();
if ($mode == 'obs') {
$tables = array('cel_obs', 'id_observation');
} else {
$tables = array('cel_images', 'id_image');
}
return $tables;
}
 
/**
* Renvoie un template de requete pour selectionner la concatenation de mots clé
* pour un element donné (utilisable avec sprintf)
*/
public static function obtenirTemplateRequeteMotsClesTexte($mode) {
list($table_liaison, $table_mots_cles) = self::getTablesMotsClesEtLiaisons($mode);
 
$requeteTpl = 'SELECT GROUP_CONCAT(mot_cle) '.
"FROM $table_mots_cles AS cm ".
"INNER JOIN $table_liaison AS cml ON cml.id_mot_cle = cm.id_mot_cle ".
'AND cml.id_element_lie = %s ';
 
return $requeteTpl;
}
/**
* Renvoie un template de recherche sur les ids de mots clés utilisables avec sprintf.
*/
public static function obtenirTemplateRequeteMotsClesIds($mode) {
list($table_liaison, $table_mots_cles) = self::getTablesMotsClesEtLiaisons($mode);
$requeteTpl = "SELECT id_element_lie FROM $table_liaison WHERE id_mot_cle IN (%s) ";
return $requeteTpl;
}
 
// Fonctions utilitaires
 
/**
* La profondeur d'un noeud est déterminée par le nombre de slashs
* qu'il contient (étant donné que ceux ci sont interdits dans le texte du mot clé.
*/
static public function comparerProfNoeuds($a, $b) {
$nb_slashs_a = substr_count($a['chemin'], '/');
$nb_slashs_b = substr_count($a['chemin'], '/');
$cmp = 0;
 
if ($nb_slashs_a == $nb_slashs_b) {
$cmp = strcasecmp($a['chemin'], $b['chemin']);
} else {
$cmp = ($a['chemin'] > $b['chemin']) ? +1 : -1;
}
return $cmp;
}
 
static public function getCheminHarmonise($chemin_parent, $mot_cle) {
return self::harmoniserChemin($chemin_parent.'/'.self::simplifier($mot_cle).'/');
}
 
static public function harmoniserChemin($chemin) {
$chemin = self::startsWith($chemin,'/') ? $chemin : '/'.$chemin;
$chemin = self::endsWith($chemin,'/') ? $chemin : $chemin.'/';
$chemin = str_replace('//', '/', $chemin);
// mise en minuscule du chemin afin d'éviter des cas où l'on aurait
// des même mots clés avec minuscule et majuscule
$chemin = strtolower($chemin);
$chemin = self::supprimerAccents($chemin);
return $chemin;
}
function supprimerAccents($str, $charset='utf-8')
{
$str = htmlentities($str, ENT_NOQUOTES, $charset);
$str = preg_replace('#&([A-za-z])(?:acute|cedil|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $str);
$str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $str); // pour les ligatures e.g. '&oelig;'
$str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères
return $str;
}
 
/**
* Fonction de slugification du mot clé
*
* Ni slashes ou antislashes ou virgules (ce qui fausserait l'arbre ou bien les mots
* clés texte dans les tables obs ou image)
*/
static public function simplifier($text) {
$caracteresASupprimer = array('\\','/', ',');
$text = str_replace($caracteresASupprimer, '', $text);
$text = trim($text);
return $text;
}
 
/**
* Gardée pour compatibilité ancienne version (mais devrait être supprimée
* dans le futur
*/
static function nettoyerMotsClesAvantSuppression($chaine) {
$valeur = str_replace('null', '', $chaine);
$valeur = trim($valeur, ';;');
return $valeur;
}
 
static public function startsWith($haystack, $needle) {
return $needle === '' || strpos($haystack, $needle) === 0;
}
 
static public function endsWith($haystack, $needle) {
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
}
 
/**
* Fonction utilisée pour importer les anciens mots clés saisis dans les widget dans un compte identifié
* Dans ce cas là, le widget remplit la case id_utilisateur par le mail indiqué lors de la saisie
* @param string $mail_utilisateur
* @param string $id_utilisateur
*/
public static function migrerMotsClesMailVersId($mail_utilisateur, $infos_utilisateur) {
return self::migrerLiaisonEtMotsCles($mail_utilisateur, $infos_utilisateur, 'obs') &&
self::migrerLiaisonEtMotsCles($mail_utilisateur, $infos_utilisateur, 'images');
}
 
/**
* ATTENTION : cette fonction suppose que l'utilisateur n'ai pas déjà de mots clés dans le CEL
* avec l'identifiant $id_utilisateur ce qui est normalement le cas
* ça devrait normalement marcher correctement même s'il en a déjà mais ça n'a pas été testé
*/
private static function migrerLiaisonEtMotsCles($email_utilisateur, $infos_utilisateur, $mode) {
list($table_liaisons, $table_mots_cles) = self::getTablesMotsClesEtLiaisons($mode);
$idUtilisateurP = Cel::db()->proteger($infos_utilisateur['id_utilisateur']);
$emailUtilisateurP = Cel::db()->proteger($email_utilisateur);
 
$requete_migration_mc = "UPDATE {$table_mots_cles} ".
"SET id_utilisateur = $idUtilisateurP ".
"WHERE id_utilisateur = $emailUtilisateurP ";
 
$migration = Cel::db()->executer($requete_migration_mc);
$migration = ($migration !== false) ? true : false;
return $migration;
}
}
?>
Property changes:
Added: svn:mergeinfo
Merged /branches/v2.0-elagueuse/jrest/lib/GestionMotsClesChemin.php:r2099
Merged /branches/v2.4-fourche/jrest/lib/GestionMotsClesChemin.php:r2313
Merged /branches/topic-dbsingleton/jrest/lib/GestionMotsClesChemin.php:r1720-1764
Merged /branches/v1.8-debroussailleuse/jrest/lib/GestionMotsClesChemin.php:r1981,1987,1992
Merged /branches/v2.2-faucille/jrest/lib/GestionMotsClesChemin.php:r2185-2186,2197-2198
Merged /branches/v1.7-croissant/jrest/lib/GestionMotsClesChemin.php:r1855,1879-1880,1885-1886,1917,1923,1983
/trunk/jrest/bibliotheque/ExtracteurMetadonnees.php
New file
0,0 → 1,652
<?php
/**
* Classe d'extraction de metadonnées afin de les mettre dans
* un tableau au format du cel
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version $Id$
* @copyright © 2012, Tela Botanica
*/
class ExtracteurMetadonnees {
 
private $meta = array();
private $tableau_ids_tags_exif = array(
'InteropIndex' => '1',
'InteropVersion' => '2',
'ProcessingSoftware' => '11',
'SubfileType' => '254',
'OldSubfileType' => '255',
'ImageWidth' => '256',
'ImageHeight' => '257',
'BitsPerSample' => '258',
'Compression' => '259',
'PhotometricInterpretation' => '262',
'Thresholding' => '263',
'CellWidth' => '264',
'CellLength' => '265',
'FillOrder' => '266',
'DocumentName' => '269',
'ImageDescription' => '270',
'Make' => '271',
'Model' => '272',
'StripOffsets' => '273',
'Orientation' => '274',
'SamplesPerPixel' => '277',
'RowsPerStrip' => '278',
'StripByteCounts' => '279',
'MinSampleValue' => '280',
'MaxSampleValue' => '281',
'XResolution' => '282',
'YResolution' => '283',
'PlanarConfiguration' => '284',
'PageName' => '285',
'XPosition' => '286',
'YPosition' => '287',
'FreeOffsets' => '288',
'FreeByteCounts' => '289',
'GrayResponseUnit' => '290',
'GrayResponseCurve' => '291',
'T4Options' => '292',
'T6Options' => '293',
'ResolutionUnit' => '296',
'PageNumber' => '297',
'ColorResponseUnit' => '300',
'TransferFunction' => '301',
'Software' => '305',
'ModifyDate' => '306',
'Artist' => '315',
'HostComputer' => '316',
'Predictor' => '317',
'WhitePoint' => '318',
'PrimaryChromaticities' => '319',
'ColorMap' => '320',
'HalftoneHints' => '321',
'TileWidth' => '322',
'TileLength' => '323',
'TileOffsets' => '324',
'TileByteCounts' => '325',
'BadFaxLines' => '326',
'CleanFaxData' => '327',
'ConsecutiveBadFaxLines' => '328',
'SubIFD' => '330',
'InkSet' => '332',
'InkNames' => '333',
'NumberofInks' => '334',
'DotRange' => '336',
'TargetPrinter' => '337',
'ExtraSamples' => '338',
'SampleFormat' => '339',
'SMinSampleValue' => '340',
'SMaxSampleValue' => '341',
'TransferRange' => '342',
'ClipPath' => '343',
'XClipPathUnits' => '344',
'YClipPathUnits' => '345',
'Indexed' => '346',
'JPEGTables' => '347',
'OPIProxy' => '351',
'GlobalParametersIFD' => '400',
'ProfileType' => '401',
'FaxProfile' => '402',
'CodingMethods' => '403',
'VersionYear' => '404',
'ModeNumber' => '405',
'Decode' => '433',
'DefaultImageColor' => '434',
'T82Options' => '435',
'JPEGTables' => '437',
'JPEGProc' => '512',
'ThumbnailOffset' => '513',
'ThumbnailLength' => '514',
'JPEGRestartInterval' => '515',
'JPEGLosslessPredictors' => '517',
'JPEGPointTransforms' => '518',
'JPEGQTables' => '519',
'JPEGDCTables' => '520',
'JPEGACTables' => '521',
'YCbCrCoefficients' => '529',
'YCbCrSubSampling' => '530',
'YCbCrPositioning' => '531',
'ReferenceBlackWhite' => '532',
'StripRowCounts' => '559',
'ApplicationNotes' => '700',
'USPTOMiscellaneous' => '999',
'RelatedImageFileFormat' => '4096',
'RelatedImageWidth' => '4097',
'RelatedImageHeight' => '4098',
'Rating' => '18246',
'XP_DIP_XML' => '18247',
'StitchInfo' => '18248',
'RatingPercent' => '18249',
'ImageID' => '32781',
'WangTag1' => '32931',
'WangAnnotation' => '32932',
'WangTag3' => '32933',
'WangTag4' => '32934',
'Matteing' => '32995',
'DataType' => '32996',
'ImageDepth' => '32997',
'TileDepth' => '32998',
'Model2' => '33405',
'CFARepeatPatternDim' => '33421',
'CFAPattern2' => '33422',
'BatteryLevel' => '33423',
'KodakIFD' => '33424',
'Copyright' => '33432',
'ExposureTime' => '33434',
'FNumber' => '33437',
'MDFileTag' => '33445',
'MDScalePixel' => '33446',
'MDColorTable' => '33447',
'MDLabName' => '33448',
'MDSampleInfo' => '33449',
'MDPrepDate' => '33450',
'MDPrepTime' => '33451',
'MDFileUnits' => '33452',
'PixelScale' => '33550',
'AdventScale' => '33589',
'AdventRevision' => '33590',
'UIC1Tag' => '33628',
'UIC2Tag' => '33629',
'UIC3Tag' => '33630',
'UIC4Tag' => '33631',
'IPTC-NAA' => '33723',
'IntergraphPacketData' => '33918',
'IntergraphFlagRegisters' => '33919',
'IntergraphMatrix' => '33920',
'INGRReserved' => '33921',
'ModelTiePoint' => '33922',
'Site' => '34016',
'ColorSequence' => '34017',
'IT8Header' => '34018',
'RasterPadding' => '34019',
'BitsPerRunLength' => '34020',
'BitsPerExtendedRunLength' => '34021',
'ColorTable' => '34022',
'ImageColorIndicator' => '34023',
'BackgroundColorIndicator' => '34024',
'ImageColorValue' => '34025',
'BackgroundColorValue' => '34026',
'PixelIntensityRange' => '34027',
'TransparencyIndicator' => '34028',
'ColorCharacterization' => '34029',
'HCUsage' => '34030',
'TrapIndicator' => '34031',
'CMYKEquivalent' => '34032',
'SEMInfo' => '34118',
'AFCP_IPTC' => '34152',
'PixelMagicJBIGOptions' => '34232',
'ModelTransform' => '34264',
'WB_GRGBLevels' => '34306',
'LeafData' => '34310',
'PhotoshopSettings' => '34377',
'ExifOffset' => '34665',
'ICC_Profile' => '34675',
'TIFF_FXExtensions' => '34687',
'MultiProfiles' => '34688',
'SharedData' => '34689',
'T88Options' => '34690',
'ImageLayer' => '34732',
'GeoTiffDirectory' => '34735',
'GeoTiffDoubleParams' => '34736',
'GeoTiffAsciiParams' => '34737',
'ExposureProgram' => '34850',
'SpectralSensitivity' => '34852',
'GPSInfo' => '34853',
'ISO' => '34855',
'Opto-ElectricConvFactor' => '34856',
'Interlace' => '34857',
'TimeZoneOffset' => '34858',
'SelfTimerMode' => '34859',
'SensitivityType' => '34864',
'StandardOutputSensitivity' => '34865',
'RecommendedExposureIndex' => '34866',
'ISOSpeed' => '34867',
'ISOSpeedLatitudeyyy' => '34868',
'ISOSpeedLatitudezzz' => '34869',
'FaxRecvParams' => '34908',
'FaxSubAddress' => '34909',
'FaxRecvTime' => '34910',
'LeafSubIFD' => '34954',
'ExifVersion' => '36864',
'DateTimeOriginal' => '36867',
'CreateDate' => '36868',
'ComponentsConfiguration' => '37121',
'CompressedBitsPerPixel' => '37122',
'ShutterSpeedValue' => '37377',
'ApertureValue' => '37378',
'BrightnessValue' => '37379',
'ExposureCompensation' => '37380',
'MaxApertureValue' => '37381',
'SubjectDistance' => '37382',
'MeteringMode' => '37383',
'LightSource' => '37384',
'Flash' => '37385',
'FocalLength' => '37386',
'FlashEnergy' => '37387',
'SpatialFrequencyResponse' => '37388',
'Noise' => '37389',
'FocalPlaneXResolution' => '37390',
'FocalPlaneYResolution' => '37391',
'FocalPlaneResolutionUnit' => '37392',
'ImageNumber' => '37393',
'SecurityClassification' => '37394',
'ImageHistory' => '37395',
'SubjectArea' => '37396',
'ExposureIndex' => '37397',
'TIFF-EPStandardID' => '37398',
'SensingMethod' => '37399',
'CIP3DataFile' => '37434',
'CIP3Sheet' => '37435',
'CIP3Side' => '37436',
'StoNits' => '37439',
'MakerNoteCanon' => '37500',
'UserComment' => '37510',
'SubSecTime' => '37520',
'SubSecTimeOriginal' => '37521',
'SubSecTimeDigitized' => '37522',
'MSDocumentText' => '37679',
'MSPropertySetStorage' => '37680',
'MSDocumentTextPosition' => '37681',
'ImageSourceData' => '37724',
'XPTitle' => '40091',
'XPComment' => '40092',
'XPAuthor' => '40093',
'XPKeywords' => '40094',
'XPSubject' => '40095',
'FlashpixVersion' => '40960',
'ColorSpace' => '40961',
'ExifImageWidth' => '40962',
'ExifImageHeight' => '40963',
'RelatedSoundFile' => '40964',
'InteropOffset' => '40965',
'FlashEnergy' => '41483',
'SpatialFrequencyResponse' => '41484',
'Noise' => '41485',
'FocalPlaneXResolution' => '41486',
'FocalPlaneYResolution' => '41487',
'FocalPlaneResolutionUnit' => '41488',
'ImageNumber' => '41489',
'SecurityClassification' => '41490',
'ImageHistory' => '41491',
'SubjectLocation' => '41492',
'ExposureIndex' => '41493',
'TIFF-EPStandardID' => '41494',
'SensingMethod' => '41495',
'FileSource' => '41728',
'SceneType' => '41729',
'CFAPattern' => '41730',
'CustomRendered' => '41985',
'ExposureMode' => '41986',
'WhiteBalance' => '41987',
'DigitalZoomRatio' => '41988',
'FocalLengthIn35mmFormat' => '41989',
'SceneCaptureType' => '41990',
'GainControl' => '41991',
'Contrast' => '41992',
'Saturation' => '41993',
'Sharpness' => '41994',
'DeviceSettingDescription' => '41995',
'SubjectDistanceRange' => '41996',
'ImageUniqueID' => '42016',
'OwnerName' => '42032',
'SerialNumber' => '42033',
'LensInfo' => '42034',
'LensMake' => '42035',
'LensModel' => '42036',
'LensSerialNumber' => '42037',
'GDALMetadata' => '42112',
'GDALNoData' => '42113',
'Gamma' => '42240',
'ExpandSoftware' => '44992',
'ExpandLens' => '44993',
'ExpandFilm' => '44994',
'ExpandFilterLens' => '44995',
'ExpandScanner' => '44996',
'ExpandFlashLamp' => '44997',
'PixelFormat' => '48129',
'Transformation' => '48130',
'Uncompressed' => '48131',
'ImageType' => '48132',
'ImageWidth' => '48256',
'ImageHeight' => '48257',
'WidthResolution' => '48258',
'HeightResolution' => '48259',
'ImageOffset' => '48320',
'ImageByteCount' => '48321',
'AlphaOffset' => '48322',
'AlphaByteCount' => '48323',
'ImageDataDiscard' => '48324',
'AlphaDataDiscard' => '48325',
'OceScanjobDesc' => '50215',
'OceApplicationSelector' => '50216',
'OceIDNumber' => '50217',
'OceImageLogic' => '50218',
'Annotations' => '50255',
'PrintIM' => '50341',
'USPTOOriginalContentType' => '50560',
'DNGVersion' => '50706',
'DNGBackwardVersion' => '50707',
'UniqueCameraModel' => '50708',
'LocalizedCameraModel' => '50709',
'CFAPlaneColor' => '50710',
'CFALayout' => '50711',
'LinearizationTable' => '50712',
'BlackLevelRepeatDim' => '50713',
'BlackLevel' => '50714',
'BlackLevelDeltaH' => '50715',
'BlackLevelDeltaV' => '50716',
'WhiteLevel' => '50717',
'DefaultScale' => '50718',
'DefaultCropOrigin' => '50719',
'DefaultCropSize' => '50720',
'ColorMatrix1' => '50721',
'ColorMatrix2' => '50722',
'CameraCalibration1' => '50723',
'CameraCalibration2' => '50724',
'ReductionMatrix1' => '50725',
'ReductionMatrix2' => '50726',
'AnalogBalance' => '50727',
'AsShotNeutral' => '50728',
'AsShotWhiteXY' => '50729',
'BaselineExposure' => '50730',
'BaselineNoise' => '50731',
'BaselineSharpness' => '50732',
'BayerGreenSplit' => '50733',
'LinearResponseLimit' => '50734',
'CameraSerialNumber' => '50735',
'DNGLensInfo' => '50736',
'ChromaBlurRadius' => '50737',
'AntiAliasStrength' => '50738',
'ShadowScale' => '50739',
'SR2Private' => '50740',
'MakerNoteSafety' => '50741',
'RawImageSegmentation' => '50752',
'CalibrationIlluminant1' => '50778',
'CalibrationIlluminant2' => '50779',
'BestQualityScale' => '50780',
'RawDataUniqueID' => '50781',
'AliasLayerMetadata' => '50784',
'OriginalRawFileName' => '50827',
'OriginalRawFileData' => '50828',
'ActiveArea' => '50829',
'MaskedAreas' => '50830',
'AsShotICCProfile' => '50831',
'AsShotPreProfileMatrix' => '50832',
'CurrentICCProfile' => '50833',
'CurrentPreProfileMatrix' => '50834',
'ColorimetricReference' => '50879',
'PanasonicTitle' => '50898',
'PanasonicTitle2' => '50899',
'CameraCalibrationSig' => '50931',
'ProfileCalibrationSig' => '50932',
'ProfileIFD' => '50933',
'AsShotProfileName' => '50934',
'NoiseReductionApplied' => '50935',
'ProfileName' => '50936',
'ProfileHueSatMapDims' => '50937',
'ProfileHueSatMapData1' => '50938',
'ProfileHueSatMapData2' => '50939',
'ProfileToneCurve' => '50940',
'ProfileEmbedPolicy' => '50941',
'ProfileCopyright' => '50942',
'ForwardMatrix1' => '50964',
'ForwardMatrix2' => '50965',
'PreviewApplicationName' => '50966',
'PreviewApplicationVersion' => '50967',
'PreviewSettingsName' => '50968',
'PreviewSettingsDigest' => '50969',
'PreviewColorSpace' => '50970',
'PreviewDateTime' => '50971',
'RawImageDigest' => '50972',
'OriginalRawFileDigest' => '50973',
'SubTileBlockSize' => '50974',
'RowInterleaveFactor' => '50975',
'ProfileLookTableDims' => '50981',
'ProfileLookTableData' => '50982',
'OpcodeList1' => '51008',
'OpcodeList2' => '51009',
'OpcodeList3' => '51022',
'NoiseProfile' => '51041',
'Padding' => '59932',
'OffsetSchema' => '59933',
'OwnerName' => '65000',
'SerialNumber' => '65001',
'Lens' => '65002',
'KDC_IFD' => '65024',
'RawFile' => '65100',
'Converter' => '65101',
'WhiteBalance' => '65102',
'Exposure' => '65105',
'Shadows' => '65106',
'Brightness' => '65107',
'Contrast' => '65108',
'Saturation' => '65109',
'Sharpness' => '65110',
'Smoothness' => '65111',
'MoireFilter' => '65112',
);
 
public function extraireMetadonnees($cheminImage) {
if ($this->peutUtiliserExifTool()) {
$this->meta = $this->decoderMetadonneesExifTool($cheminImage);
} else {
$this->meta = $this->decoderMetadonneesBasique($cheminImage);
}
 
$metadonnees = array();
$metadonnees['hauteur'] = $this->obtenirHauteur();
$metadonnees['largeur'] = $this->obtenirLargeur();
$metadonnees['date_prise_de_vue'] = $this->obtenirDatePriseDeVue();
$metadonnees['appareil_fabriquant'] = $this->obtenirAppareilFabricant();
$metadonnees['appareil_modele'] = $this->obtenirAppareilModele();
$metadonnees['meta_exif'] = $this->convertirMetaVersXML('EXIF');
$metadonnees['meta_iptc'] = $this->convertirMetaVersXML('IPTC');
$metadonnees['meta_xmp'] = $this->convertirMetaVersXML('XMP');
$metadonnees['meta_makernote'] = $this->convertirMetaVersXML('MAKERNOTE');
return $metadonnees;
}
 
private function peutUtiliserExifTool() {
// TODO indiquer ceci dans un fichier de config
return file_exists('/usr/bin/exiftool') && is_executable('/usr/bin/exiftool');
}
 
private function decoderMetadonneesExifTool($cheminImage) {
$metadata = array();
$res = exec('/usr/bin/exiftool -g -D "'.$cheminImage.'"', $metadata);
 
$metadata_decodees = array();
$categorie = '';
foreach($metadata as &$data) {
if ($this->estUnSeparateurCategorieExifTool($data)) {
$categorie = trim(str_replace('----', '', $data));
} else {
$data_decodee = $this->parserValeurMetadonneeExifTool($data);
$cle_metadonnee = str_replace(' ', '', $data_decodee['cle']);
$metadata_decodees[$categorie][$cle_metadonnee] = $data_decodee;
}
}
return $metadata_decodees;
}
 
private function estUnSeparateurCategorieExifTool($data) {
return preg_match('^---- (.)* ----^',$data);
}
 
private function parserValeurMetadonneeExifTool($data) {
$cle_valeur = explode(':',$data,2);
 
$valeur = '';
if(count($cle_valeur) == 2) {
$valeur = trim($cle_valeur[1]);
}
 
$id_cle = explode(' ',trim($cle_valeur[0]),2);
 
$id_cle[1] = str_replace(array('-','/'),'',$id_cle[1]);
 
$cle_id_valeur = array('cle' => $id_cle[1], 'id' => str_replace('-','',$id_cle[0]), 'valeur' => $valeur);
return $cle_id_valeur;
}
 
public function decoderMetadonneesBasique($chemin_fichier) {
$exif = @exif_read_data($chemin_fichier, "EXIF,COMPUTED,IFD0,FILE,COMMENT", true, false);
 
// tant pis pour les makernote et xmp, les décoder demande trop de librairies externes, autant installer exiftool alors
$metadonnees = array();
$metadonnees['XMP'] = array();
unset($metadonnees['EXIF']['MakerNote']);
$metadonnees['MAKERNOTE'] = array();
$metadonnees_non_formatees = array();
if(isset($exif['EXIF'])) {
$metadonnees_non_formatees = array_merge($metadonnees_non_formatees, $exif['EXIF']);
}
if(isset($exif['IFD0'])) {
$metadonnees_non_formatees = array_merge($metadonnees_non_formatees, $exif['IFD0']);
}
$metadonnees['EXIF'] = $this->formaterTableauExif($metadonnees_non_formatees);
$metadonnees['IPTC'] = $this->extraireIptc($chemin_fichier);
$metadonnees['File'] = array(
'ImageWidth' => array('id' => '', 'valeur' => $exif['COMPUTED']['Width']),
'ImageHeight' => array('id' => '', 'valeur' => $exif['COMPUTED']['Height']));
return $metadonnees ;
}
 
private function formaterTableauExif(&$tableau) {
$tableau_exif_formate = array();
 
foreach ($tableau as $nom_tag => $valeur) {
$id = '';
if (isset($this->tableau_ids_tags_exif[$nom_tag])) {
$id = $this->tableau_ids_tags_exif[$nom_tag];
}
$tableau_exif_formate[$nom_tag] = array('id' => $id, 'valeur' => $valeur);
}
 
return $tableau_exif_formate;
}
 
/**
* Extraction des metadonnées iptc
**/
public function extraireIptc($chemin_fichier) {
$meta = array();
 
// getimagesize renvoie les infos iptc dans le tableau info
$info = array();
$size = getimagesize($chemin_fichier, $info);
 
// s'il existe
if (isset($info["APP13"])) {
// on parse les donnees
$iptc = iptcparse($info["APP13"]);
if ($iptc) {
// et on les analyse
foreach ($iptc as $marker => $section) {
foreach ($section as $nom => $val) {
// pour remplir le tableau de donnees
$this->decoderValeurIptc($marker, $val, $meta);
}
}
}
}
 
return $meta;
}
 
/**
* Stocke une valeur de metadonnées iptc dans le champ du tableau correspondant
* @param String $nom nom de la valeur
* @param String $val valeur
* @param String $data référence vers le tableau où la donnée sera stockée
**/
private function decoderValeurIptc($nom, $val, &$data_tab) {
switch ($nom) {
case "2#005" :// mots cles iptc
$data_tab['Category'] = array('id' => '5', 'valeur' => $val);
break;
case "2#080" :// champ by line
$data_tab['By-Line'] = array('id' => '80', 'valeur' => $val);
break ;
case "2#085" :// champ by line titre
$data_tab['By-LineTitle'] = array('id' => '85', 'valeur' => $val);
break ;
case "2#090" :// ville
$data_tab['City'] = array('id' => '90', 'valeur' => $val);
break ;
case "2#092" :// sous location
$data_tab['SubLocation'] = array('id' => '92', 'valeur' => $val);
break ;
case "2#095" :// etat (pour les us)
$data_tab['ProvinceState'] = array('id' => '95', 'valeur' => $val);
break ;
case "2#100" :// code pays
$data_tab['CountryPrimaryLocationCode'] = array('id' => '100', 'valeur' => $val);
break ;
case "2#101" :// code pays
$data_tab['CountryName'] = array('id' => '101', 'valeur' => $val);
break ;
case "2#105" :// titre principal
$data_tab['Headline'] = array('id' => '105', 'valeur' => $val);
break ;
case "2#110" :// credit
$data_tab['Credit'] = array('id' => '110', 'valeur' => $val);
break ;
case "2#116" :// copyright
$data_tab['CopyrightNotice'] = array('id' => '116', 'valeur' => $val);
break ;
case "2#118" :// contact
$data_tab['Contact'] = array('id' => '118', 'valeur' => $val);
break ;
default:
unset($data_tab['nom']);
}
}
 
private function obtenirHauteur() {
$hauteur = isset($this->meta['File']['ImageHeight']) ? $this->meta['File']['ImageHeight']['valeur'] : '';
return $hauteur;
}
 
private function obtenirLargeur() {
$largeur = isset($this->meta['File']['ImageWidth']) ? $this->meta['File']['ImageWidth']['valeur'] : '';
return $largeur;
}
 
private function obtenirDatePriseDeVue() {
$date = isset($this->meta['EXIF']['DateTimeOriginal']) ? $this->meta['EXIF']['DateTimeOriginal']['valeur'] : '';
return $date;
}
 
private function obtenirAppareilFabricant() {
$fabriquant = isset($this->meta['EXIF']['Make']) ? $this->meta['EXIF']['Make']['valeur'] : '';
return $fabriquant;
}
 
private function obtenirAppareilModele() {
$modele = isset($this->meta['EXIF']['CameraModelName']) ? $this->meta['EXIF']['CameraModelName']['valeur'] : '';
return $modele;
}
 
private function convertirMetaVersXML($type) {
$xml = null;
if (isset($this->meta[$type])) {
$racine = strtolower($type);
 
$xml = '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
$xml .= "<$racine>"."\n";
foreach ($this->meta[$type] as $prop => &$valeur) {
$xml .= '<'.$prop.' id="'.$valeur['id'].'">'.$valeur['valeur'].'</'.$prop.'>'."\n";
}
$xml .= "</$racine>";
}
return $xml;
}
}
?>
/trunk/jrest/bibliotheque/Bdd2.php
New file
0,0 → 1,136
<?php
/**
* La classe de gestion de la base de données.
*
* @category php 5.2
* @package cel
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
*/
class Bdd2 extends PDO {
 
const SQL_MODE_ASSOC = PDO::FETCH_ASSOC;
const SQL_MODE_OBJET = PDO::FETCH_OBJ;
const SQL_RETOUR_COMPLET = 'All';
const SQL_RETOUR_LIGNE = 'Row';
const SQL_RETOUR_COLONNE = 'Column';
const SQL_RETOUR_BRUT = 'Raw';
 
public function __construct($config, $base = 'database_cel') {
$cfg = $config[$base];
// ATTENTION : la connexin à la bdd peut échouer si l'host vaut localhost. Utiliser 127.0.0.1 à la place.
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
try {
// Création de la connexion en UTF-8 à la BDD
parent::__construct($dsn, $cfg['username'], $cfg['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affichée)
parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'La connexion à la base de donnée via PDO a échoué : ' .$dsn . "\n". $e->getMessage();
}
}
 
/**
* Protège automatiquement toutes les chaines comprises entre deux caractères '|'.
* Puis execute la requete.
* @see protegerRequete()
* @param unknown_type $requete
*/
public function requeter($requete, $retour = self::SQL_RETOUR_COMPLET, $mode = PDO::FETCH_ASSOC) {
return $this->executerRequete($requete, $retour, $mode);
}
 
/**
* Execute la requete retournant une seule ligne de résultat.
* @param String $requete
*/
public function requeterLigne($requete, $mode = PDO::FETCH_ASSOC) {
return $this->executerRequete($requete, self::SQL_RETOUR_LIGNE, $mode);
}
 
/**
* Execute la requete retournant une seule colone de résultat.
* @param String $requete
*/
public function requeterValeurUnique($requete, $mode = PDO::FETCH_ASSOC) {
return $this->executerRequete($requete, self::SQL_RETOUR_COLONNE, $mode);
}
 
public function executerRequete($requete, $retour = self::SQL_RETOUR_COMPLET, $mode = PDO::FETCH_ASSOC) {
$resultat = false;
try {
switch ($retour) {
case self::SQL_RETOUR_COMPLET :
$resultat = $this->query($requete)->fetchAll($mode);// Retourne toutes les lignes
break;
case self::SQL_RETOUR_LIGNE :
$resultat = $this->query($requete)->fetch($mode);// Retourne la première ligne
break;
case self::SQL_RETOUR_COLONNE :
$resultat = $this->query($requete)->fetchColumn();// Retourne la première colonne de la première ligne
break;
case self::SQL_RETOUR_BRUT :
$resultat = $this->query($requete);// Retourne l'objet brut pour être utilisé dans une boucle de type foreach
break;
default:
$this->debug[] = "Le type de retour '$retour' est inconnu.";
}
if ($resultat === false) {
$this->debug[] = "La requête a retourné aucun résultat : $requete";
}
} catch (PDOException $e) {
$msgTpl = "Requête echec.\nFichier : %s.\nLigne : %s.\nMessage : %s.\nRequête : %s";
$this->debug[] = sprintf($msgTpl, $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
return $resultat;
}
 
/**
* Execute la requete retournant l'objet brut de résultat pour l'utiliser dans un foreach.
* @param String $requete
*/
public function requeterBrut($requete) {
return $this->executerRequete($requete, self::SQL_RETOUR_BRUT);
}
 
public function executer($requete) {
try {
$resultat = $this->exec($requete);
if ($resultat === false) {
$this->debug[] = "La requête a échoué : $requete";
}
} catch (PDOException $e) {
$message = "Fichier : {$e->getFile()} \nLigne : {$e->getLine()} \nMessage : {$e->getMessage()} \nRequête : $requete";
$code = E_USER_ERROR;
throw new Exception($message, $code);
}
return $resultat;
}
 
public function proteger($donnees) {
if (is_array($donnees)) {
$retour = $this->protegerTableau($donnees);
} else {
$retour = $this->quote($donnees);
}
return $retour;
}
 
private function protegerTableau(Array $tableau) {
foreach ($tableau as $id => $val) {
if (is_array($val)) {
$tableau[$id] = $this->protegerTableau($val);
} else {
$tableau[$id] = $this->proteger($val);
}
}
return $tableau;
}
 
public function obtenirDernierId() {
return $this->lastInsertId();
}
 
}
Property changes:
Added: svn:mergeinfo
Merged /branches/v1.7-croissant/jrest/lib/Bdd2.php:r1855,1879-1880,1885-1886,1917,1923,1983
Merged /branches/v2.0-elagueuse/jrest/lib/Bdd2.php:r2099-2100
Merged /branches/topic-dbsingleton/jrest/lib/Bdd2.php:r1720-1764
Merged /branches/v1.8-debroussailleuse/jrest/lib/Bdd2.php:r1981,1987,1992
/trunk/jrest/bibliotheque/GestionImage.php
New file
0,0 → 1,277
<?php
// ATTENTION ! Classe compatible uniquement avec nouveau format de bdd du cel //
 
/**
* PHP Version 5
*
* @category CEL
* @package jrest
* @author Aurelien Peronnet <aurelien@tela-botanica.org>
* @author Jean-Pascal Milcent <jpm@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/jrest/
*/
 
/**
* Classe de gestion de l'ajout, modification et suppression des images
*
* in=utf8
* out=utf8
*
*/
class GestionImage extends Cel {
 
/**
* Ajoute une image dans la base de données et stocke le fichier en fabriquant les miniatures,
* renvoie le nouvel id d'image en cas de succès
*
* @param int $id_utilisateur identifiant de l'utilisateur
* @param array $infos_fichier les infos sur le fichier à traiter, de la même forme que les
* élements du tableau $_FILES de php
*/
public function ajouterImage($id_utilisateur, $infos_fichier) {
$nouvel_ordre = $this->obtenirNouvelOrdrePourUtilisateur($id_utilisateur);
if (!$nouvel_ordre) {
$message = "Erreur lors du calcul du nouvel ordre de l'image";
$this->logger($message);
}
 
$extracteur_metadonnees = new ExtracteurMetadonnees();
$informations_image = $extracteur_metadonnees->extraireMetadonnees($infos_fichier['tmp_name']) ;
 
if (!$informations_image) {
$message = "Erreur lors de l'extraction des metadonnées";
$this->logger($message);
}
 
// ajout de quelques informations supplémentaire, en sus
// des metadonnées dejà extraites
$informations_image['ordre'] = $nouvel_ordre ;
$informations_image['publiable_eflore'] = 'false' ;
$informations_image['nom_original'] = $infos_fichier['name'] ;
 
// le md5 du fichier sert à repérer les images en doublons
$informations_image['md5'] = md5_file($infos_fichier['tmp_name']) ;
 
$informations_image['ce_utilisateur'] = $id_utilisateur ;
 
$infos_utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
 
$informations_image['courriel_utilisateur'] = $infos_utilisateur['courriel'];
$informations_image['nom_utilisateur'] = $infos_utilisateur['nom'];
$informations_image['prenom_utilisateur'] = $infos_utilisateur['prenom'];
 
$requete_insertion_infos_image = $this->construireRequeteInsertionImage($informations_image);
 
// important ! ne pas utiliser la fonction executerRequete qui renvoie une erreur si la requete contient des | (pipes)
// ce qui est fréquent dans les métadonnées
// TODO: corriger la fonction ou bien continuer à utiliser executerRequeteSimple
$resultat_insertion_infos_image = Cel::db()->executer($requete_insertion_infos_image);
 
if (!$resultat_insertion_infos_image) {
$message = "Echec de l'insertion dans la base de donnees : " ;
$this->logger($message);
}
 
$id_nouvelle_image = $this->obtenirIdImagePourIdentifiantEtOrdre($id_utilisateur, $nouvel_ordre);
 
if (!$id_nouvelle_image) {
$message = "Impossible d'obtenir le nouvel identifiant de l'image";
$this->logger($message);
}
 
$manipulateur_image = new ImageRecreation($this->config);
$fichier_stocke = $manipulateur_image->stockerFichierEtCreerMiniatures($infos_fichier, $id_nouvelle_image);
 
if (!$fichier_stocke) {
$message = "Erreur lors du stockage du fichier";
$this->logger($message);
}
 
return $id_nouvelle_image;
}
 
private function obtenirNouvelOrdrePourUtilisateur($id_utilisateur) {
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$requete = 'SELECT MAX(ordre) AS max_ordre '.
'FROM cel_images '.
"WHERE ce_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.' : '.__LINE__;
$resultat = Cel::db()->requeter($requete);
 
$nouvel_ordre = 0;
if ($resultat !== false) {
$nouvel_ordre = $resultat[0]['max_ordre'];
$nouvel_ordre++;
}
return $nouvel_ordre;
}
 
private function obtenirIdImagePourIdentifiantEtOrdre($id_utilisateur, $ordre) {
$id_image = false;
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$ordreP = Cel::db()->proteger($ordre);
$requete ='SELECT id_image '.
'FROM cel_images '.
"WHERE ce_utilisateur = $idUtilisateurP ".
" AND ordre = $ordreP ".
' -- '.__FILE__.' : '.__LINE__;
$resultat = Cel::db()->requeter($requete);
 
if (count($resultat) > 0) {
$id_image = $resultat[0]['id_image'];
}
return $id_image;
}
 
private function construireRequeteInsertionImage($informations_image) {
$requete_insertion_image = "INSERT INTO cel_images ";
$champs_a_inserer = '' ;
$valeurs_a_inserer = '' ;
foreach ($informations_image as $champ => $valeur) {
$champs_a_inserer .= $champ.',' ;
 
if (is_null($valeur)) {
$valeurs_a_inserer .= 'NULL,' ;
} else {
$valeurs_a_inserer .= Cel::db()->proteger($valeur).',' ;
}
}
 
$champs_a_inserer .= 'date_modification,' ;
$valeurs_a_inserer .= '"0000-00-00 00:00:00",' ;
$champs_a_inserer .= 'date_creation' ;
$valeurs_a_inserer .= 'CURRENT_TIMESTAMP()' ;
$requete_insertion_image .= "($champs_a_inserer) VALUES ($valeurs_a_inserer)" ;
$requete_insertion_image .= ' -- '.__FILE__.' : '.__LINE__;
 
return $requete_insertion_image;
}
 
/**
* Modifie les champs de metadonnées d'une image
*
* @param array $utilisateur identifiant utilisateur
* @param array $id id de l'image
* @param array $parametres un taleau contenant des valeurs indexées par les noms de champs de la bdd
* @return boolean true ou false suivant le succès de l'opération
*/
public function modifierImage($utilisateur, $id_image, $parametres) {
$requete_mise_a_jour_image = 'UPDATE cel_images SET ' ;
$champs_a_mettre_a_jour = $this->construireRequeteMajMetaDonnees($parametres);
$requete_mise_a_jour_image .= $champs_a_mettre_a_jour;
$requete_mise_a_jour_image .= ' WHERE id_image = '.Cel::db()->proteger($id_image).
' AND ce_utilisateur = '.Cel::db()->proteger($utilisateur).
' -- '.__FILE__.' : '.__LINE__;
$resultat_mise_a_jour = Cel::db()->executer($requete_mise_a_jour_image);
return ($resultat_mise_a_jour !== false);
}
 
/**
* Assemble la requete de mise à jour des champs de metadonnées
*
* @param array $valeurs_metadonnees un taleau contenant des valeurs indexées par les noms de champs de la bdd
* @return string une sous chaine sql utilisable dans une requete de type UPPDATE table SET valeur1=champ1 ...
*/
private function construireRequeteMajMetaDonnees($valeurs_metadonnees) {
$requete_maj_champs = '';
$champs_a_ignorer = array('id_image');
foreach ($valeurs_metadonnees as $champ => $valeur) {
if (!in_array($champ,$champs_a_ignorer)) {
if ($champ == 'date_prise_de_vue' && trim($valeur != "")) {
$date_tab = explode('/',$valeur) ;
$date = $date_tab[2].'-'.$date_tab[1].'-'.$date_tab[0] ;
$requete_maj_champs .= $champ.' = '.Cel::db()->proteger($date).' , ' ;
} else {
$requete_maj_champs .= $champ.' = '.Cel::db()->proteger($valeur).' , ' ;
}
}
}
$requete_maj_champs = rtrim($requete_maj_champs, ' , ') ;
return $requete_maj_champs;
}
 
public function supprimerImageParOrdre($id_utilisateur, $ordre_images) {
if (is_array($ordre_images)) {
$ordre_images = Cel::db()->proteger($ordre_images);
$idsImagesP = implode(',', $ordre_images);
} else {
$idsImagesP = Cel::db()->proteger($ordre_images);
}
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
 
$requete = 'SELECT id_image '.
'FROM cel_images '.
"WHERE ce_utilisateur = $idUtilisateurP ".
"AND ordre IN ($idsImagesP) ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultats = Cel::db()->requeter($requete);
 
$idsImages = array();
foreach ($resultats as $id_image) {
$idsImages[] = $id_image['id_image'];
}
 
return $this->supprimerImage($idsImages);
}
 
public function supprimerImage($id_image_ou_tableau) {
$ids_images_non_protegees = array();
if (is_array($id_image_ou_tableau)) {
$ids_images_non_protegees = $id_image_ou_tableau;
$id_image_ou_tableau = Cel::db()->proteger($id_image_ou_tableau);
$chaine_ids_images = implode(',', $id_image_ou_tableau);
} else {
$ids_images_non_protegees[] = $id_image_ou_tableau;
$chaine_ids_images = Cel::db()->proteger($id_image_ou_tableau);
}
 
$requete = 'DELETE FROM cel_images '.
"WHERE id_image in ($chaine_ids_images) ".
' -- '.__FILE__.' : '.__LINE__;
$resultat_suppression_image = Cel::db()->executer($requete);
if ($resultat_suppression_image === false) {
$message = "Erreur lors de la suppression de l'image" ;
$this->logger($message);
}
 
$gestion_mots_cles = new GestionMotsClesChemin($this->config, 'images');
$resultat_suppression_lien_images_mots_cles = $gestion_mots_cles->supprimerToutesLiaisonsPourIdsElementsLies($ids_images_non_protegees);
if (!$resultat_suppression_lien_images_mots_cles === false) {
$message = "Erreur lors de la suppression des mots cles associés à l'image" ;
$this->logger($message);
}
 
$manipulateur_image = new ImageRecreation($this->config);
$tableau_ids_image = explode(',', $chaine_ids_images);
foreach ($tableau_ids_image as $id_image_a_detruire) {
$destruction_fichier_image = $manipulateur_image->detruireImageSurDisque($id_image_a_detruire);
}
return $destruction_fichier_image;
}
 
/**
* Fonction utilisée pour importer les anciennes images saisies dans les widget dans un compte identifié
* Dans ce cas là, le widget remplit la case id_utilisateur par le mail indiqué lors de la saisie
* @param string $mail_utilisateur
* @param string $id_utilisateur
*/
public function migrerImagesMailVersId($mail_utilisateur, $infos_utilisateur) {
// ATTENTION : cette fonction suppose que l'utilisateur n'ai pas déjà d'images dans le CEL
// avec l'identifiant $id_utilisateur ce qui est normalement le cas
$requete = 'UPDATE cel_images SET '.
'ce_utilisateur = '.Cel::db()->proteger($infos_utilisateur['id_utilisateur']).', '.
'prenom_utilisateur = '.Cel::db()->proteger($infos_utilisateur['prenom']).', '.
'nom_utilisateur = '.Cel::db()->proteger($infos_utilisateur['nom']).', '.
'courriel_utilisateur = '.Cel::db()->proteger($infos_utilisateur['courriel']).' '.
'WHERE ce_utilisateur = '.Cel::db()->proteger($mail_utilisateur).' '.
' -- '.__FILE__.' : '.__LINE__;
 
$migration_releve = Cel::db()->executer($requete);
return $migration_releve;
}
}
?>
/trunk/jrest/bibliotheque/GestionObservation.php
New file
0,0 → 1,557
<?php
/**
* Classe de gestion de l'ajout, modification et suppression des observations
*
* TODO: $sous_requete .= ' date_modification = now() '
* devrait être une clause ON UPDATE ou un TRIGGER
* afin de mettre à jour la date de modification uniquement lorsqu'une modification a effectivement eu lieu
*
* @category CEL
* @package Services
* @subpackage Bibliothèque
* @author Raphaël Droz <raphael@tela-botanica.org>
* @author Aurelien Peronnet <aurelien@tela-botanica.org>
* @copyright © 2010-2014, Tela-Botanica
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
*/
class GestionObservation extends Cel {
 
static $cel_obs = ['id_observation', 'ordre', 'ce_utilisateur', 'prenom_utilisateur', 'nom_utilisateur',
'courriel_utilisateur', 'nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nt', 'famille',
'nom_referentiel', 'ce_zone_geo', 'zone_geo', 'lieudit', 'station', 'milieu', 'latitude', 'longitude',
'altitude', 'geodatum', 'date_observation', 'mots_cles_texte', 'commentaire', 'transmission',
'date_creation', 'date_modification', 'date_transmission', 'abondance', 'certitude', 'phenologie',
'code_insee_calcul'];
 
/**
* Ajoute une observation grâce aux paramètres fournis
*
* @param int $utilisateur id utilisateur du proprietaire de l'observation
* @param array $parametres tableau indexé avec les mêmes noms de champs que la bdd
*
* @return true ou false suivant le succès de l'opération
*/
public function ajouterObservation($utilisateur, $parametres) {
$origin_params = $parametres;
$base_param = array(
'nom_sel_nn' => NULL,
'nom_sel' => NULL,
'nom_ret_nn' => NULL,
'nom_referentiel' => NULL);
$parametres = array_merge($base_param, $parametres);
 
 
$parametres['ordre'] = $this->renvoyerDernierOrdreUtilisePlusUn($utilisateur);
 
$this->traiterEspece($parametres);
 
// si la détermination à échoué, alors:
// soit le référentiel d'origine était "valide", soit non
if (!$parametres['nom_sel_nn']) {
// quoiqu'il en soit, on le met à "autre"
$parametres['nom_referentiel'] = Cel::$fallback_referentiel;
}
 
$this->ajoutInfoUtilisateurARequete($parametres);
self::formaterParametresObservation($parametres);
 
$requete_insertion_observation = 'INSERT INTO cel_obs ';
$sous_requete_colonnes = $this->traiterParametresObservationEtConstruireSousRequeteAjout($parametres);
 
$requete_insertion_observation = $requete_insertion_observation.$sous_requete_colonnes;
 
// important ! ne pas utiliser la fonction executerRequete qui renvoie une erreur si la requete
// contient des | (pipes) ce qui peut arriver dans les commentaires
// TODO: corriger la fonction ou bien continuer à utiliser executerRequeteSimple
$resultat_ajout_observation = Cel::db()->executer($requete_insertion_observation);
 
$retour = true;
if ($resultat_ajout_observation === false) {
$retour = false;
$msg = "Erreur de creation d'une observation : $resultat_ajout_observation";
$this->logger('CEL_bugs', $msg);
} else {
$retour = $this->renvoyerIdPourOrdre($utilisateur ,$parametres['ordre']);
}
return $retour;
}
 
/**
* Renvoie le prochain numero d'ordre relatif à un utilisateur (i.e. dernier ordre + 1)
*
* @param int $utilisateur id utilisateur
*
* @return int le nouveau numero d'ordre relatif à l'utilisateur ou bien 0 s'il n'a jamais créé d'observations
*/
public function renvoyerDernierOrdreUtilisePlusUn($utilisateur) {
$idUtilisateurP = Cel::db()->proteger($utilisateur);
$requete = 'SELECT max(ordre) AS ordre '.
'FROM cel_obs '.
"WHERE ce_utilisateur = $idUtilisateurP ".
' -- ' . __FILE__ . ':' . __LINE__;
$dernier_ordre = Cel::db()->requeter($requete);
 
$nouvel_ordre = 0;
if (is_array($dernier_ordre) && count($dernier_ordre) > 0 && trim($dernier_ordre[0]['ordre']) != '') {
$nouvel_ordre = $dernier_ordre[0]['ordre'] + 1;
}
return $nouvel_ordre;
}
 
/**
* Renvoie l'id d'observation associé à l'utilisateur et l'ordre donné
*
* @param int $utilisateur id utilisateur
* @return int le numero d'ordre relatif à l'utilisateur
*/
public function renvoyerIdPourOrdre($utilisateur, $ordre) {
$idUtilisateurP = Cel::db()->proteger($utilisateur);
$ordreP = Cel::db()->proteger($ordre);
 
$requete = 'SELECT id_observation '.
'FROM cel_obs '.
"WHERE ce_utilisateur = $idUtilisateurP ".
"AND ordre = $ordreP ".
' -- ' . __FILE__ . ':' . __LINE__;
 
$dernier_id = Cel::db()->requeter($requete);
 
$retour = null;
if ($dernier_id != false) {
$retour = $dernier_id[0]['id_observation'];
}
return $retour;
}
 
/**
* Modifie une ou plusieurs observations grâce aux paramètres fournis
*
* @param int $utilisateur id utilisateur du proprietaire de l'observation
* @param mixed $ordre ordre(s) observation(s) relatif(s) à l'utilisateur: un seul ordre ou bien "ordre1,ordre2,ordre3" etc...
* @param array $parametres tableau indexé avec les mêmes noms de champs que la bdd
*
* @return true ou false suivant le succès de l'opération
*/
public function modifierObservation($utilisateur, $ordre, $parametres) {
if (isset($parametres['nom_sel'])) {
// uniquement en cas de nom_sel présent
$this->traiterEspece($parametres);
}
self::formaterParametresObservation($parametres);
$champ_maj = self::traiterParametresObservationEtConstruireSousRequeteMaj($parametres);
$champ_maj[] = "date_modification = now()";
$clauseSet = implode(', ', $champ_maj);
$utilisateurIdP = Cel::db()->proteger($utilisateur);
 
$requete = "UPDATE cel_obs SET $clauseSet ".
"WHERE ordre IN ($ordre) ".
" AND ce_utilisateur = $utilisateurIdP ".
' -- '.__FILE__.':'.__LINE__;
 
// important ! ne pas utiliser la fonction executerRequete qui renvoie une erreur si la requete contient des | (pipes)
// ce qui peut arriver dans les commentaires
// TODO: corriger la fonction ou bien continuer à utiliser executerRequeteSimple
$resultat = Cel::db()->executer($requete);
 
$retour = true;
if ($resultat === false) {
$retour = false;
$this->logger('CEL_bugs', "Erreur de mise à jour d'une liste d'observations : $requete");
}
return $retour;
}
 
/**
* @return nombre d'observations mises à jour, ou FALSE en cas de problème
*/
static function modifierMultiplesObservation($utilisateur, Array $ordre, $parametres) {
// nous pouvons aussi retirer 'ce_utilisateur' (== $utilisateur) qui sera dans la clause WHERE
$exclusions = ['nom_sel_nn', 'nom_sel', 'nom_ret_nn', 'nom_referentiel', 'ce_utilisateur'];
$parametres = array_diff_key($parametres, array_flip($exclusions));
$parametres = array_intersect_key($parametres, array_flip(self::$cel_obs));
 
$parametres = array_filter(
$parametres,
create_function('$e','return strpos($e, "(Valeurs multiples)") === false;'));
 
// modifie $parametres par référence
self::formaterParametresObservation($parametres);
 
$champ_maj = self::traiterParametresObservationEtConstruireSousRequeteMaj($parametres);
$nbreUpdate = count($champ_maj);
 
if ($nbreUpdate > 0) {
$champ_maj[] = 'date_modification = NOW()';
$clauseSet = implode(', ', $champ_maj);
$ordresListe = implode(', ', $ordre);
$utilisateurIdP = Cel::db()->proteger($utilisateur);
 
$requete = "UPDATE cel_obs SET $clauseSet ".
"WHERE ordre IN ($ordresListe) ".
"AND ce_utilisateur = $utilisateurIdP "
.' -- '.__FILE__.':'.__LINE__;
$nbreUpdate = Cel::db()->executer($requete);
}
return $nbreUpdate;
// TODO: return json_encode(updated_rows());
}
 
 
/**
* Modifie une observation publique dont l'id et l'utilisateur sont passé en paramètre
*
* @param int $utilisateur id utilisateur du proprietaire de l'observation
* @param int $id identifiant de l'observation
* @param array $parametres tableau indexé avec les mêmes noms de champs que la bdd
*
* @return true ou false suivant le succès de l'opération
*/
public function modifierObservationPublique($id_obs, $parametres) {
$parametresDefaut = array(
'nom_sel_nn' => NULL,
'nom_sel' => NULL,
'nom_ret_nn' => NULL,
'nom_referentiel' => NULL);
$parametres = array_merge($parametresDefaut, $parametres);
 
$this->traiterEspece($parametres);
self::formaterParametresObservation($parametres);
 
$champ_maj = self::traiterParametresObservationEtConstruireSousRequeteMaj($parametres);
$champ_maj[] = 'date_modification = NOW()';
 
$requete = "UPDATE cel_obs SET " .
implode(', ', $champ_maj) .
" WHERE id_observation = ".Cel::db()->proteger($id_obs).
" AND transmission = 1 ".
' -- ' . __FILE__ . ':' . __LINE__;
$resultat = Cel::db()->executer($requete);
 
if ($resultat === false) {
$this->logger("CEL_bugs", "Erreur de mise à jour de l'observation : ".$id_obs);
}
return $resultat;
}
 
public function modifierTransmissionObservation($ids_obs_ou_tableau, $publier) {
if (is_array($ids_obs_ou_tableau)) {
$ids_obs_ou_tableau = array_map(array(Cel::db(),'proteger'),$ids_obs_ou_tableau);
$chaine_ids_obs = implode(',',$ids_obs_ou_tableau);
} else {
$chaine_ids_obs = Cel::db()->proteger($ids_obs_ou_tableau);
}
$etat_transmission = $publier ? 1 : 0;
 
$requete = "UPDATE cel_obs ".
"SET transmission = $etat_transmission , date_modification = NOW() ".
"WHERE id_observation IN ($chaine_ids_obs) ".
' -- ' . __FILE__ . ':' . __LINE__;
 
$resultat = Cel::db()->executer($requete);
return $resultat;
}
 
/**
* Supprime une ou plusieurs observations grâce aux paramètres fournis
*
* @param int $utilisateur id utilisateur du proprietaire de l'observation
* @param mixed $ordre ordre(s) observation(s) relatif(s) à l'utilisateur: un seul ordre ou bien "ordre1,ordre2,ordre3" etc...
*
* @return true ou false suivant le succès de l'opération
*/
public function supprimerObservation($utilisateur, $ordre) {
// TODO changer le systeme pour n'utiliser plus que les id
$retour = false;
$tableau_ordre = explode(',', $ordre);
$tableau_ordre = array_map(array(Cel::db(),'proteger'), $tableau_ordre);
$ordre = implode(',', $tableau_ordre);
$idUtilisateurP = Cel::db()->proteger($utilisateur);
 
$requete = 'SELECT id_observation AS id_obs '.
'FROM cel_obs '.
"WHERE ce_utilisateur = $idUtilisateurP ".
"AND ordre IN ($ordre) ".
' -- '.__FILE__.':'.__LINE__;
$resultat_ids_observations = Cel::db()->requeter($requete);
 
$ids_obs = array();
$ids_obs_non_protegees = array();
if (is_array($resultat_ids_observations) && count($resultat_ids_observations) > 0) {
foreach ($resultat_ids_observations as $id_observation) {
$ids_obs[] = Cel::db()->proteger($id_observation['id_obs']);
$ids_obs_non_protegees[] = $id_observation['id_obs'];
}
 
$chaine_ids_obs = implode(',', $ids_obs);
$requete = 'DELETE FROM cel_obs WHERE '.
"ce_utilisateur = $idUtilisateurP ".
"AND id_observation IN ($chaine_ids_obs) ".
' -- '.__FILE__.':'.__LINE__;
 
$resultat_suppression_observations = Cel::db()->executer($requete);
 
// TODO: Faire la suppression des mots clés
// et des liaisons obs images dans une ou des fonctions à part
if ($resultat_suppression_observations === false) {
$msg = "Erreur de suppression d'une liste d'observations : $resultat_suppression_observations";
$this->logger('CEL_bugs', $msg);
} else {
// TODO [jpm] : pourquoi on supprime pas les images si on supprime les obs ?
 
$gestion_mots_cles = new GestionMotsClesChemin($this->config, 'obs');
$resultat_suppression_mots_cles = $gestion_mots_cles->supprimerToutesLiaisonsPourIdsElementsLies($ids_obs_non_protegees);
 
if ($resultat_suppression_mots_cles === false) {
$msg = "Erreur de suppression d'une liste de mots clés d'observation(s) : $resultat_suppression_mots_cles";
$this->logger('CEL_bugs', $msg);
} else {
$retour = true;
}
}
} else {
$retour = true;
}
return $retour;
}
 
/**
* Fonction utilisée pour importer les observations faites avec des comptes anonymes vers un
* utilisateur identifié
* @param string $ancien_id
* @param string $id_utilisateur
*/
public function migrerObservations($ancien_id, $id_utilisateur) {
//TODO faire une fonction plus complexe qui déplace, images, observations, mots-clés etc... d'un utilisateur à l'autre
// qui pourrait servir pour fusionner des comptes
$nouvel_ordre = $this->renvoyerDernierOrdreUtilisePlusUn($id_utilisateur);
 
// Recuperation relevés associés a la session
$idAncienP = Cel::db()->proteger($ancien_id);
$requete = 'SELECT ordre '.
'FROM cel_obs '.
"WHERE ce_utilisateur = $idAncienP ".
'ORDER BY ordre '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = Cel::db()->requeter($requete);
 
$reussite = true;
if (is_array($resultat)) {
foreach ($resultat as $releve_temporaire) {
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$ancienOrdre = $releve_temporaire['ordre'];
$requete = 'UPDATE cel_obs SET '.
"ce_utilisateur = $idUtilisateurP, ".
"ordre = $nouvel_ordre ".
"WHERE ce_utilisateur = $idAncienP ".
"AND ordre = $ancienOrdre ".
' -- '.__FILE__.' : '.__LINE__;
$migration_releve = Cel::db()->executer($requete);
 
//TODO: meilleure vérification
if ($migration_releve === false) {
$reussite = false;
}
$nouvel_ordre++;
}
}
return $reussite;
}
 
/**
* Fonction utilisée pour importer les anciennes observations saisie dans les widget dans un compte identifié
* Dans ce cas là, le widget remplit la case id_utilisateur par le mail indiqué lors de la saisie
* @param string $mail_utilisateur
* @param string $id_utilisateur
*/
public function migrerObservationsMailVersId($mail_utilisateur, $infos_utilisateur) {
// ATTENTION : cette fonction suppose que l'utilisateur n'ai pas déjà d'observations dans le CEL
// avec l'identifiant $id_utilisateur ce qui est normalement le cas
$requete_migration_releve = 'UPDATE cel_obs SET '.
'ce_utilisateur = '.Cel::db()->proteger($infos_utilisateur['id_utilisateur']).', '.
'prenom_utilisateur = '.Cel::db()->proteger($infos_utilisateur['prenom']).', '.
'nom_utilisateur = '.Cel::db()->proteger($infos_utilisateur['nom']).', '.
'courriel_utilisateur = '.Cel::db()->proteger($infos_utilisateur['courriel']).' '.
'WHERE ce_utilisateur = '.Cel::db()->proteger($mail_utilisateur).' '.
' -- '.__FILE__.' : '.__LINE__;
 
$migration_releve = Cel::db()->executer($requete_migration_releve);
 
return $migration_releve;
}
 
private function traiterEspece(&$parametres) {
if (!$parametres['nom_referentiel']) {
$parametres['nom_referentiel'] = Cel::$default_referentiel;
}
$code_referentiel = substr(trim($parametres['nom_referentiel']), 0, 5);
 
// TODO/XXX: quoi ?!
if (!$parametres['nom_sel_nn'] && !$parametres['nom_sel'] && $parametres['nom_ret_nn']) {
$parametres['nom_ret'] = $parametres['nom_ret_nn'] = $parametres['nt'] = $parametres['famille'] = '';
}
 
/* pour un nom saisi sans autocomplétion 3 cas de figure existent:
1) référentiel bdtfx
1.1) trouvé par le webservice => ref = bdtfx
1.2) pas trouvé par le webservice => ref = autre
2) si le référentiel est inconnu (explicitement mis à "autre"), alors pas d'appel au webservice => ref = autre */
if (in_array($code_referentiel, Cel::$referentiels_valides)) {
//
$deuxieme_passe = true;
// nom saisi sans numéro
if ($parametres['nom_sel'] && !$parametres['nom_sel_nn']) {
$chercheur_infos_complementaires = new RechercheInfosTaxonBeta($this->config, $code_referentiel);
// Utilisation d'un nom sans numéro nomenclatural, recherche d'une correspondance sur le nom
$complement = $chercheur_infos_complementaires->rechercherInformationsComplementairesSurNom($parametres['nom_sel']);
// Si l'on a trouvé un résultat sur la recherche, il s'agit vraisemblablement d'un copié-collé
// de nom de taxon qui n'a pas été validé par la selection
if ($complement) {
$parametres['nom_sel_nn'] = $complement->id;
$parametres['nom_ret'] = RechercheInfosTaxonBeta::supprimerBiblio($complement->nom_retenu_complet);
$parametres['nom_ret_nn'] = $complement->{"nom_retenu.id"};
$parametres['nt'] = $complement->num_taxonomique;
$parametres['famille'] = $complement->famille;
} else {
// pas de résultat de recherche sur le référentiel demandé, le webservice doit peut-être être corrigé
// mais en tout état de cause nous n'insérerons PAS de données incertaines associées à un référentiel !
$code_referentiel = Cel::$fallback_referentiel;
$parametres['nom_sel_nn'] = $parametres['nom_ret'] = $parametres['nom_ret_nn'] = $parametres['nt'] = $parametres['famille'] = NULL;
// goto out;
$deuxieme_passe = false;
}
}
 
// XXX: ne devrait plus être nécessaire maintenant que rechercherInformationsComplementairesSurNom() [plus précisément effectuerRequeteUrlRecherche()]
// a été modifiée pour retourner tous les champs nécessaire.
if ($deuxieme_passe && (! array_key_exists('nom_ret_nn', $parametres) || ! array_key_exists('nt', $parametres))) {
// Utilisation d'un nom faisant parti du referentiel : recherche du nom valide correspondant
$chercheur_infos_complementaires = new RechercheInfosTaxonBeta($this->config , $code_referentiel);
$complement = $chercheur_infos_complementaires->effectuerRequeteInfosComplementairesSurNumNom($parametres['nom_sel_nn']);
// les paramètres passés datait peut-être d'une précédente autocomplétion, mais le référentiel ayant été changé par l'utilisateur
// la détection ne fonctionne plus : nous supprimons les données précédemment générées de la base !
if (!$complement) {
$parametres['nom_sel_nn'] = $parametres['nom_ret'] = $parametres['nom_ret_nn'] = $parametres['nt'] = $parametres['famille'] = NULL;
} else {
$parametres['nom_ret'] = RechercheInfosTaxonBeta::supprimerBiblio($complement->nom_retenu_complet);
$parametres['nom_ret_nn'] = $complement->{"nom_retenu.id"};
$parametres['nt'] = $complement->num_taxonomique;
$parametres['famille'] = $complement->famille;
}
}
}
 
// référentiel "autre": on vide !
else {
$parametres['nom_sel_nn'] = $parametres['nom_ret'] = $parametres['nom_ret_nn'] = $parametres['nt'] = $parametres['famille'] = NULL;
}
 
// mise à jour du référentiel utilisé, sans n° de version
$parametres['nom_referentiel'] = $code_referentiel;
}
 
// uniquement nécessaire lors de l'ajout
private function ajoutInfoUtilisateurARequete(&$parametres) {
if (isset($parametres['ce_utilisateur'])) {
$infos_utilisateur = $this->getInfosComplementairesUtilisateur($parametres['ce_utilisateur']);
$parametres['courriel_utilisateur'] = $infos_utilisateur['courriel'];
$parametres['nom_utilisateur'] = $infos_utilisateur['nom'];
$parametres['prenom_utilisateur'] = $infos_utilisateur['prenom'];
}
}
 
/**
* Formate les paramètres fournis en ajoutant des infos complementaires
*
* @param array $parametres tableau indexé avec les mêmes noms de champs que la bdd
* @return $parametres le tableau modifié selon ce qu'il contenait
*/
static function formaterParametresObservation(&$parametres) {
// Pour empecher que des numéros de département de 1 à 9 soient saisis sans 0
// TODO: décider quoi faire pour les zones géo plus générales
if (isset($parametres['ce_zone_geo'])) {
if ($parametres['ce_zone_geo'] == 'null' || trim($parametres['ce_zone_geo']) == "") {
$parametres['ce_zone_geo'] = "";
} else {
if (strlen($parametres['ce_zone_geo']) == 4) {
$parametres['ce_zone_geo'] = '0'.$parametres['ce_zone_geo'];
}
 
if (strlen($parametres['ce_zone_geo']) > 0 && strlen($parametres['ce_zone_geo']) <= 2) {
$parametres['ce_zone_geo'] = Cel::obtenirCodeInseeCommunePourNomEtDepartement($parametres['zone_geo'], $parametres['ce_zone_geo']);
} else {
$parametres['ce_zone_geo'] = 'INSEE-C:'. $parametres['ce_zone_geo'];
}
}
}
 
// TODO : voir si l'on peut utiliser un des fonctions de la classe cel
if (isset($parametres['date_observation']) && $parametres['date_observation'] != 'null') {
//TODO: prendre en compte des dates incomplètes ?
if (count(explode('/', $parametres['date_observation'])) >= 3) {
list($jour,$mois,$annee) = explode('/', $parametres['date_observation']);
$parametres['date_observation'] = $annee.'-'.$mois.'-'.$jour.' 0:0:0';
}
}
}
 
/**
* Assemble une sous requete pour un ajout, tout en formatant les paramètres et en recherchant
* les infos complémentaires
*
* @param array $parametres un tableau avec les index correspondant aux champs de la bdd
*
* @return string une sous requete utilisable pour l'ajout d'une observation
*/
private function traiterParametresObservationEtConstruireSousRequeteAjout($parametres) {
$sous_requete = '';
 
$champs = '';
$valeurs = '';
 
// Nullifiage ...
// TODO: code dupliqué, en faire une fonction à mettre à la place appropriée
foreach($parametres as $cle => $valeur) {
$valeur = self::renvoyerValeurDenullifiee($valeur);
$champs .= $cle.', ';
$valeurs .= $valeur.', ';
}
 
$champs = '('.$champs.'mots_cles_texte, transmission, date_creation, date_modification, date_transmission)';
$valeurs = '('.$valeurs.'"", 0, now(), now(), "0000-00-00 00:00:00")';
 
$sous_requete .= $champs.' VALUES '.$valeurs;
 
return $sous_requete;
}
 
/**
* Assemble une sous requete pour une mise à jour, tout en formatant les paramètres et en recherchant
* les infos complémentaires
*
* @param array $parametres un tableau avec les index correspondant aux champs de la bdd
*
* @return string une sous requete utilisable pour la modification d'une observation
* selon la syntaxe UPDATE table SET colonne1 = valeur1, colonne2 = valeur2 WHERE condition
*/
static function traiterParametresObservationEtConstruireSousRequeteMaj($parametres) {
$champs = array();
 
// Nullifiage ...
// TODO: code dupliqué, en faire une fonction à mettre à la place appropriée
foreach ($parametres as $cle => $valeur) {
$valeur = self::renvoyerValeurDenullifiee($valeur);
$champs[] = $cle." = ".$valeur;
}
return $champs;
}
 
private static function renvoyerValeurDenullifiee($valeur) {
if (trim($valeur) == '' || trim($valeur) == 'null' || trim($valeur) == '000null') {
$valeur = 'NULL';
} else {
$valeur = Cel::db()->proteger($valeur);
}
return $valeur;
}
}
/trunk/jrest/bibliotheque/Bdd.php
New file
0,0 → 1,136
<?php
/**
* La classe de gestion de la base de données.
*
* @category php 5.2
* @package cel
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
*/
class Bdd {
 
const SQL_MODE_ASSOC = PDO::FETCH_ASSOC;
const SQL_MODE_OBJET = PDO::FETCH_OBJ;
const SQL_RETOUR_COMPLET = 'All';
const SQL_RETOUR_LIGNE = 'Row';
const SQL_RETOUR_COLONNE = 'Column';
const SQL_RETOUR_BRUT = 'Raw';
 
private $bdd = null;
 
public function __construct($cfg) {
// ATTENTION : la connexin à la bdd peut échouer si l'host vaut localhost. Utiliser 127.0.0.1 à la place.
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
try {
// Création de la connexion en UTF-8 à la BDD
$this->bdd = new PDO($dsn, $cfg['username'], $cfg['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
$this->bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$message = "La connexion à la base de donnée via PDO a échouée : $dsn {$e->getMessage()}";
$code = E_USER_ERROR;
throw new Exception($message, $code);
}
}
 
/**
* Protège automatiquement toutes les chaines comprises entre deux caractères '|'.
* Puis execute la requete.
* @see protegerRequete()
* @param unknown_type $requete
*/
public function requeter($requete, $retour = self::SQL_RETOUR_COMPLET, $mode = PDO::FETCH_ASSOC) {
$resultat = false;
try {
switch ($retour) {
case self::SQL_RETOUR_COMPLET :
$resultat = $this->bdd->query($requete)->fetchAll($mode);// Retourne toutes les lignes
break;
case self::SQL_RETOUR_LIGNE :
$resultat = $this->bdd->query($requete)->fetch($mode);// Retourne la première ligne
break;
case self::SQL_RETOUR_COLONNE :
$resultat = $this->bdd->query($requete)->fetchColumn();// Retourne la première colonne de la première ligne
break;
case self::SQL_RETOUR_BRUT :
$resultat = $this->bdd->query($requete);// Retourne l'objet brut pour être utilisé dans une boucle de type foreach
break;
default:
$message = "Le type de retour '$retour' est inconnu.";
$code = E_USER_ERROR;
throw new Exception($message, $code);
}
if ($resultat === false) {
$message = "La requête a retourné aucun résultat : $requete";
$code = E_USER_NOTICE;
throw new Exception($message, $code);
}
} catch (PDOException $e) {
$message = sprintf($this->getTxt('sql_erreur_requete'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
$code = E_USER_ERROR;
throw new Exception($message, $code);
}
return $resultat;
}
 
/**
* Protège automatiquement toutes les chaines comprises entre deux caractères '|'.
* @see protegerRequete()
* @param unknown_type $requete
*/
public function executer($requete) {
$resultat = false;
try {
$resultat = $this->bdd->exec($requete);
if ($resultat === false) {
$this->debug[] = "La requête a échoué : $requete";
}
} catch (PDOException $e) {
$message = "Fichier : {$e->getFile()} \nLigne : {$e->getLine()} \nMessage : {$e->getMessage()} \nRequête : $requete";
$code = E_USER_ERROR;
throw new Exception($message, $code);
}
return $resultat;
}
 
public function proteger($element) {
$element = is_array($element) ? $this->protegerTableau($element) : $this->protegerChaine($element);
return $element;
}
 
private function protegerChaine($chaine) {
return $this->bdd->quote($chaine);
}
 
private function protegerTableau(Array $tableau) {
foreach ($tableau as $id => $val) {
if (is_array($val)) {
$tableau[$id] = $this->protegerTableau($val);
} else {
$tableau[$id] = $this->protegerChaine($val);
}
}
return $tableau;
}
 
public function etreNull($valeur) {
$etreNull = false;
if ($valeur == '' || $valeur == null || $valeur == '000null' || $valeur == 'null' || $valeur == '*') {
$etreNull = true;
}
return $etreNull;
}
 
public function getTxt($id) {
$sortie = '';
switch ($id) {
case 'sql_erreur_requete' : $sortie = "Requête echec.\nFichier : %s.\nLigne : %s.\nMessage : %s.\nRequête : %s"; break;
default : $sortie = $id;
}
return $sortie;
}
}
?>
/trunk/jrest/index.php
12,7 → 12,7
return null;
}
 
$chemins = array('', 'services/', 'lib/');
$chemins = array('', 'services/', 'bibliotheque/', 'lib/');
foreach ($chemins as $chemin) {
$chemin = $chemin.$classe.'.php';
if (file_exists($chemin)) {