Subversion Repositories eFlore/Applications.cel

Compare Revisions

No changes between revisions

Ignore whitespace Rev 3843 → Rev 3857

/branches/v3.01-serpe/jrest/bibliotheque/Bdd.php
New file
0,0 → 1,142
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion de la base de données.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Raphaël Droz <raphael@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 Bdd 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
/branches/v3.01-serpe/jrest/bibliotheque/RechercheImage.php
New file
0,0 → 1,385
<?php
// declare(encoding='UTF-8');
/**
* Classe de recherche d'images à 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 RechercheImage extends Cel {
public static $tris_possibles = array(
// 'ordre', // On désactive ordre qui est le tri par défaut, car ça pédale trop si l'user à trop d'obs
'taxon',
'transmission',
'zone_geo',
'date_prise_de_vue',
'appareil_fabriquant',
'note_qualite',
'nom_original'
);
 
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;
}
// redéfinis en 2020, affiche toutes les infos images et obs à partir d'un join pour n'avoir que les images liées à une obs
public function rechercherImagesJoinObservation($id_utilisateur = null, $criteres = array(), $debut = 0 , $limite = 50) {
$ordre = (isset($criteres['tri']) && $criteres['tri']) ? $criteres['tri'] : '';
unset($criteres['tri']);
$direction = (isset($criteres['dir']) && $criteres['dir']) ? $criteres['dir'] : 'ASC';
unset($criteres['dir']);
$imgUrlTplBase = explode('%', $this->config['settings']['celImgUrlTpl'])[0];
$debut = ($debut < 0) ? 0 : $debut;
$requete_recherche_images = 'SELECT ci.`id_image` as id_photo, co.guid,
`id_observation` as id_obs, `nom_sel`, `nom_sel_nn`, `zone_geo` as localisation, `latitude`, `longitude`,
`date_observation` as date_obs, `commentaire`, `certitude`, `grade` as fiabilite, programme as projet,
`score_identiplante` as score_ip, `url_identiplante` as url_ip, `validation_identiplante` as est_ip_valide,
co.mots_cles_texte as tags_obs, `nom_ret`, `nom_ret_nn`, `famille`,`nom_referentiel`, `pays`,
`ce_zone_geo` as code_insee, `dept`, `lieudit`, `station`, `milieu`, `altitude`, `localisation_precision`,
`localisation_floutage`, `localisation_coherence`, `abondance`, `phenologie`, `spontaneite`,
`type_donnees`, `biblio`, `source`, `herbier`, `determinateur`, `observateur`, `observateur_structure`,
`ce_utilisateur` as id_utilisateur, `courriel_utilisateur` as mail_utilisateur, `pseudo_utilisateur` as nom_utilisateur,
ci.`nom_original`, ci.`date_prise_de_vue` as date_photo, "Creative Commons BY-SA (2.0)" as licence,
concat(`nom_sel`, " par ", `pseudo_utilisateur`, " [CC BY-SA 2.0 FR], via Tela Botanica") as attribution,
concat("'.$imgUrlTplBase.'", lpad(ci.id_image, 9, "0"), "O,") as url_photo, ci.`mots_cles_texte` as tags_photo ';
$requete_recherche_images .= $this->fabriquerRequeteJointureDblObs();
$requete_recherche_images .= ($id_utilisateur != null) ? 'AND co.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
$sous_requete_recherche = $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
$requete_recherche_images .= $sous_requete_recherche;
if (!empty($ordre)) {
$requete_recherche_images .= ' ORDER BY '.$ordre.' '.$direction;
}
$requete_recherche_images .= ' LIMIT '.$debut.','.$limite;
$resultats_images = array();
$resultats_images = Cel::db()->requeter($requete_recherche_images);
return $resultats_images;
}
 
public function rechercherImages($id_utilisateur = null, $criteres = array(), $debut = 0 , $limite = 50) {
$ordre = (isset($criteres['tri']) && $criteres['tri']) ? $criteres['tri'] : '';
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.*, co.pseudo_utilisateur ';
 
$requete_recherche_images .= $this->fabriquerRequeteJointureObs();
$requete_recherche_images .= ($id_utilisateur != null) ? 'AND co.ce_utilisateur = '.Cel::db()->proteger($id_utilisateur) : '';
 
$sous_requete_recherche = $this->fabriquerSousRequeteRecherche($id_utilisateur, $criteres);
$requete_recherche_images .= $sous_requete_recherche;
 
if (!empty($ordre)) {
$requete_recherche_images .= ' ORDER BY '.$ordre.' '.$direction;
}
$requete_recherche_images .= ' 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()) {
$requete_recherche_images = 'SELECT COUNT(*) AS nb_images ';
 
$requete_recherche_images .= $this->fabriquerRequeteJointureObs();
$requete_recherche_images .= ($id_utilisateur != null) ? 'AND co.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(), $tri = "") {
$tris_obs = array('nom_sel','transmission','zone_geo');
$criteres_obs = array('zone_geo','ce_zone_geo','taxon','transmission','recherche');
return count(array_intersect(array_keys($criteres),$criteres_obs)) > 0 ||
(!empty($tri) && in_array($tri, $tris_obs));
}
 
private function fabriquerRequeteJointureObs() {
$requete = 'FROM cel_images_export ci '.
'LEFT JOIN cel_export_total co '.
'ON ci.ce_observation = co.id_observation '.
'WHERE 1 ';
return $requete;
}
private function fabriquerRequeteJointureDblObs() {
$requete = 'FROM cel_images_export ci '.
'JOIN cel_export_total 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_export 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_export_total 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 "standard":
$sous_requete .= 'co.donnees_standard = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ';
break;
case "referentiel":
$sous_requete .= 'co.referentiel = '.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":
if (is_numeric($valeur)) {
$sous_requete .= 'co.ce_utilisateur = '.Cel::db()->proteger($valeur).' AND ';
} elseif (strpos($valeur, '@') !== false) {
$sous_requete .= 'co.courriel_utilisateur = '.Cel::db()->proteger($valeur).
' AND ';
} else {
$sous_requete .= 'co.pseudo_utilisateur LIKE '.Cel::db()->proteger($valeur.'%').
' AND ';
}
break;
case "ce_zone_geo":
if($valeur !== "NULL") {
$depts = explode(",", $valeur);
if (count($depts) == 1) {
$sous_requete .= '(co.ce_zone_geo = '. Cel::db()->proteger($valeur).') ';
} else {
$sous_requete .= '(co.dept in ('.$valeur.')) ';
}
}
break;
case "zone_geo":
if($valeur !== "NULL") {
$sous_requete .= '(co.zone_geo = '.Cel::db()->proteger($valeur).') ';
}
break;
case "pays":
if($valeur !== "NULL") {
$sous_requete .= '(co.pays like '.Cel::db()->proteger($valeur.'%').') ';
}
break;
case "famille":
$sous_requete .= 'co.famille = '.Cel::db()->proteger($valeur) ;
$sous_requete .= ' AND ' ;
break;
case "programme":
if($valeur !== "NULL") {
$sous_requete .= '(co.programme = '.Cel::db()->proteger($valeur).') ';
}
break;
case "tri":
break;
case "dir":
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 '.
'co.ce_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.courriel_utilisateur LIKE '.Cel::db()->proteger($chaine_recherche.'%').' OR '.
'co.pseudo_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) {
$idImageP = Cel::db()->proteger($id_image);
$requete = 'SELECT courriel_utilisateur '.
'FROM cel_images_export '.
"WHERE id_image = $idImageP ".
' -- '.__FILE__.':'.__LINE__;
$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 photo_tag_id '.
'FROM photo_tag_photo '.
"WHERE photo_id = $id_image ".
' -- '.__FILE__.':'.__LINE__;
return Cel::db()->requeter($requete);
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GenerateurPDF.php
New file
0,0 → 1,481
<?php
// declare(encoding='UTF-8');
/**
* Classe permettant de générer des PDFs.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botania.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>
*/
// 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 __construct($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');
 
$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);
 
$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;
}
 
public 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']))
);
 
}
 
}
/branches/v3.01-serpe/jrest/bibliotheque/Decoupage.php
New file
0,0 → 1,160
<?php
// declare(encoding='UTF-8');
/**
* Classe abstraite mettant en comun des expressions régulière pour le découpage des noms latins.
*
* @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>
*/
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
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionChampsEtendus.php
New file
0,0 → 1,295
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion de l'ajout, modification et suppression des champs supplémentaires des obs et images.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botania.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 GestionChampsEtendus extends Cel {
 
private $mode = null;
private $table_obs_etendus = null;
private $champ_id = null;
 
public function __construct($config, $mode = 'obs') {
parent::__construct($config);
$this->mode = $mode;
$this->table_obs_etendus = 'extended_field_occurrence';
$this->champ_id = 'occurrence_id';
}
 
/**
* 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_obs_etendus} ".
"WHERE {$this->champ_id} = $id ".
" AND extended_field_id = $cle ".
' -- '.__FILE__.':'.__LINE__;;
 
$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_obs_etendus} ".
"WHERE {$this->champ_id} = $id ".
' -- '.__FILE__.':'.__LINE__;
$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 occurrence_id, occurrence_id as id_observation, value as valeur, `field_id`as cle FROM {$this->table_obs_etendus} o join extended_field f
on f.`id` = extended_field_id 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 ObsEtendue();
$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_obs_etendus} ".
"( {$this->champ_id}, extended_field_id, value) ".
"VALUES ($id, $cle, $valeur) ".
"ON DUPLICATE KEY UPDATE value = VALUES(value) ".
' -- '.__FILE__.':'.__LINE__;
 
// 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, $projet = null) {
if (! $champs_etendus) return TRUE; // le tableau ... vide à été inséré
$ajout = false;
$label_champs_etendus = new GestionChampsEtendus2($this->config);
$lignes = array();
foreach ($champs_etendus as $champ_etendu) {
$champ_id = $label_champs_etendus->consulter(trim($champ_etendu->cle, "[]"));
if ($champ_id != false) {
$id = Cel::db()->proteger($champ_etendu->id);
$cle = Cel::db()->proteger($champ_id[0]['id']);
$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
if ($lignes != array()) {
$ajout = Cel::db()->executer(sprintf(
"INSERT INTO %s (%s, extended_field_id, value) VALUES %s ON DUPLICATE KEY UPDATE value = VALUES(value) -- %s:%d",
$this->table_obs_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_obs_etendus} ".
"SET value = $valeur ".
"WHERE extended_field_id = $cle".
" AND {$this->champ_id} = $id ".
' -- '.__FILE__.':'.__LINE__;
$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_obs_etendus} ".
"WHERE extended_field_id = $cle ".
"AND {$this->champ_id} = $id ".
' -- '.__FILE__.':'.__LINE__;
$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_obs_etendus} ".
"WHERE {$this->champ_id} = $id ".
' -- '.__FILE__.':'.__LINE__;
$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_obs_etendus} ".
"WHERE {$this->champ_id} IN (".implode(',',$ids).") ".
' -- '.__FILE__.':'.__LINE__;
$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 `field_id`as cle FROM `extended_field` f join extended_field_occurrence o
on f.`id` = extended_field_id WHERE `occurrence_id` in (".implode(',',$ids).") ".
' -- '.__FILE__.':'.__LINE__;
$cles = Cel::db()->requeter($requete);
$i = 0;
foreach($cles as &$cle) {
$cles_fmt[$cle['cle']] = $i++;
}
}
return array_values(array_flip($cles_fmt));
}
 
 
 
/**
* 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;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/RechercheInfosTaxonBeta.php
New file
0,0 → 1,284
<?php
// declare(encoding='UTF-8');
/**
* Classe recherchant des infos sur un taxon.
*
* Elle appelle les web service d'eflore pour éviter que le code client ne soit dépendant de la BDD d'eFlore.
*
* @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 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 __construct($config, $code_referentiel = 'bdtfx') {
parent::__construct($config);
$this->setReferentiel($code_referentiel);
}
 
public function setReferentiel($code_referentiel = 'bdtfx') {
$sousref = array("apdfna" => array("apd", "fna"),
"apdfta" => array("apd", "fta"),
"apdfsa" => array("apd", "fsa"),
"taxref", "vascan");
if (array_key_exists($code_referentiel, $sousref)) {
$this->code_referentiel = $sousref[$code_referentiel][0];
$this->formaterUrlsServicesSousRef($this->config, $sousref[$code_referentiel][1]);
} else {
$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_nom_m = str_replace('{referentiel}', $this->code_referentiel ,$config['eflore']['url_service_nom'])."?";
$this->url_service_taxon_m = 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;
}
private function formaterUrlsServicesSousRef($config, $sousref) {
$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_nom_m = str_replace('{referentiel}', $this->code_referentiel ,$config['eflore']['url_service_nom'])."?masque.ref=".$sousref."&";
$this->url_service_taxon_m = str_replace('{referentiel}', $this->code_referentiel ,$config['eflore']['url_service_taxon'])."?masque.ref=".$sousref."&";
$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_m.'masque='.$masque.'&recherche=etendue&retour.format=min&navigation.limite=50&ns.structure=au');
$urlService = $this->url_service_nom_m.'masque='.$masque.'&recherche=etendue&retour.format=min&navigation.limite=50&ns.structure=au,an';
$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'] . '/' .
$this->code_referentiel . '/' .
'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;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/ObsEtendue.php
New file
0,0 → 1,22
<?php
// declare(encoding='UTF-8');
/**
* Classe conteneur servant à gérer les infos des champs étendus.
*
* @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 ObsEtendue {
public $id = '';
public $cle = '';
public $valeur = '';
}
/branches/v3.01-serpe/jrest/bibliotheque/NameParser.php
New file
0,0 → 1,348
<?php
// declare(encoding='UTF-8');
/**
* Classe permettant de convertir une chaine d'un nom scientifique en un format standard.
*
* Source orignale :
* Taxamatch-Webservice PHP v1.0.0
* @author Michael Giddens
* @link http://www.silverbiology.com
*
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author David DELON <david@clapas.net>
* @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 NameParser {
 
/**
* Whether to debug or nor
* @var bool|integer
*/
public $debug_flag;
 
/**
* 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);
}
}
}
/branches/v3.01-serpe/jrest/bibliotheque/ImageRecreation.php
New file
0,0 → 1,797
<?php
// declare(encoding='UTF-8');
/**
* Classe de manipulation des fichiers images JPEG.
*
* @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 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);
}
}
}
 
/**
* params[0] doit contenir un seul identifiant d'image; les miniatures
* seront regénérée pour cette image
*/
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;
 
$tailleFichierOrigine = filesize($chemin_fichier_origine);
 
$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 ($tailleFichierOrigine == 0) {
$erreur = 'ERREUR : fichier image original [' . $chemin_fichier . '] de taille nulle (0) \n' ;
$this->logger('CEL_bugs',$erreur);
return false;
}
 
if ($deplacement_fichier) {
$infos_image_originale = $this->obtenirImageEtInfosPourChemin($chemin_fichier);
$taux_compression = $this->renvoyerTauxCompressionPourPoids($infos_image_originale['poids_octets']);
 
$ecritureOK = true;
if ($taux_compression < 100 && $this->mode == self::MODE_IMAGEMAGICK) {
$ecritureOK = $this->ecrireImageSurDisqueAvecMeta($chemin_fichier, $taux_compression);
}
 
if ($ecritureOK) {
return $infos_image_originale;
} else {
$erreur = 'ERREUR : probleme durant la recompression du fichier image original \n' ;
$this->logger('CEL_bugs',$erreur);
return false ;
}
} else {
$erreur = 'ERREUR : 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'], $image_et_infos['imagetype']) = getimagesize($chemin_fichier);
$image_et_infos['poids_octets'] = filesize($chemin_fichier);
$image_et_infos['chemin'] = $chemin_fichier;
 
switch (image_type_to_mime_type($image_et_infos['imagetype'])) {
case 'image/jpeg':
$image_et_infos['image'] = imagecreatefromjpeg($chemin_fichier);
break;
case 'image/png':
$image_et_infos['image'] = imagecreatefrompng($chemin_fichier);
break;
default:
// ni jpeg ni png donc pas supporté, ça dégage
return false;
}
}
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 {
// protection contre division par 0 - prob. symptôme d'un autre pb : image vide ou mal transmise,
// voir TODO dans obtenirImageEtInfosPourChemin()
$rapport = $informations_images['hauteur'] == 0 ? 0 : $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);
 
// déterminationnage de l'extension
$path = $this->obtenirDossierPourFormat($id, $format);
foreach(['jpg', 'JPG', 'jpeg', 'JPEG', 'png', 'PNG'] as $ext) {
if (file_exists($path.'/'.$id_avec_zeros_underscores.'_'.$format.'.'.$ext)) {
$extension = $ext;
break;
}
}
 
$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) {
try {
$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);
return true;
} catch (ImagickException $e) {
return false;
}
}
 
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'];
 
// 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");
try {
$img = new Imagick($chemin_image); // ce machin jette une ImagickException si le fichier est vide
$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;
}
} catch (ImagickException $e) {
// En principe si on se trouve ici c'est que l'image est vide
$erreur_ecriture = true;
$image_sortie = false;
}
 
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 {
// source AR wider than target
// 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;
 
$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']);
// retailler correctement si au moins une des dimensions de l'image produite est
// inférieure aux dimensions demandées
$geo = $imgcp->getImageGeometry();
$ratio_la = $geo['width'] / $largeur_vignette;
$ratio_ha = $geo['height'] / $hauteur_vignette;
if ($ratio_la < 1 || $ratio_ha < 1) {
// cas kipu - on agrandit au max la dimension ayant le ratio le plus faible
// (= le plus d'écart avec la taille attendue)
if ($ratio_la < $ratio_ha) {
$imgcp->scaleImage($largeur_vignette, 0);
} else {
$imgcp->scaleImage(0, $hauteur_vignette);
}
// puis on recoupe en centrant à l'arrache - bizarre que le script ne fasse pas ça de base :-/
$geo = $imgcp->getImageGeometry();
$imgcp->cropImage($largeur_vignette, $hauteur_vignette, max(0, floor($geo['width'] / 2 - $largeur_vignette / 2)), max(0, floor($geo['height'] / 2 - $hauteur_vignette / 2)));
} else {
// cas général
$imgcp->scaleImage($largeur_vignette, $hauteur_vignette, false);
}
$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;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/DecoupageNomLatin.php
New file
0,0 → 1,369
<?php
// declare(encoding='UTF-8');
/**
* Classe de découpage des noms latins.
*
* @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 DecoupageNomLatin extends Decoupage {
 
private $expression_principale = array();
private $expression_complement = array();
 
public function __construct() {
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'];
}
}
/branches/v3.01-serpe/jrest/bibliotheque/CelRestClient.php
New file
0,0 → 1,161
<?php
// declare(encoding='UTF-8');
/**
* Client REST chargé depuis la classe CEL.
*
* @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>
*/
// TODO : remplacer les trigger_error par des exceptions qui pourrait être attrapées...
class CelRestClient {
const HTTP_URL_REQUETE_SEPARATEUR = '&';
const HTTP_URL_REQUETE_CLE_VALEUR_SEPARATEUR = '=';
private $http_methodes = array('GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE');
protected $parametres = null;
private $url = null;
private $reponse_entetes = null;
 
//+----------------------------------------------------------------------------------------------------------------+
// ACCESSEURS
 
public function getReponseEntetes($cle) {
return $this->reponse_entetes;
}
 
public function getParametre($cle) {
$valeur = (isset($this->parametres[$cle])) ? $this->parametres[$cle] : null;
return $valeur;
}
 
public function ajouterParametre($cle, $valeur) {
$this->parametres[$cle] = $valeur;
}
 
public function supprimerParametre($cle) {
unset($this->parametres[$cle]);
}
 
public function nettoyerParametres() {
$this->parametres = null;
}
 
//+----------------------------------------------------------------------------------------------------------------+
// MÉTHODES
 
public function consulter($url) {
$retour = $this->envoyerRequete($url, 'GET');
return $retour;
}
 
public function ajouter($url, Array $donnees) {
$retour = $this->envoyerRequete($url, 'PUT', $donnees);
return $retour;
}
 
public function modifier($url, Array $donnees) {
$retour = $this->envoyerRequete($url, 'POST', $donnees);
return $retour;
}
 
public function supprimer($url) {
$retour = $this->envoyerRequete($url, 'DELETE');
return $retour;
}
 
public function envoyerRequete($url, $mode, Array $donnees = array()) {
$this->url = $url;
$contenu = false;
if (! in_array($mode, $this->http_methodes)) {
$e = "Le mode de requête '$mode' n'est pas accepté!";
trigger_error($e, E_USER_WARNING);
} else {
if ($mode == 'GET') {
$this->traiterUrlParametres();
}
$contexte = stream_context_create(array(
'http' => array(
'method' => $mode,
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($donnees, null, self::HTTP_URL_REQUETE_SEPARATEUR))));
$flux = @fopen($this->url, 'r', false, $contexte);
if (!$flux) {
$this->reponse_entetes = $http_response_header;
$e = "L'ouverture de l'url '{$this->url}' par la méthode HTTP '$mode' a échoué!";
trigger_error($e, E_USER_WARNING);
} else {
// Informations sur les en-têtes et métadonnées du flux
$this->reponse_entetes = stream_get_meta_data($flux);
 
// Contenu actuel de $url
$contenu = stream_get_contents($flux);
 
fclose($flux);
}
$this->traiterEntete();
}
$this->reinitialiser();
return $contenu;
}
 
private function traiterUrlParametres() {
$parametres = array();
if (count($this->parametres) > 0) {
foreach ($this->parametres as $cle => $valeur) {
$cle = rawurlencode($cle);
$valeur = rawurlencode($valeur);
$parametres[] = $cle.self::HTTP_URL_REQUETE_CLE_VALEUR_SEPARATEUR.$valeur;
}
$url_parametres = implode(self::HTTP_URL_REQUETE_SEPARATEUR, $parametres);
$this->url = $this->url.'?'.$url_parametres;
}
}
 
private function traiterEntete() {
$infos = $this->analyserEntete();
$this->traiterEnteteDebogage($infos);
}
 
private function analyserEntete() {
$entetes = $this->reponse_entetes;
$infos = array('date' => null, 'uri' => $this->url, 'debogages' => null);
if (isset($entetes)) {
if (isset($entetes['wrapper_data'])) {
$entetes = $entetes['wrapper_data'];
}
foreach ($entetes as $entete) {
if (preg_match('/^X_REST_DEBOGAGE_MESSAGES: (.+)$/', $entete, $match)) {
$infos['debogages'] = json_decode($match[1]);
}
if (preg_match('/^Date: .+ ([012][0-9]:[012345][0-9]:[012345][0-9]) .*$/', $entete, $match)) {
$infos['date'] = $match[1];
}
}
}
return $infos;
}
 
private function traiterEnteteDebogage($entetes_analyses) {
if (isset($entetes['debogages'])) {
$date = $entetes['date'];
$uri = $entetes['uri'];
$debogages = $entetes['debogages'];
foreach ($debogages as $debogage) {
$e = "DEBOGAGE : $date - $uri :\n$debogage";
trigger_error($e, E_USER_NOTICE);
}
}
}
 
private function reinitialiser() {
$this->nettoyerParametres();
}
}
/branches/v3.01-serpe/jrest/bibliotheque/CartoGroupage.php
New file
0,0 → 1,290
<?php
// declare(encoding='UTF-8');
/**
* Classe de groupage des obs par quadtree pour la carto.
*
* @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 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);
// Les stations ne doivent être comptées que la première fois !
if ($emplacement != 'stations' || $profondeur == 0) {
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);
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionUtilisateur.php
New file
0,0 → 1,271
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion des utilisateurs
*
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @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-2015 Tela Botanica <accueil@tela-botanica.org>
*/
class GestionUtilisateur extends Cel {
 
/** contient le jeton SSO décodé, si une authentification a eu lieu avec succès */
protected $token_decode;
 
public function obtenirIdentiteConnectee() {
$login_utilisateur = $this->getIdUtilisateurVerifie();
if ($login_utilisateur) {
$utilisateur['connecte'] = true;
$utilisateur['id_utilisateur'] = $login_utilisateur;
} else {
$utilisateur = $this->getUtilisateurAnonyme();
}
return $utilisateur;
}
 
/**
* Retourne l'utilisateur demandé, seulement s'il s'il est connecté au SSO
* (le nom de la méthode est trompeur @TODO renommer); retourne false si
* aucun jeton SSO n'est disponible
*/
public function obtenirUtilisateurSiExiste($login_utilisateur) {
$utilisateur = $this->getUtilisateurAnonyme();
if ($utilisateur_existe = $this->chargerInfosUtilisateur($login_utilisateur)) {
$utilisateur = $utilisateur_existe;
$utilisateur['connecte'] = true;
}
return $utilisateur;
}
 
 
 
 
 
/**
* Répercute le nom et le prénom contenus dans le jeton SSO (si au
* moins un des deux n'est pas vide) dans toutes les observations
* et images de l'auteur; si le jeton SSO est vide, ne fait rien
* (boulette-proof)
*
* @TODO gérer l'intitulé un jour
*/
protected function mettreAJourCoordonneesDansObsEtImages() {
//echo "Mise à jour obs et images !!";
if ($this->token_decode != null && $this->token_decode['id'] != '' && ($this->token_decode['nom'] != '' || $this->token_decode['prenom'] != '')) {
$requete = 'UPDATE cel_obs SET'
. ' nom_utilisateur = ' . Cel::db()->proteger($this->token_decode['nom']) . ', '
. ' prenom_utilisateur = ' . Cel::db()->proteger($this->token_decode['prenom'])
. ' WHERE ce_utilisateur = ' . Cel::db()->proteger($this->token_decode['id']) // s'assurer qu'il y a des ' autour de l'ID sans quoi les hash MD5 matcheront !
. ' -- '.__FILE__.':'.__LINE__
;
//var_dump($requete);
Cel::db()->executer($requete);
$requete = 'UPDATE cel_images SET'
. ' nom_utilisateur = ' . Cel::db()->proteger($this->token_decode['nom']) . ', '
. ' prenom_utilisateur = ' . Cel::db()->proteger($this->token_decode['prenom'])
. ' WHERE ce_utilisateur = ' . Cel::db()->proteger($this->token_decode['id']) // s'assurer qu'il y a des ' autour de l'ID sans quoi les hash MD5 matcheront !
. ' -- '.__FILE__.':'.__LINE__
;
//var_dump($requete);
Cel::db()->executer($requete);
}
}
 
 
 
// @TODO a l'air inutilisée (2017-01-02) - vérifier
private function utilisateurEstAutorise($id_utilisateur) {
$autorise = false;
$token = $this->getToken();
// TODO: tester si le jeton contient réelement quelque chose ?
if($token) {
// On demande à l'annuaire si le jeton est bien valide
$valide = file_get_contents($this->config['identification']['sso_url'].'/verifierjeton?token='.$token);
$token_decode = $this->decoderToken($token);
// Si l'utilisateur du token est bien le même que celui sur lequel on veut agir : OK
if($token_decode['id'] == $id_utilisateur) {
$autorise = true;
} else {
// Sinon on vérifie que l'utilisateur est admin
$requete = "SELECT admin FROM cel_utilisateurs_infos WHERE id_utilisateur = ".Cel::db()->proteger($token_decode['id']);
$resultat = Cel::db()->requeter($requete);
$admin = false;
if ($resultat && count($resultat) > 0) {
$autorise = ($resultat[0]['admin'] == 1);
}
}
} else {
// pas de token, on vérifie bien qu'il s'agit d'une session temporaire
$autorise = ($id_utilisateur == session_id());
}
return $autorise;
}
// renvoie false ou bien le login utilisateur actuel
private function getLoginUtilisateurVerifie() {
$token = $this->getToken();
$login = false;
if($token) {
// On demande à l'annuaire si le jeton est bien valide
 
// curl avec les options suivantes ignore le pb de certificat (pour tester en local)
// @TODO CHANGER !
$ch = curl_init();
$timeout = 5;
$url = $this->config['identification']['sso_url'].'/verifierjeton?token='.$token;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// équivalent de "-k"
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$valide = curl_exec($ch);
curl_close($ch);
 
//$valide = file_get_contents($this->config['identification']['sso_url'].'/verifierjeton?token='.$token);
$login = ($valide === "true") ? $this->obtenirLoginParToken($token) : false;
}
return $login;
}
// renvoie false ou bien le login utilisateur actuel
private function getIdUtilisateurVerifie() {
$token = $this->getToken();
$login = false;
if($token) {
// On demande à l'annuaire si le jeton est bien valide
// curl avec les options suivantes ignore le pb de certificat (pour tester en local)
// @TODO CHANGER !
$ch = curl_init();
$timeout = 5;
$url = $this->config['identification']['sso_url'].'/verifierjeton?token='.$token;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// équivalent de "-k"
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$valide = curl_exec($ch);
curl_close($ch);
//$valide = file_get_contents($this->config['identification']['sso_url'].'/verifierjeton?token='.$token);
$login = ($valide === "true") ? $this->obtenirIdParToken($token) : false;
}
return $login;
}
 
// @WTF doublon avec la méthode du dessus ? Qu'est-ce que c'est que ce bins ?
protected function verifierJeton($jeton) {
$urlServiceVerification = $this->conteneur->getParametre("urlServiceBaseAuth") . "verifierjeton";
$urlServiceVerification .= "?token=" . $jeton;
 
// file_get_contents râle si le certificat HTTPS est auto-signé
//$retour = file_get_contents($urlServiceVerification);
 
// curl avec les options suivantes ignore le pb de certificat (pour tester en local)
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $urlServiceVerification);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// équivalent de "-k"
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
curl_close($ch);
$retour = $data;
 
$retour = json_decode($retour, true);
 
return ($retour === true);
}
private function decoderToken($token) {
$token_parts = explode('.', $token);
return json_decode($this->urlsafeB64Decode($token_parts[1]), true);
}
 
/**
* Decode a string with URL-safe Base64.
* copié depuis firebase/jwt
*
* @param string $input A Base64 encoded string
* @return string A decoded string
*/
protected function urlsafeB64Decode($input) {
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
//retourne l'adresse mail
private function obtenirLoginParToken($token) {
$this->token_decode = $this->decoderToken($token);
return $this->token_decode['sub'];
}
// retourne l'id
private function obtenirIdParToken($token) {
$this->token_decode = $this->decoderToken($token);
return $this->token_decode['id'];
}
private function getToken() {
// Premier essai, dans le header
$headers = apache_request_headers();
$token = !empty($headers['Authorization']) ? $headers['Authorization'] : null;
// Sinon dans $_REQUEST ?
if($token == null) {
$token = !empty($_REQUEST['Authorization']) ? $_REQUEST['Authorization'] : null;
}
$token = str_replace('Bearer ', '', $token);
return $token;
}
private function getUtilisateurAnonyme() {
return array('connecte' => false,
'id_utilisateur' => session_id(),
'courriel' => '',
'mot_de_passe' => '',
'nom' => '',
'prenom' => '',
'licence_acceptee' => false,
'preferences_utilisateur' => '',
'admin' => false
);
}
 
}
 
/**
* Compatibilité avec nginx - merci http://php.net/manual/fr/function.getallheaders.php
*/
if (! function_exists('apache_request_headers')) {
function apache_request_headers() {
$headers = '';
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
?>
/branches/v3.01-serpe/jrest/bibliotheque/Cel.php
New file
0,0 → 1,738
<?php
// declare(encoding='UTF-8');
/**
* Classe mère abstraite contenant les méthodes génériques des services.
*
* @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>
*/
// TODO : il faudrait déplacer les méthodes des sections de cette classe dans des classes séparées chargées via un Conteneur.
abstract class Cel {
const ARRET_SERVICE = false;
 
const TYPE_OBS = 'observation';
const TYPE_IMG = 'image';
 
// TODO: delete wrappers
const SQL_MODE_ASSOC = Bdd::SQL_MODE_ASSOC;
const SQL_MODE_OBJET = Bdd::SQL_MODE_OBJET;
const SQL_RETOUR_COMPLET = Bdd::SQL_RETOUR_COMPLET;
const SQL_RETOUR_LIGNE = Bdd::SQL_RETOUR_LIGNE;
const SQL_RETOUR_COLONNE = Bdd::SQL_RETOUR_COLONNE;
const SQL_RETOUR_BRUT = Bdd::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();
static $default_referentiel = null;
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']);
// Chargement des référentiels disponibles
self::$referentiels_valides = array_keys($this->config['referentiels']);
self::$default_referentiel = $this->config['cel']['referentiel_defaut'];
// Connection à la base de données
if (self::$bdd === null) { // singleton à l'arrache
self::$bdd = new Bdd($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 Bdd($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) {
if ($val === null) {
$tableau[$id] = 'NULL';
} else {
$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_infos 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_infos 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_infos '.
"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_infos '.
"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 (self::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][0-9])|2A|2B)[0-9]{3}/i',$code_a_tester);
}
 
protected function convertirCodeZoneGeoVersCodeInsee($code_zone_geo) {
$code_departement = '';
if (self::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;
}
public function formaterZoneGeoEtCodePourAffichage($obs) {
$code_lieu = self::convertirCodeZoneGeoVersCodeInsee($obs['ce_zone_geo']);
if(empty($code_lieu) && !empty($obs['pays'])) {
$code_lieu = $obs['pays'];
}
if(!empty(trim($obs['zone_geo'])) && !empty(trim($code_lieu))) {
$code_lieu = ' ('.$code_lieu.') ';
}
return $obs['zone_geo'].$code_lieu;
}
 
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) {
$sous_taxons = array();
// Certains référentiels n'ont pas de nt
if(!empty($nt) && is_numeric($nt) && $nt > 0) {
$sous_taxons = $this->obtenirSousTaxons($referentiel, 'nt:'.$nt);
}
return $sous_taxons;
}
 
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) {
return file_get_contents($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;
}
}
?>
/branches/v3.01-serpe/jrest/bibliotheque/FormateurGroupeColonne.php
New file
0,0 → 1,931
<?php
// declare(encoding='UTF-8');
/**
* Classe métier de mise en forme des groupes de colonnes pour les exports.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botania.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>
*/
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',
'standardexport',
'auteur',
'avance',
'etendu',
'baseflor'
);
 
// 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', $programme = "") {
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;
}
if ($programme != "") {
$groupe_de_champs['avance'] = true;
$groupe_de_champs['etendu'] = true;
}
// 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['standardexport'])) {
$colonnes += Array(
'guid' => self::GenColInfo(Array('abbrev' => 'guid',
'nom' => 'Identifiant unique')),
'id_observation' => self::GenColInfo(Array('abbrev' => 'id_observation',
'nom' => 'Identifiant',
'extra' => 1,
'importable' => FALSE)),
'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)),
'famille' => self::GenColInfo(Array('abbrev' => 'famille',
'nom' => 'Famille',
'importable' => FALSE)),
'nom_referentiel' => self::GenColInfo(Array('abbrev' => 'nom_referentiel',
'nom' => 'Référentiel taxonomique')),
'certitude' => self::GenColInfo(Array('abbrev' => 'certitude',
'nom' => 'Certitude')),
'type_donnees' => self::GenColInfo(Array('abbrev' => 'type_donnees',
'nom' => 'Type d\'observation')),
'source' => self::GenColInfo(Array('abbrev' => 'source',
'nom' => 'Source de la saisie')),
'spontaneite' => self::GenColInfo(Array('abbrev' => 'spontaneite',
'nom' => 'Spontanéité')),
'latitude' => self::GenColInfo(Array('abbrev' => 'latitude',
'nom' => 'Latitude',
'extra' => 1)),
'longitude' => self::GenColInfo(Array('abbrev' => 'longitude',
'nom' => 'Longitude',
'extra' => 1)),
 
'geodatum' => self::GenColInfo(Array('abbrev' => 'geodatum',
'nom' => 'Référentiel Géographique',
'extra' => 1,
'importable' => FALSE)),
'pays' => self::GenColInfo(Array('abbrev' => 'pays',
'nom' => 'Pays')),
'zone_geo' => self::GenColInfo(Array('abbrev' => 'zone_geo',
'nom' => 'Commune')),
'ce_zone_geo' => self::GenColInfo(Array('abbrev' => 'ce_zone_geo',
'nom' => 'Identifiant Commune')),
'localisation_floutage' => self::GenColInfo(Array('abbrev' => 'localisation_floutage',
'nom' => 'Floutage (niveau de localisation diffusé)')),
'altitude' => self::GenColInfo(Array('abbrev' => 'altitude',
'nom' => 'Altitude',
'extra' => 1)), // pas de trim0 car INT(5) en DB
'lieudit' => self::GenColInfo(Array('abbrev' => 'lieudit',
'nom' => 'Lieu-dit')),
'milieu' => self::GenColInfo(Array('abbrev' => 'milieu',
'nom' => 'Milieu')),
'date_observation' => self::GenColInfo(Array('abbrev' => 'date_observation',
'nom' => 'Date',
'fonction' => 'formaterDate')),
'commentaire' => self::GenColInfo(Array('abbrev' => 'commentaire',
'nom' => 'Notes')),
'programme' => self::GenColInfo(Array('abbrev' => 'programme',
'nom' => 'Programme de sciences participatives ou observatoire citoyen',
'importable' => FALSE)),
'mots_cles_texte' => self::GenColInfo(Array('abbrev' => 'mots_cles_texte',
'nom' => 'Mots Clés',
'importable' => FALSE)),
'url_identiplante' => self::GenColInfo(Array('abbrev' => 'url_identiplante',
'nom' => "Lien vers l'observation sur IdentiPlante",
'importable' => FALSE)),
'images' => self::GenColInfo(Array('abbrev' => 'images',
'nom' => 'Image(s)',
'extra' => 1)),
);
} elseif (isset($groupe_de_champs['standard'])) {
$colonnes += Array(
'guid' => self::GenColInfo(Array('abbrev' => 'guid',
'nom' => 'Identifiant unique')),
'id_observation' => self::GenColInfo(Array('abbrev' => 'id_observation',
'nom' => 'Identifiant',
'extra' => 1,
'importable' => FALSE)),
'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)),
'famille' => self::GenColInfo(Array('abbrev' => 'famille',
'nom' => 'Famille',
'importable' => FALSE)),
'nom_referentiel' => self::GenColInfo(Array('abbrev' => 'nom_referentiel',
'nom' => 'Référentiel taxonomique')),
'certitude' => self::GenColInfo(Array('abbrev' => 'certitude',
'nom' => 'Certitude')),
'type_donnees' => self::GenColInfo(Array('abbrev' => 'type_donnees',
'nom' => 'Type d\'observation')),
'source' => self::GenColInfo(Array('abbrev' => 'source',
'nom' => 'Source de la saisie')),
'spontaneite' => self::GenColInfo(Array('abbrev' => 'spontaneite',
'nom' => 'Spontanéité')),
'latitude' => self::GenColInfo(Array('abbrev' => 'latitude',
'nom' => 'Latitude',
'extra' => 1)),
'longitude' => self::GenColInfo(Array('abbrev' => 'longitude',
'nom' => 'Longitude',
'extra' => 1)),
'geodatum' => self::GenColInfo(Array('abbrev' => 'geodatum',
'nom' => 'Référentiel Géographique',
'extra' => 1,
'importable' => FALSE)),
'pays' => self::GenColInfo(Array('abbrev' => 'pays',
'nom' => 'Pays')),
'zone_geo' => self::GenColInfo(Array('abbrev' => 'zone_geo',
'nom' => 'Commune')),
'ce_zone_geo' => self::GenColInfo(Array('abbrev' => 'ce_zone_geo',
'nom' => 'Identifiant Commune')),
'localisation_floutage' => self::GenColInfo(Array('abbrev' => 'localisation_floutage',
'nom' => 'Floutage (niveau de localisation diffusé)')),
'altitude' => self::GenColInfo(Array('abbrev' => 'altitude',
'nom' => 'Altitude',
'extra' => 1)), // pas de trim0 car INT(5) en DB
'lieudit' => self::GenColInfo(Array('abbrev' => 'lieudit',
'nom' => 'Lieu-dit')),
'milieu' => self::GenColInfo(Array('abbrev' => 'milieu',
'nom' => 'Milieu')),
'date_observation' => self::GenColInfo(Array('abbrev' => 'date_observation',
'nom' => 'Date',
'fonction' => 'formaterDate')),
'commentaire' => self::GenColInfo(Array('abbrev' => 'commentaire',
'nom' => 'Notes')),
'programme' => self::GenColInfo(Array('abbrev' => 'programme',
'nom' => 'Programme de sciences participatives ou observatoire citoyen',
'importable' => FALSE)),
'mots_cles_texte' => self::GenColInfo(Array('abbrev' => 'mots_cles_texte',
'nom' => 'Mots Clés',
'importable' => FALSE)),
'images' => self::GenColInfo(Array('abbrev' => 'images',
'nom' => 'Image(s)',
'extra' => 1)),
'url_identiplante' => self::GenColInfo(Array('abbrev' => 'url_identiplante',
'nom' => "Lien vers l'observation sur IdentiPlante",
'importable' => FALSE)),
);
}
 
if (isset($groupe_de_champs['auteur'])) {
$colonnes += array(
'auteur' => self::GenColInfo(array(
'abbrev' => 'pseudo_utilisateur',
'nom' => 'Auteur',
'extra' => 1,
'fonction_data' => 'formaterUrlUser',
'importable' => false)),
);
}
if(isset($groupe_de_champs['avance'])) {
$colonnes += array(
'localisation_coherence' => self::GenColInfo(Array('abbrev' => 'localisation_coherence',
'nom' => 'Cohérence entre la localité et les coordonnées GPS')),
'localisation_precision' => self::GenColInfo(Array('abbrev' => 'localisation_precision',
'nom' => 'Précision de la localisation')),
'station' => self::GenColInfo(Array('abbrev' => 'station',
'nom' => 'Station')),
'grade' => self::GenColInfo(Array('abbrev' => 'grade',
'nom' => 'Indicateur de fiabilité',
'importable' => FALSE)),
'validation_identiplante' => self::GenColInfo(Array('abbrev' => 'validation_identiplante',
'nom' => 'Détermination validée sur IdentiPlante',
'importable' => FALSE)),
'score_identiplante' => self::GenColInfo(Array('abbrev' => 'score_identiplante',
'nom' => 'Score IdentiPlante',
'importable' => FALSE)),
'abondance' => self::GenColInfo(Array('abbrev' => 'abondance',
'nom' => 'Abondance',
'extra' => 1)),
'phenologie' => self::GenColInfo(Array('abbrev' => 'phenologie',
'nom' => 'Phénologie',
'extra' => 1)),
'herbier' => self::GenColInfo(Array('abbrev' => 'herbier',
'nom' => 'Présence d\'un échantillon d\'herbier',
'extra' => 1)),
// TODO: importable = FALSE car pas de merge de données importées
'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)),
'observateur' => self::GenColInfo(Array('abbrev' => 'observateur',
'nom' => 'Observateur',
'extra' => 1)),
'observateur_structure' => self::GenColInfo(Array('abbrev' => 'observateur_structure',
'nom' => 'Structure de l\'observateur',
'extra' => 1)),
'determinateur' => self::GenColInfo(Array('abbrev' => 'determinateur',
'nom' => 'Déterminateur',
'extra' => 1)),
'biblio' => self::GenColInfo(Array('abbrev' => 'biblio',
'nom' => 'Source bibliographique',
'extra' => 1)),
 
 
/* '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), */ /* cas particu 'getNomCommun_v4' */
/*'nom-commun' => self::GenColInfo(array(
'abbrev' => 'nom-commun',
'nom' => 'Nom Commun',
'extra' => 1,
'fonction_data' => null ,
'preload' => array(__CLASS__, 'getNomCommun_preload')))*/
);
}
 
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['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'))),
);
}
 
 
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);
} else if(method_exists(__CLASS__, $colonne['fonction'])) {
$valeur = call_user_func(array(__CLASS__, $colonne['fonction']), $valeur);
} else if($colonne['fonction']) {
die("méthode {$colonne['fonction']} introuvable");
} else if(function_exists($colonne['fonction_data'])) {// fonction pour obtenir des champs (étendus)
$valeur = $colonne['fonction_data']($obs);
} else if(method_exists(__CLASS__, $colonne['fonction_data'])) {
$valeur = call_user_func(array(__CLASS__, $colonne['fonction_data']), $obs);
}
 
// ici à cause du passage de $cel ($this->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;
}
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;
}
 
 
 
 
 
/**
* Enlève les zéros excédentaires (devenus nomades) au début et à la fin d'une
* latitude ou longitude, en prenant garde à ne pas foirer les nombres 0.xyz
*/
public static function trim0($lonlat) {
$retour = trim($lonlat, "0");
// si on a trop enlevé de zéros à gauche, on en remet un (mode synthobois)
if (substr($retour, 0, 1) == ".") {
$retour = "0" . $retour;
}
return $retour;
}
 
public static function boolOuiNon($transmission) {
return $transmission ? 'oui' : '';
}
 
static function formaterUrlUser($obs) {
$is_id = is_numeric($obs['ce_utilisateur']);
return sprintf("%s %s",
$obs['pseudo_utilisateur'],
$is_id ? sprintf(' (' . USER_BASEURL . ')', $obs['ce_utilisateur']) : '');
}
 
// 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;
}
 
 
 
 
 
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) {
// obs étendues
$gestion_obs_etendus = new GestionChampsEtendus($cel->config);
// champs étendus
$gestion_champs_etendus = new GestionChampsEtendus2($cel->config);
$colonnes_champs_supp_par_obs = $gestion_obs_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_obs_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;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/RechercheObservation.php
New file
0,0 → 1,397
<?php
// declare(encoding='UTF-8');
/**
* Classe 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' // On désactive ordre qui est le tri par défaut, car ça pédale trop si l'user à trop d'obs
);
 
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 ';
}
 
// construction du tri
$order_by_clause = '';
if (isset($criteres['tri']) && in_array($criteres['tri'], self::$tris_possibles)) {
$order_by_clause = ' ORDER BY ' . $criteres['tri'];
 
if (isset($criteres['tri_dir']) && in_array($criteres['tri_dir'], array('ASC', 'DESC'))) {
$order_by_clause .= ' ' . $criteres['tri_dir'];
}
}
unset($criteres['tri']);
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_clause;
$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 "programme";
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 'pays':
if ($valeur == "NULL") {
$sous_requete .= "(pays IS NULL OR pays = '')";
} else {
$pays_t = explode(',', $valeur);
foreach($pays_t as &$pays) {
$pays = Cel::db()->proteger($pays);
}
$sous_requete .= '(pays IN ('.implode(',', $pays_t).')) ';
}
$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 'localite':
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;
case 'validation_identiplante':
$sous_requete .= "`id_observation` in (SELECT distinct `ce_observation` FROM tb_del.`del_commentaire` WHERE `proposition_retenue` = 1 )";
$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("");
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionMotsClesChemin.php
New file
0,0 → 1,483
<?php
// declare(encoding='UTF-8');
/**
* Classe de liaison d'images et d'observation à des mots clés en utilisant la méthode path enumeration.
*
* @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 GestionMotsClesChemin {
 
private $config;
private $mode;
 
private $table_liaison;
private $table_mots_cles;
private $id_mot_cle;
private $id_element_lie;
 
//TODO: trigger pour les tables liaisons
 
public function __construct($config, $mode = 'obs') {
$this->config = $config;
//TODO: switch suivant mode
$this->mode = $mode;
 
list($this->table_liaison, $this->table_mots_cles, $this->id_mot_cle, $this->id_element_lie) = self::getTablesMotsClesEtLiaisons($mode);
}
 
 
 
public function obtenirIdsMotsClesParIdParent($user_id, $id_mot_cle) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$idUtilisateurP = Cel::db()->proteger($user_id);
 
$sousRequete = "SELECT path FROM {$this->table_mots_cles} WHERE $this->id_mot_cle = $idMotCleP ";
$requete = "SELECT {$this->id_mot_cle} ".
"FROM {$this->table_mots_cles} ".
"WHERE path LIKE CONCAT(($sousRequete), '%') ".
"AND user_id = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
return Cel::db()->requeter($requete);
}
 
/**
* 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 {$this->id_mot_cle} FROM {$this->table_liaison} ".
"WHERE {$this->id_element_lie} = $idElementLieP ".
' -- '.__FILE__.':'.__LINE__;
return Cel::db()->requeter($requete);
}
 
public function insererParCheminSiInexistant($mot_cle, $chemin_parent, $user_id) {
$mot_cle = self::simplifier($mot_cle);
$motCleP = Cel::db()->proteger($mot_cle);
$idUtilisateurP = Cel::db()->proteger($user_id);
 
$requete = "SELECT id ".
"FROM {$this->table_mots_cles} ".
"WHERE name = $motCleP".
"AND user_id = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
$infosMotCle = Cel::db()->requeter($requete);
 
if (!empty($infosMotCle) && $infosMotCle !== false) {
$idMotCle = $infosMotCle[0]['id'];
} else {
$idMotCle = $this->insererParChemin($mot_cle, $chemin_parent, $user_id);
}
return $idMotCle;
}
 
public function insererParChemin($mot_cle, $chemin_parent, $user_id) {
$mot_cle = self::simplifier($mot_cle);
$cheminMotCle = self::getCheminHarmonise($chemin_parent, $mot_cle);
$cheminMotCleP = Cel::db()->proteger($cheminMotCle);
$idUtilisateurP = Cel::db()->proteger($user_id);
$motCleP = Cel::db()->proteger($mot_cle);
 
$requete = "INSERT INTO {$this->table_mots_cles} ".
'(path, user_id, name) '.
"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, $user_id) {
$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($user_id);
$motCleP = Cel::db()->proteger($mot_cle);
 
$sousRequete = $racineP;
if ($id_parent != '') {
$sousRequete = '(SELECT path '.
"FROM {$this->table_mots_cles} AS ctp ".
"WHERE ctp.id = $idParentP) ";
}
 
$requete = "INSERT INTO {$this->table_mots_cles} (path, user_id, name) ".
"VALUES (CONCAT($sousRequete, $motCleSimpleP, '/'), $idUtilisateurP, $motCleP ) ".
' -- '.__FILE__.':'.__LINE__;
$insertion = Cel::db()->executer($requete);
 
if ($insertion !== false) {
$resultat = Cel::db()->obtenirDernierId();
}
return $resultat;
}
 
 
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} ({$this->id_element_lie}, {$this->id_mot_cle}) ".
"VALUES $valeursGroupees ".
' -- '.__FILE__.':'.__LINE__;
return Cel::db()->executer($requete);
}
 
 
 
public function supprimerLiaisonsMotsCles($ids_mots_cles, $ids_elements_lies, $user_id) {
$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[] = "({$this->id_element_lie} = $idElementLieP AND {$this->id_mot_cle} = $idMotCleP)";
}
}
$clauseWhere = implode(' OR ', $combinaisons);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE $clauseWhere ".
' -- '.__FILE__.':'.__LINE__;
return Cel::db()->executer($requete);
}
 
public function supprimerToutesLiaisonsPourIdsElementsLies($ids_elements_lies) {
$idsElementsLiesP = Cel::db()->proteger($ids_elements_lies);
$listeIds = implode(',', $idsElementsLiesP);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE {$this->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, $user_id) {
$suppression = true;
if (!empty($ids_mots_cles)) {
$idsMotsClesP = Cel::db()->proteger($ids_mots_cles);
$listeIds = implode(',', $idsMotsClesP);
 
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE {$this->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, $user_id) {
$mot_cle = self::simplifier($mot_cle);
$idElementLieP = Cel::db()->proteger($id_element_lie);
$motCleP = Cel::db()->proteger($mot_cle);
$idUtilisateurP = Cel::db()->proteger($user_id);
 
$sousRequete = "SELECT id FROM {$this->table_mots_cles} ".
"WHERE name = $motCleP ".
"AND user_id = $idUtilisateurP ";
$requete = "DELETE FROM {$this->table_liaison} ".
"WHERE {$this->id_element_lie} = $idElementLieP ".
"AND {$this->id_mot_cle} IN ($sousRequete) ".
' -- '.__FILE__.':'.__LINE__;
 
$suppression_liaison = Cel::db()->executer($requete);
$suppression_liaison = ($suppression_liaison !== false);
 
return $suppression_liaison;
}
 
 
public function supprimerChemin($chemin, $user_id) {
$idUtilisateurP = Cel::db()->proteger($user_id);
$cheminP = Cel::db()->proteger($chemin.'%');
// TODO : triggers pour les tables liées ?
$requete = "DELETE FROM {$this->$table_mots_cles} ".
"WHERE path LIKE $cheminP ".
"AND user_id = $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, $user_id) {
//TODO: simplifier cette fonction
$ids_mot_cle_et_enfants = $this->obtenirIdsMotsClesParIdParent($user_id, $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, $user_id);
 
// suppression du mot clé proprement dit ainsi que de ses enfants
$suppression = $this->supprimerMotCleEtEnfantsParId($id_mot_cle, $user_id);
 
return $suppression && $suppression_liaison;
}
 
public function supprimerMotCleEtEnfantsParId($id_mot_cle, $user_id) {
$idMotCleP = Cel::db()->proteger($id_mot_cle);
$requete = 'SELECT path '.
"FROM {$this->table_mots_cles} ".
"WHERE id = $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($user_id);
 
$requete = "DELETE FROM {$this->table_mots_cles} ".
"WHERE path LIKE $cheminP ".
"AND user_id = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;;
 
$suppression = Cel::db()->executer($requete);
}
 
return ($suppression !== false);
 
}
 
public function obtenirIdsMotClesPourMotsCles($mots_cles, $user_id) {
$motsClesP = array();
foreach ($mots_cles as $mot_cle) {
$motsClesP[] = Cel::db()->proteger(self::simplifier($mot_cle));
}
$listeMotsClesP = implode(',', $motsClesP);
$idUtilisateurP = Cel::db()->proteger($user_id);
 
$requete = 'SELECT id as id_mot_cle, name as mot_cle '.
"FROM {$this->table_mots_cles} ".
"WHERE mot_cle IN ($listeMotsClesP) ".
"AND user_id = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
 
$resultat = Cel::db()->executerRequete($requete);
return $resultat;
}
 
 
 
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 {$this->id_element_lie} as id_element_lie ".
"FROM {$this->table_liaison} ".
"WHERE {$this->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 = 'occurrence_user_occurrence_tag';
$table_mots_cles = 'user_occurrence_tag';
$id_mot_cle = "user_occurrence_tag_id";
$id_element_lie = "occurrence_id";
} else {
$table_liaison = 'photo_tag_photo';
$table_mots_cles = 'photo_tag';
$id_mot_cle = "photo_tag_id";
$id_element_lie = "photo_id";
}
return array($table_liaison, $table_mots_cles, $id_mot_cle, $id_element_lie);
}
/**
* 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, $id_mot_cle, $id_element_lie) = self::getTablesMotsClesEtLiaisons($mode);
$requeteTpl = "SELECT $id_element_lie FROM $table_liaison WHERE $id_mot_cle IN (%s) ";
return $requeteTpl;
}
 
private static function getNomTablesEtChampsElementsLies($mode) {
$tables = array();
if ($mode == 'obs') {
$tables = array('occurrence', 'id');
} else {
$tables = array('photo', 'id');
}
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, $id_mot_cle, $id_element_lie) = self::getTablesMotsClesEtLiaisons($mode);
 
$requeteTpl = 'SELECT GROUP_CONCAT(name) '.
"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;
}
 
 
// Méthodes 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['path'], '/');
$nb_slashs_b = substr_count($a['path'], '/');
$cmp = 0;
 
if ($nb_slashs_a == $nb_slashs_b) {
$cmp = strcasecmp($a['path'], $b['path']);
} else {
$cmp = ($a['path'] > $b['path']) ? +1 : -1;
}
return $cmp;
}
 
static public function getCheminHarmonise($chemin_parent, $mot_cle) {
return self::harmoniserChemin($chemin_parent.'/');
}
 
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;
}
 
static 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;
}
 
public static function startsWith($haystack, $needle) {
return $needle === '' || strpos($haystack, $needle) === 0;
}
 
public static 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 user_id par le mail indiqué lors de la saisie
* @param string $mail_utilisateur
* @param string $user_id
*/
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 $user_id 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, $id_mot_cle, $id_element_lie) = self::getTablesMotsClesEtLiaisons($mode);
$idUtilisateurP = Cel::db()->proteger($infos_utilisateur['user_id']);
$emailUtilisateurP = Cel::db()->proteger($email_utilisateur);
 
$requete_migration_mc = "UPDATE {$table_mots_cles} ".
"SET user_id = $idUtilisateurP ".
"WHERE user_id = $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
Merged /branches/v2.8-houe/jrest/bibliotheque/GestionMotsClesChemin.php:r2477-2485
Added: svnkit:entry:sha1-checksum
+4a11bd4592bf9c437ecee299f5fce154aaf02f69
\ No newline at end of property
/branches/v3.01-serpe/jrest/bibliotheque/RechercheObservationExport.php
New file
0,0 → 1,330
<?php
// declare(encoding='UTF-8');
/**
* Classe 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 RechercheObservationExport extends Cel {
public $requete_selection_observations;
private $table = "cel_export";
public static $tris_possibles = array(
'nom_referentiel',
'nom_sel',
'certitude',
'nom_ret',
'famille',
'abondance',
'phenologie',
'ce_zone_geo',
'altitude',
'date_observation'
);
public function rechercherObservations($id_utilisateur = null, $criteres = array(), $debut = 0, $limite = 50, $autoriser_sql_brut = FALSE) {
if ((isset($criteres['standard']) && $criteres['standard'] == 0) || (!isset($criteres['standard']))) {
$this->table = "cel_export_total";
}
unset($criteres['standard']);
$requete_selection_observations = 'SELECT * FROM '.$this->table.' ';
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'] : 'id_observation';
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 .= ($debut == 0 && $limite == 0) ? '' : ' LIMIT '.$debut.','.$limite ;
$this->requete_selection_observations = $requete_selection_observations;//print_r($requete_selection_observations);exit;
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()) {
if ((isset($criteres['standard']) && $criteres['standard'] == 0) || (!isset($criteres['standard']))) {
$this->table = "cel_export_total";
}
unset($criteres['standard']);
$requete = 'SELECT COUNT(*) AS nb_obs FROM '.$this->table.' ';
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);//print_r($requete);exit;
$nb_obs = ($resultat && is_array($resultat) && count($resultat) > 0) ? $resultat[0]['nb_obs'] : '0';
return $nb_obs;
}
 
public function obtenirIdUtilisateurPourIdObs($id_obs) {
$idObsP = Cel::db()->proteger($id_obs);
$requete = 'SELECT ce_utilisateur '.
'FROM cel_export_total '.
"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 user_email as courriel_utilisateur FROM occurrence WHERE id = $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;
}
 
// 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 'pays':
if ($valeur == "NULL") {
$sous_requete .= "(pays IS NULL OR pays = '')";
} else {
$pays_t = explode(',', $valeur);
foreach($pays_t as &$pays) {
$pays = Cel::db()->proteger($pays);
}
$sous_requete .= '(pays IN ('.implode(',', $pays_t).')) ';
}
$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 .= '(dept IN (';
foreach($dpts as $dpt) {
$sous_requete .= Cel::db()->proteger($dpt).', ';
}
$sous_requete = rtrim($sous_requete,', ').')) ';
} else {
$sous_requete .= "(dept = ".Cel::db()->proteger($valeur).')';
}
}
$sous_requete .= ' AND ' ;
break;
case 'localite':
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 pseudo_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 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 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("");
}
}
/branches/v3.01-serpe/jrest/bibliotheque/ExtracteurMetadonnees.php
New file
0,0 → 1,525
<?php
// declare(encoding='UTF-8');
/**
* Classe d'extraction de metadonnées d'un fichier JPEG afin de les mettre dans un tableau au format du CEL.
*
* @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 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['date_shot'] = $this->obtenirDatePriseDeVue() ?: null;
$metadonnees['size'] = filesize($cheminImage);
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 obtenirDatePriseDeVue() {
$date = isset($this->meta['EXIF']['DateTimeOriginal']) ? $this->meta['EXIF']['DateTimeOriginal']['valeur'] : '';
return $date;
}
 
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionImage.php
New file
0,0 → 1,211
<?php
// declare(encoding='UTF-8');
/**
* Classe métier de gestion de l'ajout, modification et suppression des images.
*
* @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 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) {
$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['original_name'] = $infos_fichier['name'] ;
//$informations_image['size'] = $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['user_id'] = $id_utilisateur ;
//manque une fonction
$infos_utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
 
$informations_image['user_email'] = $infos_utilisateur['courriel'];
$informations_image['user_pseudo'] = $infos_utilisateur['pseudo'];
 
$requete = $this->construireRequeteInsertionImage($informations_image);
$resultat_insertion_infos_image = Cel::db()->executer($requete);
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, $informations_image['original_name']);
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);
 
// Remise à zéro d'idImage pour que l'erreur ne soit pas ignorée par l'appelant
$id_nouvelle_image = false;
}
 
return $id_nouvelle_image;
}
 
 
 
private function obtenirIdImagePourIdentifiantEtOrdre($id_utilisateur, $nom) {
$id_image = false;
$idUtilisateurP = Cel::db()->proteger($id_utilisateur);
$nomP = Cel::db()->proteger($nom);
$requete ='SELECT id as id_image '.
'FROM photo '.
"WHERE user_id = $idUtilisateurP ".
" AND original_name = $nomP ".
' -- '.__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) {
$infos = array(
'date_created ' => 'NOW()',
'date_updated ' => 'NOW()');
foreach ($informations_image as $champ => $valeur) {
$infos[$champ] = is_null($valeur) ? 'NULL' : Cel::db()->proteger($valeur);
}
$champs = implode(', ', array_keys($infos));
$valeurs = implode(', ', array_values($infos));
 
$requete = "INSERT INTO photo ($champs) ".
"VALUES ($valeurs) ".
' -- '.__FILE__.' : '.__LINE__;
return $requete;
}
 
/**
* Modifie les champs de metadonnées d'une image
*
* @param array $id_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($id_utilisateur, $id_image, $parametres) {
$champs_a_mettre_a_jour = $this->construireRequeteMajMetaDonnees($parametres);
 
$requete = "UPDATE photo SET $champs_a_mettre_a_jour ".
' WHERE id = '.Cel::db()->proteger($id_image).
' AND user_id = '.Cel::db()->proteger($id_utilisateur).
' -- '.__FILE__.' : '.__LINE__;
$resultat = Cel::db()->executer($requete);
return ($resultat !== 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) {
$champs_maj = array('date_modification = NOW()');
$champs_a_ignorer = array('id_image', 'date_modification');
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] ;
$dateP = Cel::db()->proteger($date);
$champs_maj[] = "$champ = $dateP";
} else {
$valeurP = Cel::db()->proteger($valeur);
$champs_maj[] = "$champ = $valeurP";
}
}
}
return implode(', ', $champs_maj);
}
 
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);
}
$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);
}
 
$requete = 'DELETE FROM photo '.
"WHERE id 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);
}
 
$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'a pas déjà d'images dans le CEL
// avec l'identifiant $id_utilisateur ce qui est normalement le cas
$requete = 'UPDATE photo SET '.
'user_id = '.Cel::db()->proteger($infos_utilisateur['id_utilisateur']).', '.
'user_pseudo = '.Cel::db()->proteger($infos_utilisateur['prenom']).', '.
'user_email = '.Cel::db()->proteger($infos_utilisateur['courriel']).' '.
'WHERE user_id = '.Cel::db()->proteger($mail_utilisateur).' '.
' -- '.__FILE__.' : '.__LINE__;
 
$migration_releve = Cel::db()->executer($requete);
return $migration_releve;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+5a59fd56b54b0de38897e58593678a1ea89405fa
\ No newline at end of property
/branches/v3.01-serpe/jrest/bibliotheque/GestionWidget.php
New file
0,0 → 1,121
<?php
// declare(encoding='UTF-8');
/**
* Classe métier de gestion de l'ajout, modification et suppression des images.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @author Delphine CAUQUIL <delphine@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-2017 Tela Botanica <accueil@tela-botanica.org>
*/
class GestionWidget extends Cel {
// le champ is_type / est_type est traité dans services/CelWidgetManager car traite les différentes valeurs anvoyés par l'interface
private $champs_defaut_valeur = array("date_created" => "CURRENT_TIMESTAMP",
"date_updated" => "CURRENT_TIMESTAMP",
"taxo_restriction_type" => "referentiel",
"taxo_restriction_value" => "bdtfx",
"location_type" => "point");
/**
* Ajoute un paramètrage de widget de saisie
*
* @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 obtenirWidget($parametres = array()) {
$requete_liste = "SELECT * FROM `project_settings` ";
$order = (isset($parametres['ordre'])) ? $parametres['ordre'] : 'project';
if ($parametres != array()) {
$requete_liste .= " WHERE ";
foreach ($parametres as $champ => $valeur) {
$requete_liste .= $champ." = '".$valeur."' AND ";
}
$requete_liste = rtrim($requete_liste, " AND ");
}
$requete_liste .= " ORDER BY ".$order;
$liste = Cel::db()->requeter($requete_liste);
return $liste;
}
 
/**
* Ajoute un paramètrage de widget de saisie
*
* @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 ajouterWidget($parametres) {
if (isset($parametres['type']) && $parametres['type'] != '') {
$rechercheId = $parametres['type'];
} else {
$rechercheId = $parametres['project'];
}
$requete_id = "SELECT project_id as idprojet FROM project_settings WHERE project = '".$parametres['project']."'";
$resultat = Cel::db()->requeter($requete_id);
if ($resultat == array()) {
$requete_id = "SELECT max(`id`) + 1 as idprojet FROM project_settings";
$resultat = Cel::db()->requeter($requete_id);
}
$project_id = $resultat[0]['idprojet'];
if (array_diff_key($this->champs_defaut_valeur, $parametres)) {
$parametres = array_merge($parametres, array_diff_key($this->champs_defaut_valeur, $parametres));
}
$requete_insertion = 'INSERT INTO project_settings '.
'(project_id, '.implode(array_keys($parametres), ', ').') VALUES ("'.$project_id.'", '.implode(Cel::db()->proteger($parametres), ', ').')';
$requete_insertion = str_replace("'CURRENT_TIMESTAMP'", 'CURRENT_TIMESTAMP', $requete_insertion);
$resultat_ajout = Cel::db()->executer($requete_insertion);
$retour = true;
if ($resultat_ajout === false) {
$retour = false;
$msg = "Erreur de creation d'un widget : $resultat_ajout";
$this->logger('CEL_bugs', $msg);
}
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 modifierWidget($projet, $langue, $parametres) {
$requete_insertion = 'UPDATE project_settings SET ';
$modifs = array_map(function($value, $key) {
return $key.'="'.$value.'"';
}, array_values($parametres), array_keys($parametres));
$requete_insertion .= implode(', ', $modifs).' WHERE project = "'.$projet.'" AND language = "'.$langue.'";' ;
//print_r($requete_insertion);
$resultat_ajout = Cel::db()->executer($requete_insertion);
$retour = true;
if ($resultat_ajout === false) {
$retour = false;
$msg = "Erreur de creation d'un widget : $resultat_ajout";
$this->logger('CEL_bugs', $msg);
} else {
if (isset($parametres['est_type']) && $parametres['est_type']) $this->modifierTypeWidget($projet, $parametres);
}
return $retour;
}
private function modifierTypeWidget($projet, $parametres) {
$params_type = array('type_localisation', 'type_espece', 'milieux','champs_supp');
//print_r(array_intersect_keys($parametres, $params_type));exit;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/RechercheInfosZoneGeo.php
New file
0,0 → 1,629
<?php
// declare(encoding='UTF-8');
/**
* Classe recherchant des infos sur une zone géo ou bien des coordonnées.
*
* @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 RechercheInfosZoneGeo extends Cel {
// @TODO: surveiller les conséquences du changement climatique :)
const POINT_EMERGE_LE_PLUS_BAS = -422;// mètres
 
public $maxTimeoutRequeteMondiale = 0;
/**
* Cas de la demande par lat et lon
* array(
* 'code_pays' => 'FR',
* 'code_zone' => 'INSEE-D:35800',
* 'type' => 'france',
* 'nom' => 'Taulignan-les-oies'
* );
*
* array(
* 'code_pays' => 'RU',
* 'code_zone' => 'OSM-ID:4511574',
* 'type' => 'monde',
* 'nom' => 'Moscou'
* );
*
* Cas de la demande par nom
* array(
* 'lat': 40.372934,
* 'lng': -3.690601,
* 'nom': 'Madrid',
* 'code_zone': 'OSM-ID:558474',
* 'code_pays': 'ES',
* 'type': 'monde'
* );
*
* array(
* 'lat': 48.860799,
* 'lng': 2.3457986,
* 'nom': 'Paris',
* 'code_zone': '75056',
* 'code_pays': 'FR',
* 'type': 'france'
* );
*
*/
 
//TODO: migrer tout ce qui concerne la zone géo dans cette classe
public function obtenirInfosPourCoordonnees($coordonnees) {
$infos_coord = null;
// Test facile qui permet d'abord de tenter une localisation bien plus rapide si les coordonnées
// sont dans la bounding box approximative de la France
if($this->testerCoordonneesWgsFrance($coordonnees['latitude'], $coordonnees['longitude'])) {
$infos_coord = $this->chercherInfosCommune('osm', $coordonnees['latitude'], $coordonnees['longitude']);
if ($infos_coord == null) {
// Sinon recherche par pays
$infos_coord = $this->chercherInfosPays($coordonnees['latitude'], $coordonnees['longitude']);
}
} else {
// Recherche par pays immédiate si en dehors de la bouding box française
$infos_coord = $this->chercherInfosPays($coordonnees['latitude'], $coordonnees['longitude']);
}
 
return array_merge($infos_coord,['lat' => $coordonnees['latitude'], 'lng' => $coordonnees['longitude']]);
}
 
// gère aussi les id_zone_geo @TODO renommer la méthode et internationaliser
// la gestion des codes
public function obtenirInfosPourNom($nom, $pays, $code_departement=null, $id_zone=null) {
$nom = trim($nom);
$pays = trim($pays);
$code_departement = trim($code_departement);
 
$retour = false;
// Dans le cas de la france on possède directement une table
// avec les centroïdes des communes
if($pays == 'FR' || $code_departement != "") {
$retour = $this->chercherCentroideCommuneBdd($nom, $code_departement, $id_zone);
} else {
// Sinon méthode habituelle, un test simple suivi d'un test plus complexe
// On cherche donc d'abord en France
// @TODO c'est quoi ce if() mal ordonné ?
if($pays == "") {
$retour = $this->chercherCentroideCommuneBdd($nom, $code_departement, $id_zone);
}
// Si on ne trouve rien on teste dans le monde entier
if($retour == false) {
$retour = $this->effectuerRequeteGeocodingMondiale($nom, '2,3,4,5,6,7,8', 1, $pays, $id_zone);
}
}
return $retour;
}
 
public function obtenirInfosPourCodeInseeCommune($code_insee) {
$code_insee = trim($code_insee);
 
$retour = $this->chercherCentroideCommuneInseeBdd($code_insee);
 
return $retour;
}
 
/**
* Renvoie tous les codes de zones pour le groupe $groupe
*/
protected function obtenirZonesDuGroupe($groupe) {
$requete = 'SELECT valeur as id_zone FROM cel_groupes_zones '
. 'WHERE id_groupe = ' . Cel::db()->proteger($groupe)
. ' -- '.__FILE__.':'.__LINE__;
 
$liste_zones = Cel::db()->requeter($requete);
return $liste_zones;
}
 
/**
* Renvoie des infos pour un groupes de zones, déclaré dans la table
* cel_groupe_zones : toutes les infos des zones concernées, plus le carré
* englobant (bounding box) et le centroïde de l'union des zones
*/
public function obtenirInfosPourGroupeZonesFrance($groupe) {
$groupe = trim($groupe);
$retour = false;
 
$zones = $this->obtenirZonesDuGroupe($groupe);
$retour = array(
'groupe' => array(),
'zones' => array()
);
foreach ($zones as $zone) {
$idZone = $zone['id_zone'];
$retour['zones'][] = $this->obtenirInfosPourNom(null, null, null, $idZone);
}
 
// recherche bbox (de Bouygues Telecom) @TODO faire ça au dessus, en une passe ?
$lngmin = 99999;
$latmin = 99999;
$lngmax = -99999;
$latmax = -99999;
foreach ($retour['zones'] as $zone) {
$lat = $zone['lat'];
$lng = $zone['lng'];
if ($lat < $latmin) { $latmin = $lat; }
if ($lat > $latmax) { $latmax = $lat; }
if ($lng < $lngmin) { $lngmin = $lng; }
if ($lng > $lngmax) { $lngmax = $lng; }
}
$retour['groupe']['bbox'] = array(
'lat_min' => $latmin,
'lat_max' => $latmax,
'lng_min' => $lngmin,
'lng_max' => $lngmax
);
// @TODO suggérer un niveau de zoom en fonction de la plus grande dimension
// de la bounding-box (ne marchera pas dans tous les cas mais ce serait
// déjà pas mal)
$retour['groupe']['centroide'] = array(
'lat' => ($latmin + $latmax) / 2,
'lng' => ($lngmin + $lngmax) / 2
);
 
return $retour;
}
public function chercherInfosCommune($projet, $latitude, $longitude) {
$valeurs_params = '?lat='.$latitude.'&lon='.$longitude;
$url_service = $this->config['cel']['url_service_geo_local'].$valeurs_params;
$url_service = str_replace(',', '.', $url_service);
$ch = curl_init($url_service);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$reponse = curl_exec($ch);
$infos_coord = $this->traiterReponseServiceCommune($reponse);
curl_close($ch);
return $infos_coord;
}
public function chercherInfosPays($latitude, $longitude) {
$valeurs_params = '?lat='.$latitude.'&lon='.$longitude;
$url_service = $this->config['cel']['url_service_geo_mondial'].$valeurs_params;
$url_service = str_replace(',', '.', $url_service);
$ch = curl_init($url_service);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$reponse = curl_exec($ch);
 
$res = json_decode($reponse, true);
$retour = null;
$retour = array(
'code_zone' => '',
'code_pays' => '',
'nom' => '',
'type' => ''
);
if(!empty($res)) {
// Cas où l'on a recherché des coordonnées en France
// On en profite pour remplir plus de données
// que le simple pays si elles sont présentes
if(isset($res[8]) && isset($res[8]['codeInsee'])) {
$retour = array(
'code_zone' => $res[8]['codeInsee'],
'code_pays' => 'FR',
'nom' => $res[8]['nom'],
'type' => 'france'
);
} else {
// Cas de la recherche en dehors de France
// La zone de plus haut niveau est toujours un pays
// (car le niveau de zone est limité à 2)
$infos_pays = $res[min(array_keys($res))];
// La zone de niveau le plus bas est la "localité"
// la plus précise qu'on a pu trouver
$infos_localite = $res[max(array_keys($res))];
// Cas où l'on a trouvé un code pays
if(!empty($infos_pays['codeIso31661'])) {
$retour = array(
'code_pays' => $infos_pays['codeIso31661'],
'nom' => ''
);
} elseif(!empty($infos_pays['codeIso31662'])) {
// Quelquefois la zone de plus haut niveau est une région ou une province
// heureusement son code est de forme XX-YY ou XX est le code iso du pays !
$retour = array(
'code_pays' => substr($infos_pays['codeIso31662'], 0, 2),
'nom' => ''
);
}
// Pas de sens de prendre un nom de localité si on a pas de pays
if(!empty($retour['code_pays'])) {
// Type sert à savoir quelle fonction de mise à jour on appellera
$retour['type'] = 'monde';
$retour['nom'] = $infos_localite['intitule'];
$retour['code_zone'] = $infos_localite['codeZoneGeo'];
}
}
}
return $retour;
}
public function traiterReponseServiceCommune($reponse) {
$retour = null;
$reponse = json_decode($reponse);
// cas du service lion 1906 qui peut renvoyer plusieurs communes (on prend la première)
if (is_array($reponse)) {
$reponse = $reponse[0];
}
if (isset($reponse->codeINSEE)) {
// Type sert à savoir quelle fonction de mise à jour on appellera
$retour = array(
'code_zone' => $reponse->codeINSEE,
'code_pays' => 'FR',
'nom' => $reponse->nom,
'type' => 'france'
);
}
return $retour;
}
 
// @TODO gérer $id_zone (inactif pour l'instant)
protected function effectuerRequeteGeocodingMondiale($nom, $niveau = '2,3,4,5,6,7,8', $limite = 1, $pays = null, $id_zone=null) {
$url_sans_pays = $this->config['cel']['url_service_geo_mondial'].'?masque='.urlencode($nom).'&niveau='.$niveau.'&limite='.$limite;
$url = $url_sans_pays.(!empty($pays) ? '&pays='.urlencode($pays) : '');
 
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->maxTimeoutRequeteMondiale);
$reponse = curl_exec($ch);
$res = @json_decode($reponse, true);
 
$retour = array();
if(!empty($res)) {
// cas de la localisation directe d'une commune
if($limite == 1) {
$retour = $this->traiterLigneResultatRequeteGeocodingMondiale($res);
} else {
// cas de l'autocompletion
$retour = array();
foreach($res as $ligne) {
$ligne_decodee = $this->traiterLigneResultatRequeteGeocodingMondiale($ligne);
$param = array('nom' => $ligne_decodee['nom'], 'code' => $ligne_decodee['code_pays']);
$retour[] = $this->formaterLigneResultat($param);
}
}
} else if($limite == 1) {
// Dans le cas où un pays a été demandé mais pas de résultat, on rentente la recherche sans
// préciser de pays
$res = @json_decode(file_get_contents($url_sans_pays), true);
if(!empty($res)) {
$retour = $this->traiterLigneResultatRequeteGeocodingMondiale($res);
}
}
return $retour;
}
 
/**
* Renvoie les infos d'une commune, incluant les coordonnées du centroïde,
* en fonction du nom et/ou du département, et/ou de l'id_zone (plus fiable)
*/
protected function chercherCentroideCommuneBdd($commune, $departement=null, $id_zone=null) {
$commune_formatee = str_replace(array(' ', '-'), '_', trim($commune));
// clauses
$clauses = array();
if ($commune != null) {
$clauses[] = 'nom LIKE '.Cel::db()->proteger($commune_formatee);
}
if ($departement != null) {
$departement = trim($departement, "*");
if (strlen($departement) > 2) {
// @TODO piège : si deux communes ont le même nom dans deux
// DROM-COM, la troncature à 2 chiffres sera insuffisante
$departement = substr($departement, 0, 2);
}
$clauses[] = 'code LIKE '.Cel::db()->proteger($departement.'%');
}
if ($id_zone != null) {
$clauses[] = 'id_zone_geo = '.Cel::db()->proteger($id_zone);
}
$clauses = implode(' AND ', $clauses);
 
$requete = 'SELECT utm_x, utm_y, utm_secteur, code FROM cel_zones_geo '
. 'WHERE ' . $clauses
. ' -- '.__FILE__.':'.__LINE__;
 
$commune_coordonnees = Cel::db()->requeter($requete);
$retour = false;
if ($commune_coordonnees && is_array($commune_coordonnees) && count($commune_coordonnees) > 0) {
$lat_lon = $this->convertirUtmVersLatLong($commune_coordonnees[0]['utm_x'],$commune_coordonnees[0]['utm_y'],$commune_coordonnees[0]['utm_secteur']);
$retour = array(
'lat' => (float) $lat_lon['lat'],
'lng' => (float) $lat_lon['lng'],
'nom' => $commune,
'code_zone' => $commune_coordonnees[0]['code'],
'code_pays' => 'FR',
'type' => 'france'
);
}
return $retour;
}
 
/**
* Renvoie les infos d'une commune, incluant les coordonnées du centroïde,
* en fonction du code insee (plus fiable)
*/
protected function chercherCentroideCommuneInseeBdd($code_insee) {
$code_insee = trim($code_insee);
$commune_coordonnees = array();
if ($code_insee != null) {
$requete = 'SELECT `wgs84_latitude`, `wgs84_longitude`, `nom` FROM `cel_zones_geo` '
. 'WHERE `code`=' .Cel::db()->proteger($code_insee)
. ' -- '.__FILE__.':'.__LINE__;
 
$commune_coordonnees = Cel::db()->requeter($requete);
}
 
$retour = false;
if ($commune_coordonnees && is_array($commune_coordonnees) && count($commune_coordonnees) > 0) {
$retour = array(
'lat' => (float) $commune_coordonnees[0]['wgs84_latitude'],
'lng' => (float) $commune_coordonnees[0]['wgs84_longitude'],
'nom' => $commune_coordonnees[0]['nom'],
'code_zone' => $code_insee,
'code_pays' => 'FR',
'type' => 'france'
);
}
 
return $retour;
}
public function obtenirListeInfosPourNom($lieu) {
$lieux_retour = $this->obtenirListeCommunes($lieu);
// S'il y a moins de 50 résultats on tente des les fusionner avec une recherche mondiale
// avec 50 - n éléments (où n est la taille du tableau de communes)
if(count($lieux_retour) < 50) {
$lieux_mondiaux = $this->effectuerRequeteGeocodingMondiale($lieu, '2,3,4,5,6,7,8', 50 - count($lieux_retour));
usort($lieux_mondiaux, array($this, 'trierListeLieux'));
$lieux_retour += $lieux_mondiaux;
}
 
// Suppression de possibles doublons @WARNING marche pas !!!
//$lieux_retour = $this->reduireListeLieux($lieux_retour);
return $lieux_retour;
}
public function obtenirListeCommunes($lieu) {
$lieu_formate = $this->formaterChaineLieuPourRequete($lieu);
$retour = array();
if ($this->estUneChaineRequeteValide($lieu_formate)) {
$requete = 'SELECT DISTINCT nom, code '.
'FROM cel_zones_geo '.
'WHERE nom LIKE '.Cel::db()->proteger($lieu_formate.'%').' '.
'ORDER BY nom '.
'LIMIT 50 '.
' -- '.__FILE__.':'.__LINE__;
 
$liste_lieux = Cel::db()->requeter($requete);
if ($liste_lieux) {
foreach ($liste_lieux as $lieu_trouve) {
$retour[] = $this->formaterLigneResultat($lieu_trouve);
}
}
}
return $retour;
}
/**
* Fonctions utilitaires
*/
 
// qui a écrit cette m*rde ?
protected function reduireListeLieux($tableau) {
$index = array();
foreach($tableau as $ligne) {
// merci de mettre des commentaires !!!
// ça enlève les 2 premières lettres ou chiffres du nom de lieu => wtf ?
$index_loc = trim(preg_replace("/\([a-zA-Z0-9]{2}\)$/", "", $ligne[0]));
if(!isset($index[$index_loc])) {
$index[$index_loc] = $ligne;
} else {
if(mb_strlen($ligne[0]) > mb_strlen($index[$index_loc][0])) {
$index[$index_loc] = $ligne;
} else if(mb_strlen($ligne[0]) == mb_strlen($index[$index_loc][0])) {
if(preg_match("/(.)* ([a-zA-Z0-9]{2})/", $ligne[0])) {
$index[$index_loc] = $ligne;
}
}
}
}
return array_values($index);
}
protected function trierListeLieux($a, $b) {
return strcmp($a[0], $b[0]);
}
protected function testerCoordonneesWgsFrance($latitude, $longitude) {
$coord_france = false;
if ($latitude != '' && $longitude != '') {
if ($latitude < 51.071667 && $latitude > 41.316667) {
if ($longitude < 9.513333 && $longitude > -5.140278) {
$coord_france = true;
}
}
}
return $coord_france;
}
protected function traiterLigneResultatRequeteGeocodingMondiale($res) {
$lat = "";
$lon = "";
$nom = "";
$code ="";
$code_pays="";
$type="";
if(!empty($res)) {
$code_pays = $this->extraireCodePays($res);
$nom = $res['intitule'];
$lat = $res['centre_lat'];
$lon = $res['centre_lng'];
$type = $code_pays == 'FR' || $code_pays == 'FX' ? 'france' : 'monde';
$retour = array(
'lat' => (float)$lat,
'lng' => (float)$lon,
'nom' => $nom,
'code_zone' => $code,
'code_pays' => $code_pays,
'type' => $type
);
}
return $retour;
}
protected function convertirUtmVersLatLong($x, $y, $sector) {
$convertisseur = new gPoint();
$convertisseur->setUTM($x, $y, $sector);
$convertisseur->convertTMtoLL();
$lat_long = array();
$lat_long['lat'] = str_replace(',','.',$convertisseur->Lat());
$lat_long['lng'] = str_replace(',','.',$convertisseur->Long());
return $lat_long;
}
protected function extraireCodePays($infos_pays) {
$code = "";
if(!empty($infos_pays['codeIso31661'])) {
$code = $infos_pays['codeIso31661'];
} elseif(!empty($infos_pays['codeIso31662'])) {
// Si pas de code pays, le sous code peut exister et content le code pays
// sous la forme codepays-souscode
$code = substr($infos_pays['codeIso31662'], 0, 2);
}
return $code;
}
protected function estUneChaineRequeteValide($lieu) {
return (strlen($lieu) > 0) && ($lieu != '%');
}
protected function formaterLigneResultat($ligne) {
if(empty($ligne['code']) || $ligne['code'] == null) {
$res = array($ligne['nom'], '');
} else {
$res = array($ligne['nom'].' ('.substr(sprintf('%02s', $ligne['code']),0,2).')', $ligne['code']);
}
return $res;
}
/**
* Remplace les * par % pour faire des recherches floues
* Remplace les + par _ (nginx envoie des "+" dans l'URL à la place des espaces)
* Remplace les espaces et les - par _ car les noms de communes peuvent avoir des espaces ou des tirets
* @param string $lieu
* @return string le lieu formaté pour la recherche
*/
protected function formaterChaineLieuPourRequete($lieu) {
$lieu = ltrim($lieu);
$lieu = preg_replace('/\*+/', '%', $lieu);
$lieu = str_replace(['+', ' ', '-'], '_', $lieu);
return $lieu;
}
 
public function obtenirAltitude($donnees) {
$altitude = [];
$api = '';
$parametres_coordonnees = '';
 
 
if(empty($donnees) || empty($donnees['code_pays'])) {
return $altitude;
}
 
$pays = $donnees['code_pays'];
$lat = $donnees['lat'] ?? $donnees['latitude'] ?? null;
$lon = $donnees['lon'] ?? $donnees['lng'] ?? $donnees['longitude'] ?? null;
 
if (!($lat && $lon)) {
return $altitude;
}
 
if ('FR' === $donnees['code_pays']) {
$api = 'ign';
$parametres_coordonnees = http_build_query(['lat' => $lat, 'lon' => $lon]);
} else {
$api = 'mapquest';
$parametres_coordonnees = http_build_query(['latLngCollection' => $lat.','.$lon]);
}
 
$url_service = sprintf($this->config[$api]['url_service_altitude'], $this->config[$api]['cle_api'], $parametres_coordonnees);
 
$ch = curl_init($url_service);
 
if ('ign' === $api) {// pas de header Authorization pour mapquest
curl_setopt($ch, CURLOPT_USERPWD, $this->config[$api]['username'] . ":" . $this->config[$api]['password']);
}
 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$reponse = curl_exec($ch);
curl_close($ch);
 
$altitude = $this->traiterReponseServiceAltitude($api, $reponse);
 
return ['altitude' => $altitude];
}
 
public function traiterReponseServiceAltitude($api, $reponse) {
$reponse = json_decode($reponse);
$elevationArrayKey = '';
$elevationKey = '';
$altitude = null;
 
// on a de la chance les réponses de ces 2 api sont structurées de la même façon
if ('ign' === $api) {
$elevationArrayKey = 'elevations';
$elevationKey = 'z';
} elseif ('mapquest' === $api) {
$elevationArrayKey = 'elevationProfile';
$elevationKey = 'height';
}
// du coup on peut faire ça
if (
isset($reponse->{$elevationArrayKey})
&& is_array($reponse->{$elevationArrayKey})
&& isset($reponse->{$elevationArrayKey}[0]->{$elevationKey})
// lorsque les coordonnées sortent de leur rayon d'action, ces APIs
// renvoient bien quelque-chose: -32768 pour mapquest, et -99999 pour l'IGN
&& POINT_EMERGE_LE_PLUS_BAS < floatval($reponse->{$elevationArrayKey}[0]->{$elevationKey})
) {
$altitude = $reponse->{$elevationArrayKey}[0]->{$elevationKey};
}
 
return $altitude;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionChampsEtendus2.php
New file
0,0 → 1,320
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion de l'ajout, modification et suppression des champs supplémentaires des obs et images dans la nouvelle bd.
*
* @internal Mininum PHP version : 5.2
* @category CEL
* @package Services
* @subpackage Bibliothèques
* @version 0.1
* @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 GestionChampsEtendus2 extends Cel {
 
private $table_champs_etendus = "extended_field";
private $table_champs_etendus_trad = "extendedfield_translation";
private $champ_id = "field_id";
private $liste_champs = "ce.`id`, ce.`project`,ce.`project_id`, ce.field_id, ce.`data_type`, ce.`is_mandatory`, ce.`unit`, ".
"cet.extended_field_id, cet.`label`, cet.`description`, cet.`default_value`, cet.`error_message`, cet.`language_iso_code`, cet.`help` ";
 
public function __construct($config) {
parent::__construct($config);
}
 
/**
* 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 = $ce.field_id ".
' -- '.__FILE__.':'.__LINE__;;
 
$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, $langue = "fr") {
$requete = "SELECT {$this->liste_champs} ".
"FROM {$this->table_champs_etendus} ce ".
"INNER JOIN {$this->table_champs_etendus_trad} cet ON ce.field_id = cet.extended_field_id ".
"WHERE cet.language_iso_code = '".$langue."' AND ";
($id_element_lie != "") ? $requete .= " {$this->champ_id} = ".Cel::db()->proteger($id_element_lie) : "";
$requete .= ' -- '.__FILE__.':'.__LINE__;
$resultat = Cel::db()->requeter($requete);
return $resultat;
}
/**
* A créer voir ajouterParProjet
*
*
*/
public function ajouter($parametres) {
return 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 ajouterParProjet(Array $champs_etendus) {
if (! $champs_etendus) return TRUE; // le tableau ... vide à été inséré
// ajout dans la table extended_field
$ajout_ce = "INSERT INTO extended_field ";
$ajout_ce .= " (`".implode("`, `", array_keys($champs_etendus['ce'][0]))."`)";
$lignes = array();
foreach ($champs_etendus['ce'] as $champ_etendu) {
$lignes[] = "('".implode("', '", $champ_etendu)."')";
}
$ajout_ce = Cel::db()->executer($ajout_ce." VALUES ".implode(", ",$lignes));
// ajout dans extendedfield_trad
$ajout_cet = "INSERT INTO extendedfield_translation ";
$ajout_cet .= " (`".implode("`, `", array_keys($champs_etendus['cet'][0]))."`)";
$lignes = array();
foreach ($champs_etendus['cet'] as $champ_etendu) {
$lignes[] = "('".implode("', '", $champ_etendu)."')";
}
$ajout_cet = Cel::db()->executer($ajout_cet." VALUES ".implode(", ",$lignes));
return ($ajout_cet !== 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 ".
' -- '.__FILE__.':'.__LINE__;
$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 ".
' -- '.__FILE__.':'.__LINE__;
$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 ".
' -- '.__FILE__.':'.__LINE__;
$suppr = Cel::db()->executer($requete);
$requete = "DELETE FROM {$this->table_champs_etendus_trad} ".
"WHERE {$this->champ_id} = $id ".
' -- '.__FILE__.':'.__LINE__;
$suppr = Cel::db()->executer($requete);
return ($suppr !== false);
}
 
 
 
/**
* Renvoie les champs étendus d'un projet
*/
public function consulterProjetChampsEtendus($projet = "", $langue = 'fr') {
if ($projet !== "") {
$requeteProjetType = "SELECT type FROM `project_settings` WHERE `project` = '".$projet."';";
$resultat = Cel::db()->executerRequete($requeteProjetType);
if ($resultat == array() || $resultat[0]['type'] == "") {
$requeteProjet = " AND ce.project = '".$projet."' AND cet.project = '".$projet."' ";
} else {
$type = $resultat[0]['type'];
$requeteProjet = " AND ce.project = '".$type."' AND cet.project = '".$type."' ";
}
} else {
$requeteProjet = "";
}
$groupes = array();
$requete = "SELECT {$this->liste_champs} ".
"FROM {$this->table_champs_etendus} ce ".
"INNER JOIN {$this->table_champs_etendus_trad} cet ON ce.field_id = cet.extended_field_id AND ce.project = cet.project ".
"WHERE cet.language_iso_code = '".$langue."' ".$requeteProjet;
$requete .= ' -- '.__FILE__.':'.__LINE__; //echo $requete;exit;
$groupes_champs = Cel::db()->executerRequete($requete);
return $groupes_champs;
}
 
/**
* Formate de la même façon que l'ancien service les données de consulterProjetChampsEtendus
*/
public function consulterGroupesChampsEtendusPredefinis($groupe = "", $langue = 'fr') {
$groupes = array();
$groupes_champs = $this->consulterProjetChampsEtendus($groupe, $langue);
$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['project']) {
if($infos_groupe != null) {
$groupes[] = $infos_groupe;
}
$infos_groupe = array();
$cle_groupe = $groupe_champ['project'];
}
if (empty($infos_groupe)) {
$infos_groupe = array(
'cle' => $groupe_champ['project_id'],
'nom' => $groupe_champ['project'],
'champs' => array()
);
}
$infos_groupe['champs'][$groupe_champ['field_id']] = array(
'id' => $groupe_champ['id'],
'cle' => $groupe_champ['field_id'],
'label' => $groupe_champ['label'],
'type' => $groupe_champ['type_champ'],
'valeur' => $groupe_champ['valeur_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, $groupe = "") {
$requete = 'SELECT ce.id as id, ce.field_id as cle, cet.`label` as label, "0" as invisible, "0" as prive '.
"FROM {$this->table_champs_etendus} ce ".
"INNER JOIN {$this->table_champs_etendus_trad} cet ON ce.field_id = cet.extended_field_id ";
if ($groupe !== "") $requete .= " WHERE project = ".$groupe;
$requete .= ' -- '.__FILE__.':'.__LINE__;
$catalogue = Cel::db()->executerRequete($requete);
$infos_champs = array();
if ($catalogue != false) {
foreach ($catalogue as $champ) {
$champ_fmt = array(
'id' => $champ['id'],
'cle' => $champ['cle'],
'label' => $champ['label'],
'options' => array(
'invisible' => "0",
'prive' => "0")
);
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;
}
}
/branches/v3.01-serpe/jrest/bibliotheque/GestionObservation.php
New file
0,0 → 1,585
<?php
// declare(encoding='UTF-8');
/**
* 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
*
* @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 GestionObservation extends Cel {
 
static $cel_obs = ["input_source='widget'", "published_location si tag sensible", "occurrence_type = 'observation de terrain'",
"is_visible_in_cel =1", "is_visible_in_veglab =0", "is_wild = 1", "sample_herbarium = 0"];
static $correspondance_obs_occurrence = ['id_observation' => 'id', 'ce_utilisateur', 'prenom_utilisateur', 'nom_utilisateur',
'courriel_utilisateur', 'nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nt', 'famille',
'nom_referentiel', 'pays', '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);
 
 
$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);
$this->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);
}
return $retour;
}
 
 
/**
* 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);
}
$this->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__;
$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
*/
public function modifierMultiplesObservation($utilisateur, Array $ordre, $parametres) {
// nous retirons 'ce_utilisateur' (== $utilisateur) qui sera dans la clause WHERE
$exclusions = ['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
$this->formaterParametresObservation($parametres);
if (isset($parametres['nom_sel'])) {
// uniquement en cas de nom_sel présent
$this->traiterEspece($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
* Utiliser uniquement pour identiplante
*
* @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);
$this->formaterParametresObservation($parametres);
 
$champ_maj = self::traiterParametresObservationEtConstruireSousRequeteMaj($parametres);
$champ_maj[] = 'date_updated = NOW()';
 
$requete = "UPDATE occurrence SET " .
implode(', ', $champ_maj) .
" WHERE id = ".Cel::db()->proteger($id_obs).
" AND is_public = 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;
}
 
/**
* Si vous utiliser cette méthode, penser à mettre à jour les champs correspondant de la table cel_images.
* Pour ce faire, utiliser GetionImage->modifierTransmissionParObs().
* @param mixed $ids_obs_ou_tableau id d'observation ou tableau d'ids d'observation.
* @param bool $publier true pour publier, false pour dépublier.
* @return type
*/
public function modifierTransmissionObservation($idsObs, $publier) {
$ids_obs_proteges = Cel::db()->proteger($idsObs);
$idsObsConcat = is_array($ids_obs_proteges) ? implode(', ', $ids_obs_proteges) : $ids_obs_proteges;
$etat_transmission = $publier ? 1 : 0;
$dateTransmission = $publier ? 'NOW()' : 'NULL';
 
$requete = 'UPDATE occurrence '.
"SET is_public = $etat_transmission , date_published = $dateTransmission, date_updated = NOW() ".
"WHERE id IN ($idsObsConcat) ".
' -- ' . __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, $id) {
// 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 AS id_obs '.
'FROM occurrence '.
"WHERE user_id = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
$resultats = Cel::db()->requeter($requete);
 
$ids_obs = array();
$ids_obs_non_protegees = array();
if (is_array($resultats) && count($resultats) > 0) {
foreach ($resultats 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 occurrence WHERE '.
"ce_utilisateur = $idUtilisateurP ".
"AND id_observation IN ($chaine_ids_obs) ".
' -- '.__FILE__.':'.__LINE__;
$resultat = 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 === false) {
$msg = "Erreur de suppression d'une liste d'observations : $resultat";
$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;
}
$nom_ref_nettoye = trim($parametres['nom_referentiel']);
$morceaux = preg_split('/.v?[0-9]/', $nom_ref_nettoye);
$code_referentiel = $morceaux[0];
 
// TODO/XXX: quoi ?!
// si on n'a un nom_ret_nn mais pas de nom_sel_nn ni de nom_sel, on efface tout => WTF ?
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 partie 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'], $code_referentiel);
// 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_sel'] = $complement->nom_sci . ' ' . $complement->auteur;
$parametres['nom_ret_nn'] = $complement->{"nom_retenu.id"};
$parametres['nt'] = $complement->num_taxonomique;
$parametres['famille'] = $complement->famille;
}
}
} else { // référentiel "autre": on vide !
$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
*/
public function formaterParametresObservation(&$parametres) {
 
if (isset($parametres['ce_zone_geo'])) {
if (!$this->estPossiblementUnDepartement($parametres['ce_zone_geo'])) {
$parametres['ce_zone_geo'] = "";
} else {
if($this->estLaCorse($parametres['ce_zone_geo'])) {
// "Décorsifiage"
$parametres['ce_zone_geo'] = str_ireplace(array("2A", "2B"), "2", $parametres['ce_zone_geo']);
}
// Pour empecher que des numéros de département de 1 à 9 soient saisis sans 0
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'] = $parametres['ce_zone_geo'];
}
$parametres['pays'] = 'FR';
}
}
 
if(empty($parametres['pays']) && !empty($parametres['latitude']) && !empty($parametres['longitude'])) {
$recherche_pays = new RechercheInfosZoneGeo($this->config);
// Dans le cas de l'insertion de l'obs, il serait déraisonnable d'attendre plus de 2 secondes
// pour détecter le pays qui sera de toute façon retrouvé par le script lancé en cron
$recherche_pays->maxTimeoutRequeteMondiale = 2;
$infos_pays = $recherche_pays->obtenirInfosPourCoordonnees($parametres);
if($infos_pays['type'] == 'france') {
$parametres['pays'] = 'FR';
// Ajout d'infos supplémentaires si jamais on a pas de ce zone geo ou de zone geo
if(empty($parametres['ce_zone_geo'])) {
$parametres['ce_zone_geo'] = $infos_pays['code_insee'];
}
if(empty($parametres['zone_geo'])) {
$parametres['zone_geo'] = $infos_pays['nom'];
}
} else {
$parametres['pays'] = $infos_pays['code_pays'];
}
}
 
// 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';
}
}
}
private function estPossiblementUnDepartement($code) {
return $code != 'null' && trim($code) != "" &&
(is_numeric($code) || $this->estLaCorse($code));
}
private function estLaCorse($code) {
return strtoupper($code) == "2A" || strtoupper($code) == "2B";
}
 
/**
* 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) {
$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(), NULL)';
 
$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) {
$correspondance = array("nom_sel" => "user_sci_name",
"nom_sel_nn" => "user_sci_name_id",
"nom_ret" => "accepted_sci_name",
"nom_ret_nn" => "accepted_sci_name_id",
"famille" => "family",
"certitude" => "certainty",
"valide" => "is_identiplante_validated",
"transmission" => "is_public");
// Nullifiage ...
// TODO: code dupliqué, en faire une fonction à mettre à la place appropriée
$champs = array();
foreach ($parametres as $cle => $valeur) {
$valeur = self::renvoyerValeurDenullifiee($valeur);
if (isset($correspondance[$cle])) {
$champs[] = $correspondance[$cle].' = '.$valeur;
}
}
if (!isset($parametres['transmission'])) {
$champs[] = "is_identiplante_validated = 1";
$champs[] = "certainty = 'certain'";
}
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;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+81186f62458e2fe752e3a8cf376a5dfbed2e0704
\ No newline at end of property