Subversion Repositories eFlore/Applications.del

Compare Revisions

No changes between revisions

Ignore whitespace Rev 1535 → Rev 1536

/branches/v1.1-helium/services/modules/0.1/nomstaxons/ListeTaxons.php
New file
0,0 → 1,88
<?php
// declare(encoding='UTF-8');
/**
* Le web service récupère une liste de noms suivant un référentiel et un masque donné
*
* @category php 5.2
* @package del
* @subpackage images
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @author Grégoire Duché <gregoire@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ListeTaxons {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $requete = null;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_nomstaxons.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Lancement du service
$taxons = $this->chargerNoms($this->masque->getMasque('referentiel'), $this->masque->getMasque('nom'));
$total = $this->compterNoms($taxons);
$this->navigation->setTotal($total);
$this->conteneur->setSansLimite();
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $taxons);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis le serveur de tous les noms
* */
private function chargerNoms($referentiel, $requete) {
$url = sprintf($this->conteneur->getParametre('url_service_completion_base'), $referentiel, urlencode($requete));
$res = json_decode(@file_get_contents($url),true);
$res = (array)$res;
$res_fmt = array();
if(isset($res['resultat'])) {
foreach($res['resultat'] as $nn => $info_nom) {
$res_fmt[] = array($info_nom['nom_sci_complet'], (string)$nn);
}
}
return $res_fmt;
}
/**
* Compter le nombre total de noms pour affichage dans entete.
* */
private function compterNoms($taxons) {
return sizeof($taxons);
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Syndication.php
New file
0,0 → 1,175
<?php
/**
* Description :
* Classe principale de chargement des services de syndication
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package del-service
* @author Aurélien 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>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Syndication extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
private $squelette_dossier = null;
private $formats_autorises = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
$this->squelette_dossier = dirname(__FILE__).DIRECTORY_SEPARATOR.'syndication'.DIRECTORY_SEPARATOR.'squelettes'.DIRECTORY_SEPARATOR;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserProjet();
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$this->verifierRessourcesEtParametres();
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($this->creerResultatService($resultat));
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
private function verifierRessourcesEtParametres() {
$servicesDispos = Config::get('servicesDispo');
if (!isset($this->ressources[0]) || !in_array($this->ressources[0], explode(',',$servicesDispos))) {
$message = "Vous devez indiquer un nom de service valide, les services disponibles sont ".$servicesDispos;
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
$chaineFormatsAutorises = Config::get('formatsRss');
$this->formats_autorises = explode(',', $chaineFormatsAutorises);
if (!isset($this->ressources[1]) || !in_array($this->ressources[1], $this->formats_autorises)) {
$message = "Vous devez indiquer un format de flux valide, les formats acceptés sont ".$chaineFormatsAutorises;
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
} else {
$this->format = $this->ressources[1];
}
}
 
private function traiterRessources() {
$retour = '';
$retour = $this->initialiserService();
 
return $retour;
}
private function creerResultatService($donnees) {
$resultat = new ResultatService();
$resultat->mime = $this->getTypeMime();
$resultat->corps = SquelettePhp::analyser($this->squelette_dossier.$this->format.'.tpl.xml', $donnees);
return $resultat;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->projetNom = 'syndication';
$this->chargerConfigProjet();
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
private function chargerNomService() {
if (!isset($this->ressources[0])) {
$message = "Vous devez indiquer un nom de service";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
} else {
$this->serviceNom = $this->ressources[0];
}
}
 
private function obtenirNomClasseService($mot) {
$classeNom = 'Syndication'.ucwords($mot);
return $classeNom;
}
private function getTypeMime() {
$mime = '';
switch ($this->format) {
case 'atom' :
$mime = 'application/atom+xml';
break;
case 'rss1' :
case 'rss2' :
$mime = 'application/rss+xml';
break;
case 'opml' :
$mime = 'text/x-opml';
break;
default:
$mime = 'text/html';
}
return $mime;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Determinations.php
New file
0,0 → 1,201
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Determinations extends RestService {
 
/*
* url possibles :
* http://localhost/del/services/0.1/images/ => toutes les images, appel à la classe ListeImages
* http://localhost/del/services/0.1/images/#id => une image donnée => non utilisé sur DEL pour l'instant
* http://localhost/del/services/0.1/images/#id/votes/ => tous les votes d'une image classés par protocole
* http://localhost/del/services/0.1/images/#id/votes/protocole/#id => tous les votes d'une image et d'un protocole donné
* http://localhost/del/services/0.1/images/#id/votes/#id => un vote donné pour une image donnée.
* */
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
if ($this->avoirRessourceService()) {
$retour = $this->initialiserService();
}
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->chargerNomDuProjet();
$this->chargerConfigProjet();
 
}
 
private function chargerNomDuProjet() {
$this->projetNom = 'determinations';
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function avoirRessourceService() {
$presenceRessourceService = false;
if (isset($this->ressources[0])) {
if($this->ressources[0] == 'images-determinations-probables') {
$presenceRessourceService = true;
$this->serviceNom = 'liste-images-determinations-probables';
} else if($this->ressources[0] == 'valider-determination') {
$presenceRessourceService = true;
$this->serviceNom = 'valider-determination';
} else {
$this->editerMessageErreurRessource();
}
}
return $presenceRessourceService;
}
private function editerMessageErreurRessource() {
$message = "Le service demandé '".$this->projetNom.'/'.implode('/', $this->ressources).
"' n'est pas disponible pour le projet ".$this->projetNom." !\n".
"Les services disponibles sont : images, images/#id/votes, images/#id/vote/#id_vote";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
private function etreRessourceIdentifiant($num) {
$presenceId = false;
if (is_numeric($this->ressources[$num])) {
$presenceId = true;
} else {
$message = "Le service demandé '$service' nécessite d'avoir un identifiant d'image valide";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $presenceId;
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Communes.php
New file
0,0 → 1,122
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Communes extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
$retour = $this->initialiserService();
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->projetNom = 'communes';
$this->chargerConfigProjet();
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} else {
//TODO : throw exception
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
private function chargerNomService() {
// si la méthode est POST, on ajouter un commentaire
$this->serviceNom = 'liste-communes';
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Commentaires.php
New file
0,0 → 1,178
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Commentaires extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $parametres) {
$this->methode = 'ajouter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$corps = $reponseHttp->getCorps();
return $corps;
}
public function supprimer($ressources) {
$this->methode = 'supprimer';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->ressources = $ressources;
$this->conteneur = new Conteneur();
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
$retour = $this->initialiserService();
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->projetNom = 'commentaires';
$this->chargerConfigProjet();
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'supprimer') {
$retour = $service->supprimer($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
private function chargerNomService() {
// si la méthode est POST, on ajouter un commentaire
if ($this->methode == 'ajouter') {
$this->serviceNom = 'ajouter-commentaire';
} else if ($this->methode == 'supprimer') {
$this->serviceNom = 'supprimer-commentaire';
}
else {
//S'il n'y a pas de ressources => tous les commentaires
if (!isset($this->ressources) || empty($this->ressources)) {
$this->serviceNom = 'liste-commentaires';
} else {
if (!is_numeric($this->ressources[0])) {
$message = "La première ressource doit être un identifiant";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
} else {
$this->serviceNom = 'consulter-commentaire';
}
}
}
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/observations/VoteObservation.php
New file
0,0 → 1,279
<?php
// declare(encoding='UTF-8');
/**
* Les web service permet d'ajouter ou de modifier les votes associés aux propositions d'une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class VoteObservation extends RestService {
protected $conteneur;
protected $gestionBdd;
protected $bdd;
protected $navigation;
 
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
$this->navigation = $conteneur->getNavigation();
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
}
 
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
private function configurer() {
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
}
 
/**
* Méthode seulement présente pour interdire proprement l'accès à la consultation
* */
public function consulter($ressources, $parametres) {
 
$this->configurer();
 
//TODO: faut il vérifier quelque chose, si on n'a pas renseigné les valeurs correctement
// dans ressource, le service n'est même pas appelé
$votes = $this->chargerVotes($ressources[1]);
$this->conteneur->getNavigation()->setTotal(count($votes));
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $votes);
return $resultat;
}
 
private function chargerVotes($id_commentaire) {
$requeteVotes = 'SELECT * FROM '.
$this->gestionBdd->formaterTable('del_commentaire_vote').
'WHERE ce_proposition = '.$this->proteger($id_commentaire);
$resultatsVotes = $this->bdd->recupererTous($requeteVotes);
$votes = array();
foreach ($resultatsVotes as $vote) {
$votes[$vote['id_vote']] = $this->formaterVotes($vote);
$utilisateur = $this->chercherUtilisateur($vote['ce_utilisateur']);
if($utilisateur) {
$votes[$vote['id_vote']] = array_merge($votes[$vote['id_vote']], $utilisateur);
}
}
return $votes;
}
 
/**
* Trouver l'utilisateur en fonction des paramètres ou de la base de données si le nom, prénom et courriel ne sont pas donnés
* @return le tableau utilisateur
* */
private function chercherUtilisateur($id) {
$utilisateur = array();
$requete = "SELECT id_utilisateur as 'auteur.id', nom as 'auteur.nom', prenom as 'auteur.prenom', courriel as 'auteur.courriel' ".
'FROM '.$this->gestionBdd->formaterTable('del_utilisateur').
'WHERE id_utilisateur = '.$this->bdd->proteger($id);
$utilisateur = $this->bdd->recuperer($requete);
 
return $utilisateur;
}
 
/**
* Formater un vote en fonction du fichier de configuration config_votes.ini
* @param $votes array()
* */
private function formaterVotes($vote) {
$retour = array();
foreach ($vote as $param => $valeur) {
$retour[$this->mappingVotes[$param]] = $valeur;
}
return $retour;
}
 
/**
* Ajoute un vote à une proposition grâce aux informations passées en paramètres
* @param array $ressources tableau des informations contenues dans l'url après le nom du service
* @param array $parametres contenu du post
*
* @return void
*
* */
public function ajouter($ressources, $parametres) {
 
$this->verifierParametresAjoutModif($ressources, $parametres);
 
// Vérifie si la proposition existe, et la crée sinon
// (cas du vote sur la proposition fabriquée à partir de l'observation originale)
if($ressources[1] == 0) {
$ressources[1] = $this->renvoyerIdOuCreerPropositionDeterminationInitiale($ressources[0]);
}
 
$insertion = 'INSERT INTO del_commentaire_vote '.
'(ce_proposition , ce_utilisateur , valeur , date) '.
'VALUES ('.$this->proteger($ressources[1]).','.$this->proteger($parametres['utilisateur']).','.
$this->proteger($parametres['valeur']).', NOW());';
$resultatsVotes = $this->bdd->requeter($insertion);
if ($resultatsVotes == false) {
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
} else {
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_CREATION_OK);
}
}
 
private function renvoyerIdPropositionObservation($id_observation) {
 
$id_commentaire = null;
// Récupération de l'utilisateur + nom sel de l'observation originale
$requete_selection_obs = 'SELECT ce_utilisateur, nom_sel '.
'FROM del_observation '.
'WHERE id_observation ='.$this->proteger($id_observation);
 
$obs = $this->bdd->recuperer($requete_selection_obs);
 
if(is_array($obs) && isset($obs['ce_utilisateur'])) {
// On considère que la proposition faite par le même utilisateur et ayant le même
// nom sélectionné est bien la proposition composée à partir de l'obs originale
$requete_selection_comm = 'SELECT id_commentaire FROM del_commentaire '.
'WHERE ce_observation = '.$this->proteger($id_observation).' '.
' AND nom_sel = '.$this->proteger($obs['nom_sel']).' AND '.
' ce_utilisateur = '.$this->proteger($obs['ce_utilisateur']);
 
$commentaire = $this->bdd->recuperer($requete_selection_comm);
$id_commentaire = (is_array($commentaire) && $commentaire['id_commentaire'] != null) ? $commentaire['id_commentaire'] : null;
}
 
return $id_commentaire;
}
 
private function renvoyerIdOuCreerPropositionDeterminationInitiale($id_observation) {
$proposition_existe = $this->propositionInitialeExiste($id_observation);
if(!$proposition_existe) {
$id_proposition = $this->creerPropositionAPartirObservation($id_observation);
} else {
$requete = "SELECT id_commentaire FROM del_commentaire WHERE ce_observation = ".$id_observation." ".
"AND proposition_initiale = 1";
$resultat = $this->bdd->recuperer($requete);
$id_proposition = $resultat['id_commentaire'];
}
return $id_proposition;
}
 
private function propositionInitialeExiste($id_observation) {
 
$requete = "SELECT COUNT(*) >= 1 as existe FROM del_commentaire WHERE ce_observation = ".$id_observation." ".
"AND proposition_initiale = 1";
$resultat = $this->bdd->recuperer($requete);
return $resultat['existe'] == 1;
}
 
private function creerPropositionAPartirObservation($id_observation) {
 
$insertion = 'INSERT IGNORE INTO del_commentaire '.
'(ce_observation, ce_utilisateur, utilisateur_prenom, utilisateur_nom, utilisateur_courriel, '.
'nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, nt, famille, nom_referentiel, date, proposition_initiale) '.
'SELECT id_observation, ce_utilisateur, prenom, nom, '.
' courriel, nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, '.
" nt, famille, 'bdtfx_v1', NOW(), '1' ".
'FROM del_observation do '.
' LEFT JOIN del_utilisateur du ON '.
'do.ce_utilisateur = du.id_utilisateur '.
'WHERE id_observation = '.$this->proteger($id_observation);
 
$resultat = $this->bdd->requeter($insertion);
// Attention à l'abstraction utilisée, récupérer le dernier id en mysql ou msqli ça marche bien, mais pour d'autres moins
$id = $this->bdd->recupererIdDernierAjout();
 
return $id;
}
 
/**
* Modifie un vote associé à une proposition grâce aux informations passées en paramètres
* @param array $ressources tableau des informations contenues dans l'url après le nom du service
* @param array $parametres contenu du post
*
* @return void
*
* */
public function modifier($ressources, $parametres) {
 
$this->verifierParametresAjoutModif($ressources, $parametres);
 
// Si l'identifiant de proposition vaut 0, c'est un vote sur une proposition
// fabriquée à partir de l'observation originale, dont on doit obtenir l'id
// (cas où l'on vient de voter pour celle et cela a créé la proposition, puis
// on revote pour celle ci en changeant d'avis sans recharger la page)
if($ressources[1] == 0) {
$id_proposition = $this->renvoyerIdOuCreerPropositionDeterminationInitiale($ressources[0]);
if($id_proposition == null) {
$this->envoyerMessageErreurIdProposition();
exit;
} else {
$ressources[1] = $id_proposition;
}
}
 
$modification = 'UPDATE del_commentaire_vote '.
'SET valeur ='.$this->proteger($parametres['valeur']).', '.
'date = NOW() '.
'WHERE '.
'ce_proposition = '.$this->proteger($ressources[1]).' AND '.
'ce_utilisateur = '.$this->proteger($parametres['utilisateur']);
 
$resultatsVotes = $this->bdd->requeter($modification);
if ($resultatsVotes == false) {
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
} else {
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_OK);
}
}
 
public function verifierParametresAjoutModif($ressources, $parametres) {
$erreurs = array();
if (!is_numeric($ressources[1])) {
$erreurs[] = '- le paramètre indiquant l\'identifiant de la proposition doit être numérique ;';
}
 
if (!isset($parametres['utilisateur'])) {
$erreurs[] = '- paramètre "utilisateur" manquant ;';
}
 
if (!isset($parametres['valeur'])) {
$erreurs[] = '- paramètre "valeur" manquant ;';
} else {
if (!is_numeric($parametres['valeur'])) {
$erreurs[] = '- le paramètre "valeur" doit être numérique ;';
} elseif($parametres['valeur'] != 0 && $parametres['valeur'] != 1) {
$erreurs[] = '- le paramètre "valeur" ne peut prendre que la valeur 0 ou 1 ;';
}
}
 
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
RestServeur::ajouterMessage($e);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
protected function envoyerMessageErreurIdProposition() {
$erreurs = array('Aucun identifiant de proposition n\'est lié à cette observation');
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
RestServeur::ajouterMessage($e);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
 
protected function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/observations/Observation.php
New file
0,0 → 1,347
<?php
/**
* Le web service observations récupère toutes les information pour une observation:
* images, votes sur image et protocole, commentaires, votes sur commentaires, ...
*
* @category php 5.2
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*
* @config-depend: "url_image" (dans configurations/config_observations.ini)
* ex: http://www.tela-botanica.org/appli:cel-img:%09dXL.jpg
*/
 
// http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
 
require_once(dirname(__FILE__) . '/../DelTk.php');
 
class Observation {
 
/* Map les champs MySQL vers les champs utilisés dans le JSON pour les clients pour
chacune des différentes tables utilisées pour le chargement de résultats ci-dessous.
- chargerObservation() (v_del_image)
- chargerVotesImage() (del_image_vote, del_image_protocole)
- chargerCommentaires() (del_commentaire_vote, del_commentaire).
Si la valeur vaut 1, aucun alias ne sera défini (nom du champ d'origine), cf consulter() */
static $mappings = array(
'observations' => array( // v_del_image
"id_observation" => 1,
"date_observation" => 1,
"date_transmission" => 1,
"famille" => "determination.famille",
"nom_sel" => "determination.ns",
"nom_sel_nn" => "determination.nn",
"nom_referentiel" => "determination.referentiel",
"nt" => "determination.nt",
"ce_zone_geo" => "id_zone_geo",
"zone_geo" => 1,
"lieudit" => 1,
"station" => 1,
"milieu" => 1,
"ce_utilisateur" => "auteur.id",
"mots_cles_texte" => "mots_cles_texte",
"commentaire" => 1),
/* exclus car issus de la jointure annuaire:
"nom" => "auteur.nom", // XXX: jointure annuaire
"prenom" => "auteur.prenom", // XXX: jointure annuaire
"courriel" => "observateur", // XXX: jointure annuaire */
/* présents dans cel_obs mais exclus:
ordre, nom_ret, nom_ret_nn, latitude, longitude, altitude, geodatum
transmission, date_creation, date_modificationabondance, certitude, phenologie, code_insee_calcule */
 
'images' => array( // v_del_image
"id_image" => 1,
"hauteur" => 1,
// "largeur" => 1, inutile semble-t-il
"date_prise_de_vue" => "date"),
/* présents dans cel_images mais exclus:
i_commentaire, nom_original, publiable_eflore, i_mots_cles_texte, i_ordre,
i_ce_utilisateur, i_prenom_utilisateur, i_nom_utilisateur, i_courriel_utilisateur */
 
'protocoles' => array( // del_image_protocole
"ce_protocole" => "protocole.id",
"id_protocole" => "protocole.id",
"intitule" => "protocole.intitule",
"descriptif" => "protocole.descriptif",
"tag" => "protocole.tag"),
 
/* See desc del_commentaire_vote & desc del_image_vote;
Les deux schémas sont similaires, à l'exception de ce_protocole
spécifique à del_image_vote et ce_proposition => ce_image */
'votes' => array( // del_image_vote et del_commentaire_vote
"id_vote" => "vote.id",
"ce_proposition" => "proposition.id",
"ce_image" => "image.id",
"ce_utilisateur" => "auteur.id", // attention, conflit avec commentaire, cf ci-dessous
"valeur" => "vote",
"date" => 1, // attention, conflit avec commentaire, cf ci-dessous
// absents du JSON, et pourtant présents dans services/configurations/config_mapping_votes.ini
// (nécessiterait une propre jointure sur del_utilisateur)
/* "nom" => "auteur.nom",
"prenom" => "auteur.prenom",
"courriel" => "auteur.courriel" */),
 
'commentaires' => array( // del_commentaire
"id_commentaire" => 1,
"ce_observation" => "observation",
"ce_proposition" => "proposition",
"ce_commentaire_parent" => "id_parent",
 
// les deux alias suivants sont particuliers afin d'éviter un conflit d'alias
// lors des jointures avec del_commentaire_vote ci-dessus
// (cf cas particulier dans la boucle de chargerCommentaires())
"ce_utilisateur" => "__auteur_com",
"date" => "__date_com",
 
"texte" => 1,
"utilisateur_nom" => "auteur.nom",
"utilisateur_prenom" => "auteur.prenom",
"utilisateur_courriel" => "auteur.courriel",
"nom_sel" => 1,
"nom_sel_nn" => 1,
"nom_ret_nn" => 1,
"nom_referentiel" => 1,
"proposition_initiale" => 1),
);
 
private $conteneur;
private $gestionBdd;
private $bdd;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_votes.ini');
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
if (!$ressources || count($ressources) != 1 ) {
throw new Exception("Le service observation accepte un unique identifiant d'observation", RestServeur::HTTP_CODE_ERREUR);
}
 
// initialise les mappings:
// substitue les valeurs à 1 par le nom de la clef (pas d'alias de champ)
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['protocoles'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['commentaires'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
 
 
// Gestion des configuration du script
$idobs = $ressources[0];
$protocole = isset($parametres['protocole']) && is_numeric($parametres['protocole']) ?intval($parametres['protocole']) : NULL;
 
// 1) récupération de l'observation (et de ses images (v_del_image est une vue utilisant des INNER JOIN))
$liaisons = self::chargerObservation($this->bdd, $idobs);
if(!$liaisons) {
header('HTTP/1.0 404 Not Found');
// don't die (phpunit)
throw(new Exception());
}
 
// 2) réassocie les images "à plat" à leur observation (merge)
// TODO: appliquer le formattage dépendant de la configuration en fin de processus
$observations = self::reformateObservationSimpleIndex($liaisons, $this->conteneur->getParametre('url_images'));
// bien que dans notre cas il n'y ait qu'une seule observation, issue de plusieurs images
// dans $liaisons, $observation est un tableau (cf reformateObservation).
// Considérons la chose comme telle au cas où le webservice doivent demain demander une paire
// d'observations (... convergence avec ListeObservations & DelTk)
 
$observation = array_pop($observations);
 
// 3) charge les données de votes et protocoles associés aux images
if($observation['images']) {
$votes = self::chargerVotesImage($this->bdd, $observation['images'], $protocole);
// 3") merge/reformate les données retournées
self::mapVotesToImages($votes,
$observation['images']);
}
 
// 4) charge les commentaires et les votes associés
// modifie/créé $observation['commentaires']
self::chargerCommentaires($this->bdd, $observation);
 
 
// désindexe le tableau (tel qu'apparement attendu par les applis), c'est une exception
// corriger l'appli cliente pour utiliser les index puis supprimer cette ligne
$observation['images'] = array_values($observation['images']);
// autre élément de post-processing: le ce_utilisateur de l'observation non-numeric...
if(!is_numeric($observation['auteur.id'])) $observation['auteur.id'] = "0";
if(!isset($observation['auteur.nom'])) $observation['auteur.nom'] = '[inconnu]';
 
if(isset($parametres['justthrow'])) return $observation;
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $observation;
return $resultat;
}
 
static function chargerObservation($db, $idobs) {
// prenom_utilisateur, nom_utilisateur, courriel_utilisateur sont exclus du mapping
// car nous utilisons une construction SQL élaborée pour eux (cf IFNULL ci-dessous)
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL, 'dob');
 
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL, 'dob');
 
// champs de l'annuaire (del_utilisateur): id_utilisateur prenom, nom, courriel
$annuaire_fields = implode(', ', array("IFNULL(du.prenom, prenom_utilisateur) AS `auteur.prenom`",
"IFNULL(du.nom, nom_utilisateur) AS `auteur.nom`",
"IFNULL(du.courriel, courriel_utilisateur) AS observateur"));
return $db->recupererTous(sprintf(
'SELECT %s, %s, %s FROM v_del_image as dob'.
' LEFT JOIN del_utilisateur du ON CAST(du.id_utilisateur AS CHAR) = CAST(dob.ce_utilisateur AS CHAR)'.
' WHERE dob.id_observation = %d -- %s',
$obs_fields, $image_fields, $annuaire_fields, $idobs, __FILE__ . ':' . __LINE__));
}
 
// Charger les images et leurs votes associés
static function chargerVotesImage($db, $images, $protocole = NULL) {
if(!$images) return NULL;
 
$select = array('votes' =>
array('id_vote', 'ce_image', 'ce_protocole', 'ce_utilisateur', 'valeur', 'date', /* del_image_vote */),
'protocole' =>
array('id_protocole', 'intitule', 'descriptif', 'tag' /* del_image_protocole */ ));
$vote_fields = DelTk::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'v'); // "v": cf alias dans la requête
$proto_fields = DelTk::sqlFieldsToAlias(self::$mappings['protocoles'], $select['protocole'], 'p');
$where = array();
$where[] = sprintf('v.ce_image IN (%s)',
implode(',', array_values(array_map(create_function('$a', 'return $a["id_image"];'), $images))));
 
if ($protocole) {
$where[] = "v.ce_protocole = $protocole";
}
 
return $db->recupererTous(sprintf(
'SELECT %s, %s FROM del_image_vote AS v'.
' INNER JOIN del_image_protocole p ON v.ce_protocole = p.id_protocole'.
' WHERE %s -- %s',
$vote_fields, $proto_fields,
$where ? implode(' AND ', $where) : 1,
__FILE__ . ':' . __LINE__));
}
/**
* Formater une observation depuis une ligne liaison
* @param $liaison liaison issue de la recherche
* @return $observation l'observation mise en forme
* Exemple: vote, au sortir de MySQL contient:
* 'xxx' => 'blah', 'descriptif' => 'foo', 'valeur' => 3
* et le tableau de mapping contient:
* descriptif = protocole.descriptif, valeur = vote
* Alors $retour[ contient:
*
* */
static function mapVotesToImages($votes, &$images) {
if(!$votes) return;
 
// pour chaque vote
foreach ($votes as $vote) {
$imgid = $vote['image.id'];
$protoid = $vote['protocole.id'];
// un vote sans image associée ? est-ce possible ?
// if(!isset($images[$imgid])) continue;
if(!array_key_exists('protocoles_votes', $images[$imgid]) ||
!array_key_exists($protoid, $images[$imgid]['protocoles_votes'])) {
// extrait les champs spécifique au protocole (le LEFT JOIN de chargerVotesImage les ramène en doublons
$protocole = array_intersect_key($vote, array_flip(self::$mappings['protocoles']));
$images[$imgid]['protocoles_votes'][$protoid] = $protocole;
}
 
$vote = array_intersect_key($vote, array_flip(self::$mappings['votes']));
$images[$imgid]['protocoles_votes'][$protoid]['votes'][$vote['vote.id']] = $vote;
}
}
 
// Charger les commentaires et leurs votes associés
static function chargerCommentaires($db, &$observation) {
$select = array('votes' =>
array('id_vote', 'ce_proposition', 'ce_utilisateur', 'valeur', 'date' /* del_commentaire_vote */),
'commentaires' =>
array('id_commentaire', 'ce_observation', 'ce_proposition', 'ce_commentaire_parent', 'texte',
'ce_utilisateur', 'utilisateur_prenom', 'utilisateur_nom', 'utilisateur_courriel',
'nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nt', 'famille', 'nom_referentiel', 'date',
'proposition_initiale'));
$vote_fields = DelTk::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'cv'); // "v": cf alias dans la requête
$comment_fields = DelTk::sqlFieldsToAlias(self::$mappings['commentaires'], $select['commentaires'], 'dc');
 
$commentaires = $db->recupererTous(sprintf(
"SELECT %s, %s FROM del_commentaire as dc".
// LEFT JOIN optionnel, mais explicatif:
// on ne récupère des infos de vote que pour les commentaires comportant un
// nom_sel "valide"
" LEFT JOIN del_commentaire_vote cv".
" ON cv.ce_proposition = dc.id_commentaire AND dc.nom_sel != '' AND dc.nom_sel IS NOT NULL".
" WHERE ce_observation = %d -- %s",
$comment_fields, $vote_fields,
$observation['id_observation'],
__FILE__ . ':' . __LINE__));
 
if(!$commentaires) return;
 
// les commentaires réunifiées et dont les votes sont mergés
$ret = array();
foreach ($commentaires as $comment) {
$commentid = $comment['id_commentaire'];
$voteid = $comment['vote.id'];
 
if(!array_key_exists($commentid, $ret)) {
$comment_extract = array_intersect_key($comment, array_flip(self::$mappings['commentaires']));
// cas particulier: conflit d'aliases avec del_commentaire_vote
$comment_extract['auteur.id'] = $comment_extract['__auteur_com'];
$comment_extract['date'] = $comment_extract['__date_com'];
unset($comment_extract['__auteur_com'], $comment_extract['__date_com']);
 
// toujours un éléments "votes", quand bien même il n'y en aurait pas
$comment_extract['votes'] = array();
$ret[$commentid] = $comment_extract;
}
 
if(!$comment['nom_sel'] || ! $voteid) continue;
$vote = array_intersect_key($comment, array_flip(self::$mappings['votes']));
$ret[$commentid]['votes'][$voteid] = $vote;
}
$observation['commentaires'] = $ret;
}
 
// cf ListeObservation::reformateObservation() et ListeImages2::reformateImagesDoubleIndex()
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
static function reformateObservationSimpleIndex($obs, $url_pattern = '') {
// XXX: cf Observation.php::consulter(), nous pourriouns ici
// conserver les valeurs vides (pour les phptests notamment, ou non)
$obs = array_map('array_filter', $obs);
$obs_merged = array();
foreach($obs as $o) {
$id = $o['id_observation'];
$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
if(!isset($obs_merged[$id])) $obs_merged[$id] = $o;
$obs_merged[$id]['images'][$image['id_image']] = $image;
}
return $obs_merged;
}
 
}
/branches/v1.1-helium/services/modules/0.1/observations/ListeObservations.php
New file
0,0 → 1,435
<?php
/**
* Le web service observations récupère toutes les observations et, pour chacune d'elle, les
* images qui lui sont associées.
* Basée sur la classe antérieure dans ListeObservations.php de
* Grégoire Duché et Aurélien Peronnet
* (formaterVote, formaterDeterminations, chargerNombreCommentaire, chargerVotes, chargerDeterminations)
*
* @category php 5.2
* @package del
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*
* TODO:
* PDO::prepare()
* Sphinx pour auteur, genre, ns, commune, tag et masque-général
*/
 
require_once(dirname(__FILE__) . '/../DelTk.php');
/*
restore_error_handler();
restore_exception_handler();
error_reporting(E_ALL);
*/
 
class ListeObservations {
 
private $conteneur;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
 
static $tris_possibles = array('date_observation');
// paramètres autorisés
 
static $sql_fields_liaisons = array(
'dob' => array('id_observation', 'nom_sel AS `determination.ns`', 'nt AS `determination.nt`',
'nom_sel_nn AS `determination.nn`', 'famille AS `determination.famille`',
'nom_referentiel AS `determination.referentiel`',
'ce_zone_geo AS id_zone_geo', 'zone_geo', 'lieudit',
'station', 'milieu', 'date_observation', 'mots_cles_texte', 'date_transmission',
'ce_utilisateur AS `auteur.id`', 'prenom_utilisateur AS `auteur.prenom`',
'nom_utilisateur AS `auteur.nom`', 'courriel_utilisateur AS observateur',
'commentaire'),
'di' => array('id_image', 'date_prise_de_vue AS `date`', 'hauteur',/* 'largeur','nom_original' // apparemment inutilisés */),
'du' => array('prenom', 'nom', 'courriel'),
'dc' => array('commentaire')
);
 
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
$this->conteneur->chargerConfiguration('config_observations.ini');
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
 
static function reformateObservation($obs, $url_pattern = '') {
$obs = array_map('array_filter', $obs);
$obs_merged = array();
foreach($obs as $o) {
$id = $o['id_observation'];
 
// car auteur.id peut être un email, un hash, ou un annuaire_tela.U_ID
// mais dans les deux premiers cas SELECT courriel AS observateur fait déjà l'affaire
if(!isset($o['auteur.id']) || !is_numeric($o['auteur.id'])) $o['auteur.id'] = "0";
if(!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
 
$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
if(!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
$obs_merged['"' . $id . '"']['images'][] = $image;
}
return $obs_merged;
}
 
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
**/
public function consulter($ressources, $parametres) {
// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
 
// toujours nécessaire puisque nous tapons sur v_del_image qui INNER JOIN cel_images, or nous voulons certes
// toutes les images, mais nous voulons $limite observations uniques.
$req['groupby'][] = 'vdi.id_observation';
 
$db = $this->bdd;
 
// filtrage de l'INPUT
$params = DelTk::requestFilterParams($parametres, DelTk::$parametres_autorises, $this->conteneur);
 
$params['masque.tag'] = DelTk::buildTagsAST(@$parametres['masque.tag'], 'OR', ',');
 
// ... et paramètres par défaut
$params = array_merge(DelTk::$default_params, $params);
 
// création des contraintes (masques)
DelTk::sqlAddConstraint($params, $db, $req);
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
 
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
$idobs_tab = self::getIdObs($params, $req, $db);
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
$idobs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'), $idobs_tab));
 
if($idobs) {
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
 
// 2) récupération des données nécessaires pour ces observations (obs + images)
// ici les champs récupérés sont issus de self::$sql_fields_liaisons mais sans préfixes
// car tout provient de v_del_image
$obs_unfmt = self::getInfos($idobs, $db);
 
// 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
$observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
 
// 4) récupération des données nécessaires pour ces observations (commentaires + votes)
// modifie $observations
$this->configurer();
$this->chargerDeterminations($observations);
 
// 5) restauration de l'ordre souhaité initialement
$observations = self::sortArrayByArray($observations, $idobs);
} else {
$observations = array();
$total = 0;
}
 
// 6) JSON output
$resultat = new ResultatService();
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params, Config::get('url_service')),
'resultats' => $observations);
return $resultat;
}
 
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;
}
 
// SQL helpers
/*
* Retourne une liste ordonnée d'id d'observation correspondant aux critères
* passés dans p et aux clauses where/join présentes dans le tableau $req
*
* @param p: $params (filtrés sauf escape-string)
* @param req: le tableau représentant les composants de la requete SQL
* @param db: l'instance de db
*/
static function getIdObs($p, $req, $db) {
$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
' FROM v_del_image vdi'.
' %s' . // LEFT JOIN if any
' WHERE %s'. // where-clause ou TRUE
' %s'. // group-by
' %s'. // having (si commentaires)
' ORDER BY %s %s %s'.
' LIMIT %d, %d -- %s',
$req['join'] ? implode(' ', $req['join']) : '',
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
 
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
$req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
 
$p['tri'], strtoupper($p['ordre']),
// date_transmission peut-être NULL et nous voulons de la consistence
// (sauf après r1860 de Cel)
$p['tri'] == 'date_transmission' ? ', id_observation' : '',
 
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
 
$res = $db->recupererTous($req_s);
$err = mysql_error();
if(!$res && $err) {
// http_response_code(400);
// if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
throw new Exception('not found', 400);
}
// ordre préservé, à partir d'ici c'est important.
return $res;
}
 
/*
Champs récupérés:
Pour del_images, la vue retourne déjà ce que nous recherchons de cel_obs et cel_images
(cel_obs.* et cel_[obs_]images.{id_observation, id_image, date_prise_de_vue AS date, hauteur, largeur})
Pour del_commentaires: nous voulons *
Reste ensuite à formatter.
Note: le préfixe de table utilisé ici (vdi) n'impacte *aucune* autre partie du code car rien
n'en dépend pour l'heure. (inutilisation de $req['select'])
*/
static function getInfos($idobs, $db) {
/*$select_fields = implode(',', array_merge(
array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['dob']),
array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['di']),
array_map(create_function('$a', 'return "du.".$a;'), self::$sql_fields_liaisons['du'])));*/
$select_fields = array_merge(self::$sql_fields_liaisons['dob'],
self::$sql_fields_liaisons['di']);
$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
// ' LEFT JOIN del_commentaire AS dc ON di.id_observation = dc.ce_observation AND dc.nom_sel IS NOT NULL'.
' WHERE id_observation IN (%s)',
implode(',', $select_fields),
implode(',', $idobs));
return $db->recupererTous($req_s);
}
 
 
/**
* Complément à DelTk::sqlAddConstraint()
*
* @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
* @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
* @param $c conteneur, utilisé soit pour l'appel récursif à requestFilterParams() en cas de param "masque"
* soit pour la définition du type (qui utilise la variable nb_commentaires_discussion)
*/
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
if(!empty($p['masque.tag'])) {
// TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
// i_mots_cles_texte provient de la VIEW v_del_image
if(isset($p['masque.tag']['AND'])) {
/* Lorsque nous interprêtons la chaîne provenant du masque général (cf: buildTagsAST($p['masque'], 'OR', ' ') dans sqlAddMasqueConstraint()),
nous sommes splittés par espace. Cependant, assurons que si une virgule à été saisie, nous n'aurons pas le motif
" AND CONCAT(mots_cles_texte, i_mots_cles_texte) REGEXP ',' " dans notre requête.
XXX: Au 12/11/2013, une recherche sur tag depuis le masque général implique un OU, donc le problème ne se pose pas ici */
$subwhere = array();
foreach($p['masque.tag']['AND'] as $tag) {
if(trim($tag) == ',') continue;
 
$subwhere[] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
$db->proteger(strtolower($tag)));
}
$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
}
else {
$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
$db->proteger(strtolower(implode('|', $p['masque.tag']['OR']))));
}
}
 
if(!empty($p['masque.type'])) {
self::addTypeConstraints($p['masque.type'], $db, $req, $c);
}
}
 
/* Le masque fait une recherche générique parmi de nombreux champs ci-dessus.
Nous initialisons donc ces paramètres (excepté masque biensur), et nous rappelons
récursivement. À la seule différence que nous n'utiliserons que $or_req['where']
imploded par des " OR ". */
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
if(!empty($p['masque'])) {
$or_params = array('masque.auteur' => $p['masque'],
'masque.departement' => $p['masque'],
'masque.id_zone_geo' => $p['masque'],
'masque.tag' => $p['masque'],
'masque.ns' => $p['masque'],
'masque.famille' => $p['masque'],
'masque.date' => $p['masque'],
'masque.genre' => $p['masque'],
/* milieu: TODO ? */ );
/* Cependant les champs spécifiques ont priorité sur le masque général.
Pour cette raison nous supprimons la génération de SQL du masque général sur les
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
if(isset($p['masque.tag'])) unset($or_params['masque.tag']);
if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
if(isset($p['masque.date'])) unset($or_params['masque.date']);
if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
 
 
$or_masque = DelTk::requestFilterParams($or_params, array_keys($or_params), $c);
if(isset($or_params['masque.tag'])) {
$or_masque['masque.tag'] = DelTk::buildTagsAST($p['masque'], 'OR', ' ');
}
 
// $or_req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
$or_req = array('join' => array(), 'where' => array());
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
self::sqlAddConstraint($or_masque, $db, $or_req);
 
if($or_req['where']) {
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
// utile au cas ou des jointures seraient rajoutées
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
}
}
}
 
 
private function configurer() {
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
}
 
 
/*
* @param $req: la représentation de la requête MySQL complète, à amender.
*/
static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
if(array_key_exists('adeterminer', $val)) {
// On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
$req['where'][] = '(' . implode(' OR ', array(
'vdi.certitude = "aDeterminer"',
'vdi.certitude = "douteux"',
'vdi.mots_cles_texte LIKE "%aDeterminer%"',
'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
)) . ')';
}
if(array_key_exists('validees', $val)) {
//On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
}
 
// solution n°1: impraticable
if(false && array_key_exists('endiscussion', $val)) {
//Si on veut les observations en discussion,
// on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
$req['groupby'][] = 'vdi.id_observation';
$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
}
 
if(array_key_exists('endiscussion', $val)) {
$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
}
}
 
 
/**
* Récupérer toutes les déterminations et le nombre de commentaire au total
* @param array $observations la liste des observations à mettre à jour
* */
private function chargerDeterminations(&$observations) {
$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
$observations));
$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
implode(',',$idObs),
__FILE__ . ':' . __LINE__);
$propositions = $this->bdd->recupererTous($r);
if(!$propositions) return;
foreach ($propositions as $proposition) {
$idObs = $proposition['ce_observation'];
$idComment = $proposition['id_commentaire'];
$comment = $this->formaterDetermination($idComment, $proposition);
if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
}
}
 
private function formaterDetermination($commentId, $proposition) {
if(!$proposition) return NULL;
 
$proposition_formatee = array('nb_commentaires' => '0');
foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
if (isset($proposition[$nomOriginal])) {
$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
}
}
 
// Charger les votes sur les déterminations
$resultatsVotes = $this->bdd->recupererTous(
sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
foreach ($resultatsVotes as $vote) {
$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
}
 
 
// chargerNombreCommentaire()
// Charger le nombre de commentaires (sans détermination) associé à l'observation
$listeCommentaires = $this->bdd->recupererTous(sprintf(
'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
$commentId, __FILE__ . ':' . __LINE__));
foreach ($listeCommentaires as $ligneProposition) {
// ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
if($ligneProposition['ce_commentaire_parent']) {
// TODO/debug: id_commentaire_parent != $commentId ??
// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
if($ligneProposition['ce_commentaire_parent'] != $commentId) {
// restore_error_handler();
error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
$ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
}
$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
} else {
$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
}
}
 
return $proposition_formatee;
}
 
/**
* Formater un vote en fonction du fichier de configuration config_votes.ini
* @param $votes array()
* */
private function formaterVote($vote) {
$retour = array();
foreach ($vote as $param=>$valeur) {
$retour[$this->mappingVotes[$param]] = $valeur;
}
return $retour;
}
}
/branches/v1.1-helium/services/modules/0.1/observations/ListeObservationsWidget.php
New file
0,0 → 1,394
<?php
// declare(encoding='UTF-8');
/**
* Le web service observations récupère toutes les observations et, pour chacune d'elle, les
* images qui lui sont associées.
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*/
 
class ListeObservationsWidget {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $tri = 'date_transmission';
private $directionTri = 'desc';
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_observations.ini');
$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
$this->verifierParametresTri();
$this->initialiserTri();
// Lancement du service
$observations = $this->chargerObservations();
$total = $this->compterObservations();
$this->navigation->setTotal($total);
$observations = $this->formaterObservations($observations);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $observations);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
private function verifierParametresTri() {
$erreurs = array();
$tris_possibles = $this->conteneur->getParametre('tris_possibles');
$tris_possibles_tableau = explode(',', $tris_possibles);
if(isset($this->parametres['tri']) && !in_array($this->parametres['tri'], $tris_possibles_tableau)) {
$erreurs[] = '- le type de tri demandé est incorrect, les valeurs possibles sont '.$tris_possibles.' ;';
}
$directions_tri = array('asc', 'desc');
if(isset($this->parametres['ordre']) && !in_array($this->parametres['ordre'], $directions_tri)) {
$erreurs[] = '- la direction du tri demandé est incorrecte, les valeurs supportées sont asc ou desc ;';
}
if (!empty($erreurs)) {
$e = 'Erreur lors de l\'analyse des parametres du tri : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
private function initialiserTri() {
$this->tri = isset($this->parametres['tri']) ? $this->parametres['tri'] : $this->tri;
$this->directionTri = isset($this->parametres['ordre']) ? $this->parametres['ordre'] : $this->directionTri;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
private function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingObservation = $this->conteneur->getParametre('mapping_observation');
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
}
/**
* Vérifier que le service est bien configuré
* */
private function verifierConfiguration() {
$erreurs = array();
$tableauObservations = $this->conteneur->getParametre('observations');
if (empty($tableauObservations)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [images] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
if ($this->conteneur->getParametre('url_images') == null) {
$erreurs[] = '- paramètre "url_images" manquant ;';
}
}
 
if (empty($this->mappingObservation)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_observation] ou celui-ci est vide ;';
} else {
$champsMappingObs = array('id_observation', 'date_observation', 'date_transmission', 'famille', 'nom_sel', 'nom_sel_nn', 'nt',
'ce_zone_geo', 'zone_geo', 'lieudit', 'station', 'courriel', 'ce_utilisateur', 'nom', 'prenom');
foreach ($champsMappingObs as $champ) {
if (!isset($this->mappingObservation[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
// nom du masque => nom BDD
case 'auteur' :
$whereAuteur = ' '.$this->creerFiltreAuteur($this->masque->getMasque('auteur'));
if($whereAuteur != '') {
$where[] = $whereAuteur;
}
break;
case 'date' :
$whereDate = ' '.$this->creerFiltreDate($valeurMasque);
if($whereDate != '') {
$where[] = $whereDate;
}
break;
case 'departement' :
$where[] = ' '.$this->creerFiltreIdZoneGeo($valeurMasque);
break;
case 'genre' :
$where[] = ' '.$this->mappingFiltre['ns'].' LIKE '.$this->proteger('%'.$valeurMasque.'% %');
break;
case 'tag' :
$where[] = ' '.$this->creerFiltreMotsCles($valeurMasque);
break;
case 'ns' :
$where[] = ' nom_sel LIKE '.$this->proteger($valeurMasque.'%');
break;
case 'commune' :
$where[] = ' '.$this->mappingFiltre[$idMasque].' LIKE '.$this->proteger(str_replace(array('-',' '), '_', $valeurMasque).'%');
break;
case 'masque' :
$where[] = ' '.$this->creerFiltreMasqueGeneral($valeurMasque);
break;
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
private function creerFiltreMasqueGeneral($valeurMasque) {
//TODO: affecter d'aborder les variables, puis les tester pour les
// ajouter à la chaine
$whereAuteur = $this->creerFiltreAuteur($valeurMasque);
$whereIdZoneGeo = $this->creerFiltreIdZoneGeo($valeurMasque);
$masqueGeneral = '( '.
(($whereAuteur != '') ? $whereAuteur.' OR ' : '' ).
(($whereIdZoneGeo != '') ? $whereIdZoneGeo.' OR ' : '' ).
'zone_geo LIKE '.$this->proteger($this->remplacerParJokerCaractere($valeurMasque).'%').' OR '.
$this->creerFiltreMotsCles($valeurMasque).' OR '.
'nom_sel LIKE '.$this->proteger($valeurMasque.'%').' OR '.
'famille LIKE '.$this->proteger($valeurMasque.'%').' OR '.
'milieu LIKE '.$this->proteger($valeurMasque).' OR '.
$this->mappingFiltre['ns'].' LIKE '.$this->proteger('%'.$valeurMasque.'% %').' OR '.
$this->creerFiltreDate($valeurMasque).
') ';
return $masqueGeneral;
}
private function creerFiltreAuteur($valeurMasque) {
$masque = '';
$auteurId = $valeurMasque;
if (is_numeric($auteurId)) {
$masque = ' ce_utilisateur = '.$auteurId;
} else {
if (strpos($auteurId, '@') === false) {
$tableauNomPrenom = explode(' ',$auteurId, 2);
if(count($tableauNomPrenom) == 2) {
// on teste potentiellement un nom prenom ou bien un prénom nom
$masque = '('.
'(nom LIKE '.$this->proteger($tableauNomPrenom[0].'%').' AND '.
'prenom LIKE '.$this->proteger($tableauNomPrenom[1].'%').') OR '.
'(nom LIKE '.$this->proteger($tableauNomPrenom[1].'%').' AND '.
'prenom LIKE '.$this->proteger($tableauNomPrenom[0].'%').') OR '.
'(dob.nom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[0].'%').' AND '.
'dob.prenom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[1].'%').') OR '.
'(dob.nom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[1].'%').' AND '.
'dob.prenom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[0].'%').') '.
')';
} else {
$masque = '(
(nom LIKE '.$this->proteger($auteurId.'%').' OR '.
'prenom LIKE '.$this->proteger($auteurId.'%').' OR '.
'dob.nom_utilisateur LIKE '.$this->proteger($auteurId.'%').' OR '.
'dob.prenom_utilisateur LIKE '.$this->proteger($auteurId.'%').')'.
')';
}
} else {
$masque = " courriel LIKE ".$this->proteger($valeurMasque.'%').
" OR dob.courriel_utilisateur LIKE ".$this->proteger($valeurMasque.'%')." ";
}
}
return $masque;
}
private function remplacerParJokerCaractere($valeurMasque) {
return str_replace(array('-',' '), '_', $valeurMasque);
}
//TODO: déplacer les fonctions ci dessus et dessous dans une classe
// utilitaire
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;
}
private function obtenirIdDepartement($nomDpt) {
$nomDpt = $this->supprimerAccents($nomDpt);
$nomDpt = strtolower(str_replace(' ','-',$nomDpt));
$idDpt = $this->conteneur->getParametre($nomDpt);
if($idDpt == null || $idDpt == ' ') {
$idDpt = ' ';
}
return $idDpt;
}
private function creerFiltreIdZoneGeo($valeurMasque) {
$masque = '';
$dept = $valeurMasque;
if (is_numeric($dept)) {
$dept = sprintf('%02s', $dept);
$dept = sprintf("%-'_5s", $dept);
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$dept);
} else {
$deptId = $this->conteneur->getParametre($dept);
if ($deptId != null) {
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$deptId.'%');
} else {
$id = $this->obtenirIdDepartement($valeurMasque);
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$id.'%');
}
}
return $masque;
}
private function creerFiltreDate($valeurMasque) {
//TODO: définir dans le fichier de config un tableau contenant plusieurs format de date
// autorisés pour la recherche, qui seraient ajoutés au OR
$masque = '(';
$masque .= (is_numeric($valeurMasque)) ? ' YEAR(date_observation) = '.$this->proteger($valeurMasque).' OR ' : '';
$masque .= " DATE_FORMAT(date_observation, '%d/%m/%Y') = ".$this->proteger($valeurMasque).' '.
')';
return $masque;
}
private function creerFiltreMotsCles($valeurMasque) {
$mots_cles = explode(' ', $valeurMasque);
$requeteMotsClesImg = '';
$requeteMotsClesObs = '';
//TODO voir s'il existe un moyen plus simple que le foreach + rtrim
// comme avec implode (attention au fait que l'on concatène des % au début et à la fin)
foreach($mots_cles as $mot_cle) {
$requeteMotsCles = $this->proteger('%'.$mot_cle.'%');
$requeteMotsClesObs .= 'dob.mots_cles_texte LIKE '.$requeteMotsCles.' AND ';
}
$requeteMotsClesImg = rtrim($requeteMotsClesImg, ' AND ');
$requeteMotsClesObs = rtrim($requeteMotsClesObs, ' AND ');
$masque = '('.
'('.$requeteMotsClesImg.') OR '.
'('.$requeteMotsClesObs.') '.
') ';
return $masque;
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES OBSERVATIONS
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de toutes les liaisons entre images et observations
* */
private function chargerObservations() {
$requete = 'SELECT DISTINCT dob.id_observation '.
'FROM '.$this->gestionBdd->formaterTable('del_observation', 'dob').
'INNER JOIN '.$this->gestionBdd->formaterTable('del_obs_image', 'doi').
'ON doi.id_observation = dob.id_observation '.
$this->chargerClauseWhere().
' ORDER BY '.$this->tri.' '.$this->directionTri.' '.
$this->gestionBdd->getLimitSql();
return $this->bdd->recupererTous($requete);
}
/**
* Compter le nombre total d'images dans la base pour affichage dans entete.
* */
private function compterObservations() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
/**
* Retourner un tableau d'images formaté en fonction des liaisons trouvées
* @param $liaisons les liaisons de la table del_obs_images
* */
private function formaterObservations($liaisons) {
$observations = array();
foreach ($liaisons as $liaison) {
$observations[] = $liaison['id_observation'];
}
return $observations;
}
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Protocoles.php
New file
0,0 → 1,196
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Protocoles extends RestService {
 
/*
* url possibles :
* http://localhost/del/services/0.1/observations/ => toutes les observations (infos obs, infos images, infos propositions, infos nb commentaires)
* http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
* */
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$resultat = '';
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
if ($this->avoirRessourceService()) {
$retour = $this->initialiserService();
}
return $retour;
}
private function avoirRessourceIdentifiant($num) {
$presenceId = false;
if (is_numeric($this->ressources[$num])) {
$presenceId = true;
} else {
$message = "Le service demandé '$service' nécessite d'avoir un identifiant d'image valide";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $presenceId;
}
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->chargerNomDuProjet();
$this->chargerConfigProjet();
 
}
 
private function chargerNomDuProjet() {
$this->projetNom = 'protocoles';
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function avoirRessourceService() {
/*
* url possibles :
* http://localhost/del/services/0.1/observations/ => toutes les observations (infos obs, infos images, infos propositions, infos nb commentaires)
* http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
* */
$presenceRessourceService = false;
if (isset($this->ressources[0])) {
if ($this->avoirRessourceIdentifiant(0)) {
if (sizeof($this->ressources) == 1) {
$presenceRessourceService = true;
$this->serviceNom = 'observation';
} else {
if (isset($this->ressources[1])) {
$presenceRessourceService = $this->avoirRessourceSousService();
}
}
}
} else {
$presenceRessourceService = true;
$this->serviceNom = 'liste-protocoles';
}
return $presenceRessourceService;
}
private function avoirRessourceSousService() {
$presenceRessourceService = false;
$servicesDispo = Outils::recupererTableauConfig('servicesDispo');
if ($this->avoirRessourceIdentifiant(1)) {
$service = $this->ressources[2];
if (in_array($service, $servicesDispo)) {
$presenceRessourceService = true;
$this->serviceNom = 'vote-observation';
} else {
$message = "Le service demandé '$service' n'est pas disponible pour le projet {$this->projetNom} !\n".
"Les services disponibles sont : ".implode(', ', $servicesDispo);
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
}
return $presenceRessourceService;
}
 
private function initialiserService() {
//$this->chargerNomDuService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/MotsCles.php
New file
0,0 → 1,211
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package del
* @author Aurélien 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>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class MotsCles extends RestService {
 
/*
* url possibles :
* http://localhost/del/services/0.1/motscles/#id => les mots clés associés à une image donnée
* */
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $parametres) {
$this->methode = 'ajouter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$corps = $reponseHttp->getCorps();
return $corps;
}
public function supprimer($ressources) {
$this->methode = 'supprimer';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->ressources = $ressources;
$this->conteneur = new Conteneur();
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
public function modifier($ressources, $requeteDonnees) {
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
$retour = $this->initialiserService();
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->chargerNomDuProjet();
$this->chargerConfigProjet();
 
}
 
private function chargerNomDuProjet() {
$this->projetNom = 'motscles';
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_mots_cles.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function chargerNomService() {
// si la méthode est POST, on ajouter un commentaire
if ($this->methode == 'ajouter') {
$this->serviceNom = 'ajouter-mot-cle';
} else if ($this->methode == 'supprimer') {
$this->serviceNom = 'supprimer-mot-cle';
}
else {
//S'il n'y a pas de ressources => tous les commentaires
if (!isset($this->ressources) || empty($this->ressources)) {
$this->serviceNom = 'liste-mots-cles';
} else {
if (!is_numeric($this->ressources[0])) {
$message = "La première ressource doit être un identifiant";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
} else {
$this->serviceNom = 'consulter-mots-cles';
}
}
}
}
private function editerMessageErreurRessource() {
$message = "Le service demandé '".$this->projetNom.'/'.implode('/', $this->ressources).
"' n'est pas disponible pour le projet ".$this->projetNom." !\n".
"Les services disponibles sont : images, images/#id/votes, images/#id/vote/#id_vote";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
private function etreRessourceIdentifiant($num) {
$presenceId = false;
if (is_numeric($this->ressources[$num])) {
$presenceId = true;
} else {
$message = "Le service demandé '$service' nécessite d'avoir un identifiant d'image valide";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $presenceId;
}
 
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_mots_cles.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
} elseif ($this->methode == 'supprimer') {
$retour = $service->supprimer($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Utilisateurs.php
New file
0,0 → 1,159
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Utilisateurs extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function modifier($ressources, $parametres) {
$this->methode = 'modifier';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
$retour = $this->initialiserService();
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->projetNom = 'utilisateurs';
$this->chargerConfigProjet();
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
private function chargerNomService() {
//S'il n'y a pas de ressources => envoyer sur identification anonyme
if (!isset($this->ressources) || empty($this->ressources)) {
$this->serviceNom = 'identification-anonyme';
} else {
//S'il y a 1 ressource et que celle ci est 'deconnecter', envoyer vers deconnecter
if (sizeof($this->ressources) == 1 && ($this->ressources[0] == 'deconnecter')) {
$this->serviceNom = 'deconnecter';
} else if(sizeof($this->ressources) == 2 && $this->ressources[1] == 'preferences') {
$this->serviceNom = 'preferences';
} else if (sizeof($this->ressources) == 2) {
$this->serviceNom = 'connecter';
} else {
$this->serviceNom = 'identification-anonyme';
}
}
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
 
 
}
?>
/branches/v1.1-helium/services/modules/0.1/syndication/squelettes/atom.tpl.xml
New file
0,0 → 1,36
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><?=$titre?></title>
<link href="<?=$lien_service?>" rel="alternate" type="text/html" hreflang="fr" />
<link href="<?=$lien_service?>" rel="self" type="application/atom+xml"/>
<updated><?=$date_maj_ATOM?></updated>
<author>
<name><?=$editeur?></name>
</author>
<id><?=$guid?></id>
<rights>Copyright (c) <?=$annee_courante?>, <?=$editeur?></rights>
<generator uri="<?=$lien_service?>" version="<?=$generateur_version?>"><?=$generateur?></generator>
 
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<entry>
<id><?=$item['guid']?></id>
<title><?=$item['titre']?></title>
<? if (isset($item['lien'])) : ?>
<link href="<?=$item['lien']?>"/>
<? endif; ?>
<updated><?=$item['date_maj_ATOM']?></updated>
<published><?=$item['date_maj_ATOM']?></published>
<author><name><?=$item['modifier_par']?></name></author>
<content type="xhtml" xml:lang="fr">
<div xmlns="http://www.w3.org/1999/xhtml">
<?=$item['description'];?>
</div>
</content>
</entry>
<?php endforeach; ?>
<?php endif; ?>
 
</feed>
/branches/v1.1-helium/services/modules/0.1/syndication/squelettes/opml.tpl.xml
New file
0,0 → 1,18
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
<opml version="1.0">
<head>
<text/>
</head>
<body>
<outline text="DEL">
<?php foreach ($liste_flux as $flux) : ?>
<outline title="<?=$flux['titre']?>"
description="<?=$flux['description']?>"
htmlUrl="<?=$flux['url_html']?>"
xmlUrl="<?=$flux['url_xml']?>"
type="<?=$flux['type']?>"
text="<?=$flux['texte']?>"/>
<?php endforeach; ?>
</outline>
</body>
</opml>
/branches/v1.1-helium/services/modules/0.1/syndication/squelettes/rss1.tpl.xml
New file
0,0 → 1,44
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
 
<!DOCTYPE rdf:RDF [
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
%HTMLlat1;
]>
 
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns="http://purl.org/rss/1.0/">
 
<channel rdf:about="<?=$guid?>">
<title><?=$titre?></title>
<link><?=$lien_service?></link>
<description><?=$description?></description>
<dc:publisher><?=$editeur?></dc:publisher>
<dc:date><?=$date_maj_W3C?></dc:date>
<?php if (isset($items)) : ?>
<items>
<rdf:Seq>
<?php foreach ($items as $item) : ?>
<rdf:li resource="<?=$item['guid']?>" />
<?php endforeach; ?>
</rdf:Seq>
</items>
<?php endif; ?>
</channel>
 
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<item rdf:about="<?=$item['guid']?>">
<title><?=$item['titre']?></title>
<link><?=(isset($item['lien'])) ? $item['lien'] : 'http://www.tela-botanica.org/'?></link>
<description><?=$item['description_encodee']?></description>
<dc:creator><?=$item['modifier_par']?></dc:creator>
<dc:date><?=$item['date_maj_W3C']?></dc:date>
</item>
<?php endforeach; ?>
<?php endif; ?>
</rdf:RDF>
/branches/v1.1-helium/services/modules/0.1/syndication/squelettes/rss2.tpl.xml
New file
0,0 → 1,26
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><?=$titre?></title>
<link><?=$lien_service?></link>
<atom:link href="<?=$lien_service?>" rel="self" type="application/rss+xml" />
<description><?=$description?></description>
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<item>
<guid><?=$item['guid']?></guid>
<title><?=$item['titre']?></title>
<? if (isset($item['lien'])) : ?>
<link><?=$item['lien']?></link>
<? endif; ?>
<description><?=$item['description_encodee']?></description>
<category><?= $item['categorie'] ?></category>
<author><?=$item['modifier_par']?></author>
<pubDate><?=$item['date_maj_RSS']?></pubDate>
</item>
<?php endforeach; ?>
<?php endif; ?>
</channel>
</rss>
/branches/v1.1-helium/services/modules/0.1/syndication/SyndicationCommentaires.php
New file
0,0 → 1,315
<?php
/**
* Service fournissant des informations concernant les commentaire de DEL au format RSS1, RSS2 ou ATOM.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Aurélien 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>
* @version $Id$
* @copyright 2010
*/
class SyndicationCommentaires {
 
private $ressources = null;
private $parametres = null;
private $format = null;
private $service = null;
private $squelette = null;
private $squelette_dossier = null;
private $masque = array();
private $mappingFiltre = array();
private $conteneur = null;
private $gestionBdd = null;
private $navigation = null;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_syndication_commentaires.ini');
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->navigation = $conteneur->getNavigation();
}
 
public function consulter($params = array()) {
$this->verifierConfiguration();
 
if ($this->fluxAdminDemande()) {
$this->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersCommentaires();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
 
if (empty($this->mappingFiltre)) {
$erreurs[] = 'Le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide.';
}
 
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* Verifier si le flux admin est demande
*/
private function fluxAdminDemande() {
return $this->conteneur->getParametre('admin') != null && $this->conteneur->getParametre('admin') == 1;
}
 
private function demanderAutorisationAdmin() {
$verification = new ControleAcces($this->conteneur);
$verification->demanderAuthentificationAdmin();
}
 
private function formaterPourRss($elements) {
$donnees = $this->construireDonneesCommunesAuFlux($elements);
foreach ($elements as $element) {
$identifiants[$element['id_commentaire']] = $element['id_commentaire'];
}
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
private function construireDonneesCommunesAuFlux($infos) {
$donnees = array();
$donnees['guid'] = htmlspecialchars($this->creerUrlService());
$donnees['titre'] = 'identiPlante : commentaires et propositions';
$donnees['description'] = 'Ce flux regroupe les dernières déterminations et commentaires rédigés dans l\'application identiPlante';
$donnees['lien_service'] = htmlspecialchars($this->creerUrlService());
$donnees['lien_del'] = $this->conteneur->getParametre('delAppliLien');
$donnees['editeur'] = $this->conteneur->getParametre('editeur');
$derniere_info_en_date = reset($infos);
$date_modification_timestamp = $this->convertirDateHeureMysqlEnTimestamp($derniere_info_en_date['date']);
$donnees['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$donnees['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$donnees['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$donnees['annee_courante'] = date('Y');
$donnees['generateur'] = 'DEL - SyndicationCommentaire';
$donnees['generateur_version'] = (preg_match('/([0-9]+)/', '$Revision$', $match)) ? $match[1] : '0';
return $donnees;
}
 
private function creerUrlService() {
$url_service = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
return $url_service;
}
 
private function construireDonneesCommunesAuxItems($info) {
$item = array();
$date_modification_timestamp = $this->convertirDateHeureMysqlEnTimestamp($info['date']);
$item['date_maj_simple'] = strftime('%A %d %B %Y à %H:%M', $date_modification_timestamp);
$item['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$item['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$item['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$item['titre'] = $this->creerTitre($info);
$item['guid'] = $this->creerGuidItem($info);
$item['lien'] = $this->creerLienItem($info);
$item['categorie'] = $this->creerCategorie($item);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->creerAuteur($info);
return $item;
}
 
protected function convertirDateHeureMysqlEnTimestamp($date_heure_mysql){
$timestamp = 0;
// Le date de 1970-01-01 pose problème dans certains lecteur de Flux, on met donc la date de création de Tela
$date_heure_mysql = ($date_heure_mysql == '0000-00-00 00:00:00') ? '1999-12-14 00:00:00' : $date_heure_mysql;
if ($date_heure_mysql != '0000-00-00 00:00:00') {
$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]);
}
return $timestamp;
}
 
private function creerTitre($element) {
$nomPropose = htmlspecialchars($element['nom_sel']);
$intitule = ($element['nom_sel'] != '') ? "Proposition $nomPropose" : 'Commentaire';
$auteur = htmlspecialchars($this->creerAuteur($element));
$nomSelActuel = htmlspecialchars($element['dob_nom_sel']);
$zoneGeo = htmlspecialchars((($element['dob_zone_geo'] != '') ? $element['dob_zone_geo'] : '?'));
$dateObs = htmlspecialchars(strftime('%d %B %Y', strtotime($element['dob_date_observation'])));
 
$titre = "$intitule par $auteur pour $nomSelActuel à $zoneGeo le $dateObs";
return $titre;
}
 
private function creerAuteur($info) {
$auteur = 'Anonyme';
if ($info['utilisateur_prenom'] != '' && $info['utilisateur_nom'] != '') {
$auteur = $info['utilisateur_prenom'].' '.$info['utilisateur_nom'];
}
return $auteur;
}
 
private function creerGuidItem($element) {
$guid = sprintf($this->conteneur->getParametre('guidObsTpl'), $element['id_commentaire']);
return $guid;
}
 
private function creerLienItem($element) {
$lien = sprintf($this->conteneur->getParametre('delFicheObsTpl'), $element['dob_id_observation']);
return $lien;
}
 
private function creerCategorie($element) {
$categorie = 'Commentaires';
$categorie = htmlentities($categorie);
return $categorie;
}
 
private function creerDescription($donnees, $item) {
$idCommentaire = $donnees['id_commentaire'];
$idObs = $donnees['dob_id_observation'];
$nomPropose = ($donnees['nom_sel'] != '') ? htmlspecialchars($donnees['nom_sel']) : '';
$dateCommentaire = htmlspecialchars(strftime('%A %d %B %Y à %H:%M', $this->convertirDateHeureMysqlEnTimestamp($donnees['date'])));
$nomSelActuel = htmlspecialchars($donnees['dob_nom_sel']);
$etreProposition = ($nomPropose != '') ? true : false;
$intitule = ($etreProposition) ? 'Proposition' : 'Commentaire';
$txt = ($donnees['texte'] != '') ? htmlspecialchars($donnees['texte']) : '';
$auteur = htmlspecialchars($this->creerAuteur($donnees)).
($this->fluxAdminDemande() ? ' ('.$donnees['utilisateur_courriel'].')' : '');
$lieuObs = htmlspecialchars((($donnees['dob_zone_geo'] != '') ? $donnees['dob_zone_geo'] : '?'));
$dateObs = htmlspecialchars(str_replace(' 00:00:00', '', $donnees['dob_date_observation']));
$observateur = htmlspecialchars($this->creerObservateur($donnees));
 
$contenuCommentaire = '';
if ($etreProposition) {
$contenuCommentaire =
'<li><span class="champ">'."Nom proposé :</span> <em>$nomPropose</em></li>".
((!empty($txt)) ? '<li><span class="champ">'."Argumentaire :</span> $txt</li>" : '').
'<li><span class="champ">'."Auteur de la proposition :</span> $auteur</li>".
'<li><span class="champ">'."Proposé le :</span> $dateCommentaire</li>";
} else {
$contenuCommentaire =
'<li><span class="champ">'."Commentaire #$idCommentaire :</span> <pre>$txt</pre></li>".
'<li><span class="champ">'."Auteur du commentaire :</span> $auteur</li>".
'<li><span class="champ">'."Commenté le :</span> $dateCommentaire</li>";
}
 
$description = '<style>.champ{color:grey} .gauche{float:left;padding:0 20px 0 0;} ul{list-style-type:none;padding:0;}</style>'.
'<h2>'."$intitule identiPlante #$idCommentaire pour l'observation #$idObs".'</h2>'.
'<div class="gauche">'.
" <h3>Observation #$idObs</h3>".
' <ul>'.
' <li><span class="champ">'."Nom saisi actuel :</span> <em>$nomSelActuel</em></li>".
' <li><span class="champ">'."Lieu :</span> $lieuObs</li>".
' <li><span class="champ">'."Date :</span> $dateObs</li>".
' <li><span class="champ">'."Auteur :</span> $observateur</li>".
' </ul>'.
'</div>'.
'<div class="gauche">'.
" <h3>$intitule #$idCommentaire</h3>".
" <ul>$contenuCommentaire</ul>".
'</div>';
return $description;
}
 
private function creerObservateur($info) {
$observateur = 'Anonyme';
if ($info['observateur_prenom'] != '' && $info['observateur_nom'] != '') {
$observateur = $info['observateur_prenom'].' '.$info['observateur_nom'];
}
return $observateur;
}
 
private function getDerniersCommentaires() {
$requete = 'SELECT DISTINCT dc.*, '.
' dob.id_observation AS dob_id_observation, dob.ce_zone_geo AS dob_ce_zone_geo, '.
' dob.zone_geo AS dob_zone_geo, dob.date_observation AS dob_date_observation, dob.nom_sel AS dob_nom_sel, '.
' duo.prenom AS observateur_prenom, duo.nom AS observateur_nom, duo.courriel AS observateur_courriel '.
'FROM del_commentaire AS dc '.
' INNER JOIN del_observation AS dob '.
' ON dob.id_observation = dc.ce_observation '.
' LEFT JOIN del_utilisateur AS duo '.
' ON dob.ce_utilisateur = duo.id_utilisateur '.
'WHERE proposition_initiale != 1 '.
$this->chargerClauseWhere().' '.
'ORDER BY dc.date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' ';
 
$elements = $this->gestionBdd->getBdd()->recupererTous($requete);
return $elements;
}
 
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
 
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach ($tableauMasque as $idMasque => $valeurMasque) {
$idMasque = str_replace('masque.', '', $idMasque);
$champ = $this->mappingFiltre[$idMasque];
$valeurMasquePattern = $this->gestionBdd->getBdd()->proteger($valeurMasque.'%');
$valeurMasqueProtegee = $this->gestionBdd->getBdd()->proteger($valeurMasque);
 
switch ($idMasque) {
case 'espece':
$where[] = " dob.$champ LIKE $valeurMasquePattern OR dc.$champ LIKE $valeurMasquePattern ";
break;
case 'auteur':
$where[] = $this->creerFiltreAuteur($valeurMasque);
break;
default:
$where[] = " $champ = $valeurMasqueProtegee ";
}
}
}
 
$whereSql = '';
if (!empty($where)) {
$whereSql = ' AND '.implode('AND', $where);
}
return $whereSql;
}
 
private function creerFiltreAuteur($auteurId) {
$whereAuteur = '';
if (is_numeric($auteurId)) {
$whereAuteur = " dc.ce_utilisateur = $auteurId ";
} else {
$auteurIdMotif = $this->gestionBdd->getBdd()->proteger($auteurId.'%');
 
if (strpos($auteurId, '@') === false) {
$tableauNomPrenom = explode(' ', $auteurId, 2);
if (count($tableauNomPrenom) == 2) {
// on teste potentiellement un nom prenom ou bien un prénom nom
$nomMotif = $this->gestionBdd->getBdd()->proteger($tableauNomPrenom[0].'%');
$prenomMotif = $this->gestionBdd->getBdd()->proteger($tableauNomPrenom[1].'%');
 
$whereAuteur = ' ('.
"(dc.utilisateur_nom LIKE $nomMotif AND dc.utilisateur_prenom LIKE $prenomMotif) ".
'OR '.
"(dc.utilisateur_nom LIKE $nomMotif AND dc.utilisateur_prenom LIKE $prenomMotif) ".
') ';
} else {
$whereAuteur = "(dc.utilisateur_nom LIKE $auteurIdMotif OR dc.utilisateur_prenom LIKE $auteurIdMotif) ";
}
} else {
$whereAuteur = " dob.utilisateur_courriel LIKE $auteurIdMotif ";
}
}
return $whereAuteur;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/syndication/SyndicationTagsParProtocole.php
New file
0,0 → 1,318
<?php
/**
* Service fournissant des informations concernant les tags sur les images de DEL en fonction d'un protocole
* au format RSS1, RSS2 ou ATOM.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Grégoire DUCHE <gregoire@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version $Id$
* @copyright 2010
*/
class SyndicationTagsParProtocole {
 
/**
* Paramètres du service
* */
private $ressources = null;
private $parametres = null;
private $format = null;
private $service = null;
private $squelette = null;
private $squelette_dossier = null;
private $masque = array();
private $mappingFiltre = array();
private $conteneur = null;
private $gestionBdd = null;
private $navigation = null;
private $type_rss = null;
 
/**
* Constructeur
* Initialiser les configurations
* */
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_syndication_tagsparprotocole.ini');
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->navigation = $conteneur->getNavigation();
}
 
/**
* Consulter
* Méthode par défaut pour récupérer l'ensemble des tags.
* Vérifie la configuration et retourne les derniers tags formatés
* */
public function consulter($params = array()) {
$this->verifierConfiguration();
$this->type_rss = $params[1];
if ($this->fluxAdminDemande()) {
$this->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersVotesImage();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
 
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
} else {
$champsMappingFiltre = array('image', 'protocole');
foreach ($champsMappingFiltre as $champ) {
if (!isset($this->mappingFiltre[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour le commentaire est manquant ;';
}
}
}
 
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* Verifier si le flux admin est demandé
*/
private function fluxAdminDemande() {
return $this->conteneur->getParametre('admin') != null && $this->conteneur->getParametre('admin') == 1;
}
 
/**
* Si le flux est un flux admin, demander un mot de passe
* */
private function demanderAutorisationAdmin() {
$verification = new ControleAcces($this->conteneur);
$verification->demanderAuthentificationAdmin();
}
 
/**
* Formater les données pour mettre en page le RSS
* */
private function formaterPourRss($elements) {
$donnees = $this->construireDonneesCommunesAuFlux($elements);
foreach ($elements as $element) {
$identifiants[$element['id_tag']] = $element['id_tag'];
}
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
/**
* Générer les métadonnées du flux (titre, dates, editeur etc.)
* */
private function construireDonneesCommunesAuFlux($infos) {
$donnees = array();
$donnees['guid'] = htmlspecialchars($this->creerUrlService());
$donnees['titre'] = 'pictoFlora : tags';
$donnees['description'] = 'Ce flux regroupe les derniers tags des images de pictoFlora';
$donnees['lien_service'] = htmlspecialchars($this->creerUrlService());
$donnees['lien_del'] = $this->conteneur->getParametre('pictoAppliLien');
$donnees['editeur'] = $this->conteneur->getParametre('editeur');
$derniere_info_en_date = reset($infos);
$date_modification_timestamp = strtotime($derniere_info_en_date['date']);
$donnees['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$donnees['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$donnees['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$donnees['annee_courante'] = date('Y');
$donnees['generateur'] = 'DEL - SyndicationCommentaire';
$donnees['generateur_version'] = (preg_match('/([0-9]+)/', '$Revision$', $match)) ? $match[1] : '0';
return $donnees;
}
 
/**
* Générer le lien du flux RSS
* */
private function creerUrlService() {
$url_service = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
return $url_service;
}
 
/**
* Générer les données communes & spécifiques à chaque item
* */
private function construireDonneesCommunesAuxItems($info) {
$item = array();
$date_modification_timestamp = strtotime($info['date']);
$item['date_maj_simple'] = strftime('%A %d %B %Y à %H:%M', $date_modification_timestamp);
$item['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$item['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$item['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$item['titre'] = $this->creerTitre($info);
$item['guid'] = $this->creerGuidItem($info);
$item['lien'] = $this->creerLienItem($info);
$item['categorie'] = $this->creerCategorie($item);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->formaterNomTagueur($info);
return $item;
}
 
private function creerCategorie($element) {
$categorie = 'Tag protocole';
$categorie = htmlentities($categorie);
return $categorie;
}
 
private function creerGuidItem($element) {
$guid = sprintf($this->conteneur->getParametre('tagParProtocole'), $element['id_tag']);
return $guid;
}
 
private function creerLienItem($element) {
// TODO : ajouter un lien vers la plateforme validation de picto lorsqu'elle sera dispo
$lien = $this->conteneur->getParametre('pictofloraFicheObsTpl');
return $lien;
}
 
private function creerTitre($element) {
$tag = $element['tag'];
$nomSel = htmlspecialchars($element['nom_sel']);
$tagueur = htmlspecialchars($this->formaterNomTagueur($element));
$auteurImg = $this->formaterNomAuteurImg($element);
$titre = "Tag «{$tag}» par $tagueur pour $nomSel de $auteurImg";
return $titre;
}
 
private function creerDescription($donnees, $item) {
$idTag = htmlspecialchars($donnees['id_tag']);
$idObs = htmlspecialchars($donnees['id_observation']);
$idImg = htmlspecialchars($donnees['id_image']);
$urlImg = $this->getUrlImage($donnees['id_image']);
$miniatureUrl = $this->getUrlImage($donnees['id_image'], 'CRS');
$nomSelActuel = htmlspecialchars($donnees['nom_sel']);
$dateObs = htmlspecialchars(str_replace(' 00:00:00', '', $donnees['date_observation']));
$lieuObs = htmlspecialchars($donnees['zone_geo']);
$tag = htmlspecialchars($donnees['tag']);
$dateTag = htmlspecialchars(strftime('%A %d %B %Y à %H:%M', strtotime($donnees['date'])));
$auteurImg = htmlspecialchars($this->creerAuteurImg($donnees));
$tagueur = htmlspecialchars($this->creerTagueur($donnees));
 
$description = '<style>.champ{color:grey} .gauche{float:left;padding:0 20px 0 0;} ul{list-style-type:none;padding:0;}</style>'.
'<h2>'."Tag pictoFlora #$idTag pour l'image #$idImg de l'observation #$idObs".'</h2>'.
'<div class="gauche">'.
' <a href="'.$urlImg.'">'.
' <img src="'.$miniatureUrl.'" alt="Img #'.$idImg.'"/>'.
' </a>'.
'</div>'.
'<div class="gauche">'.
" <h3>Image #$idImg de l'observation #$idObs</h3>".
' <ul>'.
' <li><span class="champ">'."Auteur de l'image :</span> $auteurImg</li>".
' <li><span class="champ">'."Nom saisi actuel :</span> <em>$nomSelActuel</em></li>".
' <li><span class="champ">'."Lieu :</span> $lieuObs</li>".
' <li><span class="champ">'."Date :</span> $dateObs</li>".
' </ul>'.
'</div>'.
'<div class="gauche">'.
" <h3>Tag #$idTag</h3>".
' <ul>'.
' <li><span class="champ">'."Tag :</span> <strong>$tag</strong></li>".
' <li><span class="champ">'."Auteur :</span> $tagueur</li>".
' <li><span class="champ">'."Taguée le :</span> $dateTag</li>".
' </ul>'.
'</div>';
return $description;
}
 
private function getUrlImage($id, $format = 'L') {
$url_tpl = $this->conteneur->getParametre('celImgUrlTpl');
$id = sprintf('%09s', $id).$format;
$url = sprintf($url_tpl, $id);
return $url;
}
 
private function creerAuteurImg($info) {
$auteur = $this->formaterNomAuteurImg($info).
($this->fluxAdminDemande() ? ' ('.$info['auteur_courriel'].')' : '');
return $auteur;
}
 
private function formaterNomAuteurImg($info) {
$auteur = 'Anonyme';
if ($info['auteur_prenom'] != '' && $info['auteur_nom'] != '') {
$auteur = $info['auteur_prenom'].' '.$info['auteur_nom'];
}
return $auteur;
}
 
private function creerTagueur($info) {
$tagueur = $this->formaterNomTagueur($info).
($this->fluxAdminDemande() ? ' ('.$info['tagueur_courriel'].')' : '');
return $tagueur;
}
 
private function formaterNomTagueur($info) {
$tagueur = 'Anonyme';
if ($info['tagueur_prenom'] != '' && $info['tagueur_nom'] != '') {
$tagueur = $info['tagueur_prenom'].' '.$info['tagueur_nom'];
}
return $tagueur;
}
 
/**
* Retrouver les derniers votes image
* */
private function getDerniersVotesImage() {
$requete = 'SELECT DISTINCT id_tag, tag, date, '.
' do.id_observation, do.nom_sel, do.zone_geo, do.date_observation, doi.id_image, '.
' duo.prenom AS auteur_prenom, duo.nom AS auteur_nom, duo.courriel AS auteur_courriel, '.
' du.prenom AS tagueur_prenom, du.nom AS tagueur_nom, du.courriel AS tagueur_courriel '.
'FROM del_image_tag AS dit '.
' INNER JOIN del_obs_image AS doi '.
' ON ce_image = id_image '.
' INNER JOIN del_observation AS do '.
' ON doi.id_observation = do.id_observation '.
' LEFT JOIN del_utilisateur AS duo '.
' ON do.ce_utilisateur = duo.id_utilisateur '.
' LEFT JOIN del_utilisateur AS du '.
' ON if((CHAR_LENGTH(dit.ce_utilisateur) <> 32),CAST(dit.ce_utilisateur AS unsigned),0) '.
' = du.id_utilisateur '.
'WHERE actif = 1 '.
$this->chargerClauseWhere().' '.
'ORDER BY date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite();
$elements = $this->gestionBdd->getBdd()->recupererTous($requete);
return $elements;
}
 
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach ($tableauMasque as $idMasque => $valeurMasque) {
$idMasque = str_replace('masque.', '', $idMasque);
$champMasque = $this->mappingFiltre[$idMasque];
$masqueMotif = $this->gestionBdd->getBdd()->proteger($valeurMasque);
 
switch ($idMasque) {
case 'image':
//TODO : ajouter le protocole ? case 'protocole':
default:
$where[] = " $champMasque = $masqueMotif ";
}
}
}
 
$conditionsTxt = (!empty($where)) ? ' AND '.implode('AND', $where) : '';
return $conditionsTxt;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/syndication/SyndicationVotesParProtocole.php
New file
0,0 → 1,341
<?php
/**
* Service fournissant des informations concernant les votes sur les images de DEL en fonction d'un protocole
* au format RSS1, RSS2 ou ATOM.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Grégoire DUCHE <gregoire@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version $Id$
* @copyright 2010
*/
class SyndicationVotesParProtocole {
/**
* Paramètres du service
* */
private $ressources = null;
private $parametres = null;
private $format = null;
private $service = null;
private $squelette = null;
private $squelette_dossier = null;
private $masque = array();
private $mappingFiltre = array();
private $conteneur = null;
private $gestionBdd = null;
private $navigation = null;
private $type_rss = null;
 
/**
* Constructeur
* Initialiser les configurations
* */
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_syndication_votesparprotocole.ini');
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->navigation = $conteneur->getNavigation();
}
 
/**
* Consulter
* Méthode par défaut pour récupérer l'ensemble des votes.
* Vérifie la configuration et retourne les derniers votes formatés
* */
public function consulter($params = array()) {
$this->verifierConfiguration();
$this->type_rss = $params[1];
if ($this->fluxAdminDemande()) {
$this->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersVotesImage();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
 
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
} else {
$champsMappingFiltre = array('image', 'protocole');
foreach ($champsMappingFiltre as $champ) {
if (!isset($this->mappingFiltre[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour le commentaire est manquant ;';
}
}
}
 
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* Verifier si le flux admin est demandé
*/
private function fluxAdminDemande() {
return $this->conteneur->getParametre('admin') != null && $this->conteneur->getParametre('admin') == 1;
}
 
/**
* Si le flux est un flux admin, demander un mot de passe
* */
private function demanderAutorisationAdmin() {
$verification = new ControleAcces($this->conteneur);
$verification->demanderAuthentificationAdmin();
}
 
/**
* Formater les données pour mettre en page le RSS
* */
private function formaterPourRss($elements) {
$donnees = $this->construireDonneesCommunesAuFlux($elements);
foreach ($elements as $element) {
$identifiants[$element['id_vote']] = $element['id_vote'];
}
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
/**
* Générer les métadonnées du flux (titre, dates, editeur etc.)
* */
private function construireDonneesCommunesAuFlux($infos) {
$donnees = array();
$donnees['guid'] = htmlspecialchars($this->creerUrlService());
$donnees['titre'] = 'pictoFlora : votes';
$donnees['description'] = 'Ce flux regroupe les derniers votes sur les images de pictoFlora';
$donnees['lien_service'] = htmlspecialchars($this->creerUrlService());
$donnees['lien_del'] = $this->conteneur->getParametre('pictoAppliLien');
$donnees['editeur'] = $this->conteneur->getParametre('editeur');
$derniere_info_en_date = reset($infos);
$date_modification_timestamp = strtotime($derniere_info_en_date['date_vote']);
$donnees['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$donnees['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$donnees['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$donnees['annee_courante'] = date('Y');
$donnees['generateur'] = 'DEL - SyndicationCommentaire';
$donnees['generateur_version'] = (preg_match('/([0-9]+)/', '$Revision$', $match)) ? $match[1] : '0';
return $donnees;
}
 
/**
* Générer le lien du flux RSS
* */
private function creerUrlService() {
$url_service = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
return $url_service;
}
 
/**
* Générer les données communes & spécifiques à chaque item
* */
private function construireDonneesCommunesAuxItems($info) {
$item = array();
$date_modification_timestamp = strtotime($info['date_vote']);
$item['date_maj_simple'] = strftime('%A %d %B %Y à %H:%M', $date_modification_timestamp);
$item['date_maj_RSS'] = date(DATE_RSS, $date_modification_timestamp);
$item['date_maj_ATOM'] = date(DATE_ATOM, $date_modification_timestamp);
$item['date_maj_W3C'] = date(DATE_W3C, $date_modification_timestamp);
$item['titre'] = $this->creerTitre($info);
$item['guid'] = $this->creerGuidItem($info);
$item['lien'] = $this->creerLienItem($info);
$item['categorie'] = $this->creerCategorie($item);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->creerVotant($info);
return $item;
}
 
private function creerCategorie($element) {
$categorie = 'Vote protocole';
$categorie = htmlentities($categorie);
return $categorie;
}
 
private function creerGuidItem($element) {
$guid = sprintf($this->conteneur->getParametre('voteParProtocole'), $element['id_vote']);
return $guid;
}
 
private function creerLienItem($element) {
$lien = sprintf($this->conteneur->getParametre('pictofloraFicheObsTpl'), $element['id_observation']);
return $lien;
}
 
private function creerTitre($element) {
$noteVote = $element['valeur'];
$nomSci = htmlspecialchars($element['nom_sel']);
$votant = htmlspecialchars($this->creerVotant($element));
$observateur = htmlspecialchars($this->creerObservateur($element));
 
$titre = "Vote $noteVote par $votant pour $nomSci de $observateur";
return $titre;
}
 
private function creerDescription($donnees, $item) {
$idVote = htmlspecialchars($donnees['id_vote']);
$idObs = htmlspecialchars($donnees['id_observation']);
$idImg = htmlspecialchars($donnees['ce_image']);
$urlImg = $this->getUrlImage($donnees['ce_image']);
$miniatureUrl = $this->getUrlImage($donnees['ce_image'], 'CRS');
$nomSelActuel = htmlspecialchars($donnees['nom_sel']);
$dateObs = htmlspecialchars(str_replace(' 00:00:00', '', $donnees['date_observation']));
$lieuObs = htmlspecialchars($donnees['zone_geo']);
$protocole = htmlspecialchars($donnees['intitule']);
$votant = htmlspecialchars($this->creerVotant($donnees));
$dateVote = htmlspecialchars(strftime('%A %d %B %Y à %H:%M', strtotime($donnees['date_vote'])));
$observateur = htmlspecialchars($this->creerObservateur($donnees));
 
$description = '<style>.champ{color:grey} .gauche{float:left;padding:0 20px 0 0;} ul{list-style-type:none;padding:0;}</style>'.
'<h2>'."Vote pictoFlora #$idVote pour l'image #$idImg de l'observation #$idObs".'</h2>'.
'<div class="gauche">'.
' <a href="'.$urlImg.'">'.
' <img src="'.$miniatureUrl.'" alt="Img #'.$idImg.'"/>'.
' </a>'.
'</div>'.
'<div class="gauche">'.
" <h3>Image #$idImg de l'observation #$idObs</h3>".
' <ul>'.
' <li><span class="champ">'."Auteur de l'image :</span> $observateur</li>".
' <li><span class="champ">'."Nom saisi actuel :</span> <em>$nomSelActuel</em></li>".
' <li><span class="champ">'."Lieu :</span> $lieuObs</li>".
' <li><span class="champ">'."Date :</span> $dateObs</li>".
' </ul>'.
'</div>'.
'<div class="gauche">'.
" <h3>Vote #$idVote</h3>".
' <ul>'.
' <li><span class="champ">'."Protocole :</span> <strong>$protocole</strong></li>".
' <li><span class="champ">'."Valeur :</span> <strong>{$donnees['valeur']}</strong>/5</li>".
' <li><span class="champ">'."Votant :</span> $votant</li>".
' <li><span class="champ">'."À voté le :</span> $dateVote</li>".
' </ul>'.
'</div>';
return $description;
}
 
private function getUrlImage($id, $format = 'L') {
$url_tpl = $this->conteneur->getParametre('celImgUrlTpl');
$id = sprintf('%09s', $id).$format;
$url = sprintf($url_tpl, $id);
return $url;
}
 
private function creerVotant($info) {
$votant = 'Anonyme';
if (isset($info['votant_prenom']) && isset($info['votant_nom'])) {
$votant = $info['votant_prenom'].' '.$info['votant_nom'];
}
return $votant;
}
 
private function creerObservateur($info) {
$observateur = 'Anonyme';
if ($info['observateur_prenom'] != '' && $info['observateur_nom'] != '') {
$observateur = $info['observateur_prenom'].' '.$info['observateur_nom'];
}
return $observateur;
}
 
/**
* Retrouver les derniers votes image
* */
private function getDerniersVotesImage() {
$requete = 'SELECT DISTINCT id_vote, ce_image, valeur, divo.date AS date_vote, '.
' duo.prenom AS observateur_prenom, duo.nom AS observateur_nom, '.
' duv.prenom AS votant_prenom, duv.nom AS votant_nom, '.
' do.id_observation, do.nom_sel, do.zone_geo, do.date_observation, dip.intitule '.
'FROM del_image_vote AS divo '.
' INNER JOIN del_obs_image AS doi '.
' ON divo.ce_image = doi.id_image '.
' INNER JOIN del_observation AS do '.
' ON do.id_observation = doi.id_observation '.
' INNER JOIN del_image_protocole AS dip '.
' ON ce_protocole = id_protocole '.
' LEFT JOIN del_utilisateur AS duo '.
' ON do.ce_utilisateur = duo.id_utilisateur '.
' LEFT JOIN del_utilisateur AS duv '.
' ON if((CHAR_LENGTH(divo.ce_utilisateur) <> 32),CAST(divo.ce_utilisateur AS unsigned),0) '.
' = duv.id_utilisateur '.
$this->chargerClauseWhere().' '.
'ORDER BY divo.date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite();
 
$elements = $this->gestionBdd->getBdd()->recupererTous($requete);
return $elements;
}
 
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
case 'image':
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->gestionBdd->getBdd()->proteger($valeurMasque);
break;
case 'protocole':
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->gestionBdd->getBdd()->proteger($valeurMasque).' ';
break;
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->gestionBdd->getBdd()->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
 
private function creerFiltreAuteur($valeurMasque) {
$masque = '';
$auteurId = $valeurMasque;
if (is_numeric($auteurId)) {
$masque = ' dc.ce_utilisateur = '.$auteurId;
} else {
if (strpos($auteurId, '@') === false) {
$tableauNomPrenom = explode(' ',$auteurId, 2);
if(count($tableauNomPrenom) == 2) {
// on teste potentiellement un nom prenom ou bien un prénom nom
$masque = '('.
'(dc.utilisateur_nom LIKE '.$this->gestionBdd->getBdd()->proteger($tableauNomPrenom[0].'%').' AND '.
'dc.utilisateur_prenom LIKE '.$this->gestionBdd->getBdd()->proteger($tableauNomPrenom[1].'%').') OR '.
'(dc.utilisateur_nom LIKE '.$this->gestionBdd->getBdd()->proteger($tableauNomPrenom[1].'%').' AND '.
'dc.utilisateur_prenom LIKE '.$this->gestionBdd->getBdd()->proteger($tableauNomPrenom[0].'%').')'.
')';
} else {
$masque = '(
(dc.utilisateur_nom LIKE '.$this->gestionBdd->getBdd()->proteger($auteurId.'%').' OR '.
'dc.utilisateur_prenom LIKE '.$this->gestionBdd->getBdd()->proteger($auteurId.'%').')'.
')';
}
} else {
$masque = " do.utilisateur_courriel LIKE ".$this->gestionBdd->getBdd()->proteger($valeurMasque.'%')." ";
}
}
return $masque;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/determinations/ValiderDetermination.php
New file
0,0 → 1,125
<?php
// declare(encoding='UTF-8');
/**
* Le web service récupére un identifiant de proposition et appelle un service web externe
* (du cel) afin de modifier le nom de l'observation associée par celui de la proposition
*
* @category php 5.2
* @package del
* @subpackage images
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ValiderDetermination {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_determinations.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
public function modifier($ressources, $parametres) {
$this->verifierParametres($ressources, $parametres);
$id_proposition = $ressources[1];
$e = $this->modifierObservationParDetermination($id_proposition, $parametres['auteur.id']);
if($e == 'ok' || $e == 'OK' || $e == 'Not Modified') {
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_OK);
return;
}
throw new Exception("error: Cel returned \"$e\"", RestServeur::HTTP_CODE_ERREUR);
}
public function verifierParametres($ressources, $parametres) {
$erreurs = array();
if (!is_numeric($ressources[1])) {
$erreurs[] = '- le paramètre indiquant l\'identifiant de la proposition doit être numérique ;';
}
 
//Le paramètre auteur.id (id de l'auteur de la détermination)
// est là pour éviter que le $_POST ne soit vide
if (!isset($parametres['auteur.id'])) {
$erreurs[] = '- paramètre "auteur.id" manquant ;';
}
if ($erreurs) {
throw new Exception("Erreur lors de la configuration :\n" . implode("\n", $erreurs),
RestServeur::HTTP_CODE_ERREUR);
}
}
/*
* 1) récupère l'observation, autrement échec
* 2) récupère et l'auteur officiel et valide l'authentification (match sur $_POST['auteur.id'] et $_SESSION), autrement échec
* 3) prépare les nouvelles valeurs à mettre à jour dans cel_obs depuis del_commentaire
* 4) effectue la mise à jour
* 5) si tout s'est passé comme convenu, marque la proposition comme "retenue"
*/
private function modifierObservationParDetermination($id_proposition, $auteurId) {
$champs_a_modifier = array('nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nom_referentiel');
 
// obtenirInformationDetermination()
// TODO: else { LIMIT 1 ? }
$proposition = $this->bdd->recuperer(sprintf('SELECT ce_observation, id_commentaire, %s FROM del_commentaire WHERE id_commentaire = %d AND %s',
implode(',', $champs_a_modifier),
intval($id_proposition),
$auteurId ? 'ce_utilisateur = ' . $this->bdd->proteger($auteurId) : 1));
 
if(! $proposition) {
throw new Exception("Cette proposition est invalide.", RestServeur::HTTP_CODE_ERREUR);
}
 
 
$obsId = $proposition['ce_observation'];
// obtenirInformationsObservation()
$realAuteurId = $this->bdd->recuperer(sprintf('SELECT ce_utilisateur FROM del_observation WHERE id_observation = %d',
intval($obsId)));
$realAuteurId = $realAuteurId['ce_utilisateur']; // XXX: PHP-5.3
 
if(! $this->validationReqOriginMatchObsAuthor($realAuteurId)) {
throw new Exception("Seul l'utilisateur ayant saisi l'observation peut la valider : veuillez vous identifier.\n",
RestServeur::HTTP_CODE_ERREUR);
}
 
$parametres_formates = array_intersect_key($proposition,
array_flip($champs_a_modifier));
$parametres_formates = array_merge($parametres_formates,
array('id_observation' => $obsId,
'ce_utilisateur' => $realAuteurId,
// on change la "certitude" de l'observation correspondante
'certitude' => 'Certaine',
'obsKeywordDelete' => 'adeterminer'));
$ret = $this->conteneur->getRestClient()->modifier($this->conteneur->getParametre('url_service_validation_base') . $obsId,
$parametres_formates);
// cf cel/jrest/services/CelValidationObservation.php::updateElement()
// TODO: check sur HTTP code == 200, plutôt que sur texte
if($ret == 'ok' || $ret == 'OK') {
// remet à 0 le status "proposition_retenue" pour toutes les propositions faites sur cette
// observation à l'exception de celle désormais validée
$this->bdd->requeter(sprintf('UPDATE del_commentaire SET proposition_retenue = IF(id_commentaire = %d, 1, 0)' .
' WHERE ce_observation = %d -- %s',
$proposition['id_commentaire'], $obsId, __FILE__ . ':' . __LINE__));
}
return $ret;
}
private function validationReqOriginMatchObsAuthor($ce_utilisateur) {
$controle_utilisateur = new ControleAcces($this->conteneur);
$utilisateur_connecte = $controle_utilisateur->getInfosUtilisateurConnecte();
return $ce_utilisateur == $utilisateur_connecte['id_utilisateur'];
}
}
/branches/v1.1-helium/services/modules/0.1/determinations/ListeImagesDeterminationsProbables.php
New file
0,0 → 1,312
<?php
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à la détermination la plus probable
* avec la possibilité de ne renvoyer que les images les mieux notées pour un protocole donné
*
* @category php 5.2
* @package del
* @subpackage images
* @author Raphaël Droz <raphael@tela-botanica.org>
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ListeImagesDeterminationsProbables {
private $indexImagesIds = array();
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $tri = 'date_transmission';
private $directionTri = 'desc';
private $protocole_defaut = null;
private $valeur_vote_min_defaut = null;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_determinations.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
// initialiserRessourcesEtParametres()
$this->ressources = $ressources;
$this->parametres = $parametres;
if(!isset($parametres['masque.protocole'])) {
$this->parametres['masque.protocole'] = $this->protocole_defaut;
}
if(!isset($parametres['masque.valeur_vote_min'])) {
$this->parametres['masque.valeur_vote_min'] = $this->valeur_vote_min_defaut;
}
// Gestion des configuration du script
// configurer();
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingObservation = $this->conteneur->getParametre('mapping_observation');
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
// verifierConfiguration()
if(! $this->conteneur->getParametre('protocoles_possibles')) {
throw new Exception("Erreur lors de la configuration : \n- le fichier de configuration ne contient pas le parametre protocoles_possibles ou celui-ci est vide ;", RestServeur::HTTP_CODE_ERREUR);
}
 
$this->verifierParametres();
// initialiserTri()
$this->tri = isset($this->parametres['tri']) ? $this->parametres['tri'] : $this->tri;
$this->directionTri = isset($this->parametres['ordre']) ? $this->parametres['ordre'] : $this->directionTri;
 
// Lancement du service
$liaisons = $this->chargerLiaisons();
$images = array();
$total = 0;
 
if($liaisons) {
$compte = $this->bdd->recuperer('SELECT FOUND_ROWS() AS nbre');
$total = (int) $compte['nbre'];
 
$images = $this->chargerImage($liaisons);
$images = $this->chargerPropositionPlusProbable($images);
}
$this->navigation->setTotal($total);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $images);
return $resultat;
}
private function verifierParametres() {
$erreurs = array();
$tableau_protocoles = explode(',',$this->conteneur->getParametre('protocoles_possibles'));
 
if(isset($this->parametres['masque.protocole']) &&
(!is_numeric($this->parametres['masque.protocole']) ||
!in_array($this->parametres['masque.protocole'], $tableau_protocoles)
)) {
$erreurs[] = '- la valeur pour le protocole doit être un entier compris dans les numéros de protocole existants '.$this->conteneur->getParametre('protocoles_possibles');
}
if(isset($this->parametres['masque.valeur_min_vote']) &&
(!is_numeric($this->parametres['masque.valeur_min_vote']) ||
$this->parametres['masque.valeur_min_vote'] < 0 ||
$this->parametres['masque.valeur_min_vote'] > 5
)) {
$erreurs[] = '- la valeur minimum de valeur des votes doit être un entier compris entre 0 et 5 ';
}
if (!empty($erreurs)) {
throw new Exception("Erreur lors de l\'analyse des parametres du service : \n" . implode("\n", $erreurs), RestServeur::HTTP_CODE_ERREUR);
}
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES IMAGES
--------------------------------------------------------------------------------*/
private function chargerLiaisons() {
$champs = array('di.id_observation as id_observation', 'nom_referentiel', 'nom_ret', 'nom_ret_nn', 'nt', 'famille', 'ce_zone_geo', 'zone_geo',
'lieudit', 'station', 'milieu', 'date_observation', 'di.mots_cles_texte as mots_cles_texte', 'di.commentaire as commentaire',
'di.i_mots_cles_texte AS mots_cles_texte_image ', 'date_transmission', 'di.id_image as id_image', 'di.ce_utilisateur as ce_utilisateur',
'prenom', 'nom', 'courriel', 'di.nom_utilisateur', 'di.prenom_utilisateur', 'di.courriel_utilisateur', 'nom_original');
$where = array();
if(@$this->parametres['masque.protocole']) $where[] = 'ds.ce_protocole = ' . intval($this->parametres['masque.protocole']);
if(@intval($this->parametres['masque.valeur_vote_min'])) $where[] = 'moyenne >= ' . intval($this->parametres['masque.valeur_vote_min']);
 
$limite = @min(intval($this->parametres['navigation.limite']), 1000);
$limite = $limite ? $limite : 10; // 0 => 10
 
$requeteLiaisons = sprintf(
'SELECT SQL_CALC_FOUND_ROWS %s ce_protocole, moyenne, nb_votes, nb_tags'
// IF(LENGTH()) workaround pour la jointure sur tela_annuaire depuis un hash md5 casté
. ' FROM v_del_image di LEFT JOIN del_utilisateur du ON du.id_utilisateur = IF(LENGTH(di.ce_utilisateur) > 5, 0, di.ce_utilisateur)'
. ' LEFT JOIN del_image_stat ds ON di.id_image = ds.ce_image'
. ' WHERE %s GROUP BY id_image /* car plusieurs image pour obs, see PHP */ ORDER BY moyenne LIMIT %d, %d -- %s:%d',
 
implode(', ',$champs),
implode(' AND ', $where),
intval(@$this->parametres['navigation.depart']),
$limite,
__FILE__, __LINE__);
 
// GROUP BY (très couteux) car multiples observations associées à une image
// eg: 16150,16185,16245,16246,118995,129989
 
return $this->bdd->recupererTous($requeteLiaisons);
}
/**
* Retourner un tableau d'images formaté en fonction des liaisons trouvées
* @param $liaisons les liaisons de la table del_obs_images
* */
private function chargerImage($liaisons) {
$images = array();
foreach ($liaisons as $liaison) {
if($liaison['ce_utilisateur'] == 0) {
$liaison['prenom'] = $liaison['prenom_utilisateur'];
$liaison['nom'] = $liaison['nom_utilisateur'];
}
$idObs = $liaison['id_observation'];
$idImage = $liaison['id_image'];
// On enregistre l'ID de l'obs pour n'effectuer qu'une seule requête par la suite
$this->obsIds[] = $idObs;
$index = $liaison['id_image'].'-'.$liaison['id_observation'];
$this->indexImagesIds[$idImage] = $index;
$images[$index] = array(
'id_image' => $idImage,
'id_observation' => $idObs,
'auteur.intitule' => $liaison['prenom'].' '.$liaison['nom'],
'binaire.href' => sprintf($this->conteneur->getParametre('url_images'), $idImage),
'determination.famille' => $liaison['famille'],
'determination.referentiel' => $liaison['nom_referentiel'],
'determination.ns' => $liaison['nom_ret'],
'determination.nn' => $liaison['nom_ret_nn'],
'determination.nt' => $liaison['nt'],
'date_observation' => $liaison['date_observation'],
'localite' => $this->formaterLieu($liaison),
'mots_cles_image_cel' => $this->formaterMotsClesCel($liaison['mots_cles_texte_image']),
'mots_cles_image_del' => ""
);
}
return $images;
}
/**
* Charger les votes pour chaque image
**/
private function chargerPropositionPlusProbable($images) {
$resultatsPropositions = $this->bdd->recupererTous(sprintf(
'SELECT * FROM del_commentaire WHERE ce_observation IN (%s) AND nom_sel IS NOT NULL',
implode(',', $this->obsIds)));
 
$resultatsVotes = $this->bdd->recupererTous(sprintf(
'SELECT ce_proposition, valeur, ce_utilisateur FROM del_commentaire_vote WHERE ce_proposition IN'.
' ( SELECT id_commentaire FROM del_commentaire WHERE ce_observation IN (%s) AND nom_sel IS NOT NULL )'.
' ORDER BY ce_proposition',
implode(',', $this->obsIds)));
 
$resultatsMotsCles = $this->bdd->recupererTous(sprintf(
'SELECT tag, ce_image FROM del_image_tag WHERE ce_image IN (%s) AND actif = 1',
implode(',', array_keys($this->indexImagesIds))));
 
$propositions = array();
$votes = array();
foreach($resultatsVotes as $vote) {
if(!isset($votes[$vote['ce_proposition']])) {
$votes[$vote['ce_proposition']] = 0;
}
$valeur = ($vote['valeur'] == 1) ? 1 : -1;
$votes[$vote['ce_proposition']] += is_numeric($vote['ce_utilisateur']) ? 3*$valeur : $valeur;
}
foreach($resultatsPropositions as $proposition) {
$id_proposition = $proposition['id_commentaire'];
$id_obs = $proposition['ce_observation'];
if(isset($votes[$id_proposition])) {
$score = $votes[$id_proposition];
} else {
$score = -1;
}
$proposition['valeur'] = $score;
if(!isset($propositions[$id_obs])) {
$propositions[$id_obs] = $proposition;
} else {
$score_ancienne_proposition = $propositions[$id_obs]['valeur'];
$propositions[$id_obs] = ($score >= $score_ancienne_proposition) ?
$proposition : $propositions[$id_obs];
}
}
foreach ($resultatsMotsCles as $motCle) {
$tag = $motCle['tag'];
$index = $this->indexImagesIds[$motCle['ce_image']];
if($images[$index]['mots_cles_image_del'] != "") {
$tag = ",".$tag;
}
$images[$index]['mots_cles_image_del'] .= $tag ;
}
 
foreach ($images as $id => $image) {
if ($this->doitRemplacerObservationParProposition($propositions, $image)) {
$id_obs = $image['id_observation'];
$images[$id]['determination.famille'] = $propositions[$id_obs]['famille'];
$images[$id]['determination.ns'] = $propositions[$id_obs]['nom_sel'];
$images[$id]['determination.nn'] = $propositions[$id_obs]['nom_sel_nn'];
$images[$id]['determination.nt'] = $propositions[$id_obs]['nt'];
}
if(isset($images[$id]['determination.nn'])) {
$images[$id]['url_fiche_eflore'] = sprintf($this->conteneur->getParametre('url_fiche_eflore'), $images[$id]['determination.nn']); // formaterUrlFicheEflore
}
}
return $images;
}
private function doitRemplacerObservationParProposition($propositions, $image) {
return ((isset($propositions[$image['id_observation']]) &&
$propositions[$image['id_observation']] != null &&
$propositions[$image['id_observation']]['nom_sel_nn'] != 0) &&
($propositions[$image['id_observation']]['valeur'] > 0 ||
$image['determination.nn'] == 0)
);
}
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater les mots clés du cel en n'affichant que ceux faisant partie
* d'une liste définie dans le fichier de configuration
* @param $chaineMotCleCel la chaine de mots clés du cel
* @return string la chaine filtrée
* */
private function formaterMotsClesCel($chaineMotCleCel) {
return implode(',', array_intersect(
explode(',', $this->conteneur->getParametre('mots_cles_cel_affiches')), // $tabMotsClesAffiches
explode(',', $chaineMotCleCel))); // $tabMotsClesCel
}
 
private function formaterLieu($image) {
if(! $image['ce_zone_geo']) return '';
 
$lieu = $image['zone_geo'];
$id_zone_geo = $image['ce_zone_geo'];
if(strpos($image['ce_zone_geo'], 'INSEE-C:') === 0) {
$id_zone_geo = str_replace('INSEE-C:', '', $image['ce_zone_geo']);
$id_zone_geo = strlen($id_zone_geo) >= 5 ? substr($id_zone_geo, 0, 2) : $id_zone_geo;
}
return $lieu . ' ('.$id_zone_geo.')';
}
}
/branches/v1.1-helium/services/modules/0.1/commentaires/ConsulterCommentaire.php
New file
0,0 → 1,221
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ConsulterCommentaire {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $idCommentaire;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_commentaires.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
// Lancement du service
$commentaires = $this->chargerCommentaires();
$commentaires = $this->formaterCommentaires($commentaires);
$total = $this->compterCommentaires();
$this->navigation->setTotal($total);
/*$images = $this->chargerImage($liaisons);
$images = $this->chargerVotes($images);
// Mettre en forme le résultat et l'envoyer pour affichage*/
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $commentaires);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
if (sizeof($this->ressources) == 1 && (is_numeric($this->ressources[0]))) {
$this->idCommentaire = $ressources[0];
} else {
$e = 'L\identifiant d\un commentaire est numérique';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
$tableauCommentaires = $this->conteneur->getParametre('commentaires');
if (empty($tableauCommentaires)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [commentaires] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
}
 
if (empty($this->mappingCommentaire)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_commentaire] ou celui-ci est vide ;';
} else {
$champsMappingCom = array('id_commentaire', 'texte', 'ce_utilisateur', 'utilisateur_nom', 'utilisateur_prenom', 'utilisateur_courriel', 'date');
foreach ($champsMappingCom as $champ) {
if (!isset($this->mappingCommentaire[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour le commentaire est manquant ;';
}
}
}
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
} else {
$champsMappingFiltre = array('observation');
foreach ($champsMappingFiltre as $champ) {
if (!isset($this->mappingFiltre[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
if (isset($this->idCommentaire)) {
$where[] = 'id_commentaire = '.$this->idCommentaire;
}
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
//TODO: scinder ceci en fonctions réutilisables ?
// voir si c'est interessant par rapport à la recherche générale
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de tous les commentaires
* */
private function chargerCommentaires() {
$requeteLiaisons = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS * '.
'FROM '.$this->gestionBdd->formaterTable('del_commentaire', 'dc');
$requeteLiaisons .= $this->chargerClauseWhere();
$requeteLiaisons .= $this->gestionBdd->getLimitSql();
return $this->bdd->recupererTous($requeteLiaisons);
}
/**
* Compter le nombre total de commentaires dans la base pour affichage dans entete.
* */
private function compterCommentaires() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater les commentaires
* @param $commentaires les commentaires à mettre à jour
* @return $commentaires les commentaires mis à jour
* */
private function formaterCommentaires($commentaires) {
$retourCommentaires = array();
foreach ($commentaires as $id => $commentaire) {
foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
$retourCommentaires[$nomFinal] = $commentaire[$nomOriginal];
}
}
 
return $retourCommentaires;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/commentaires/ListeCommentaires.php
New file
0,0 → 1,205
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ListeCommentaires {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_commentaires.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
// Lancement du service
$commentaires = $this->chargerCommentaires();
$commentaires = $this->formaterCommentaires($commentaires);
$total = $this->compterCommentaires();
$this->navigation->setTotal($total);
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $commentaires);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
$tableauCommentaires = $this->conteneur->getParametre('commentaires');
if (empty($tableauCommentaires)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [commentaires] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
}
 
if (empty($this->mappingCommentaire)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_commentaire] ou celui-ci est vide ;';
} else {
$champsMappingCom = array('id_commentaire', 'texte', 'ce_utilisateur', 'utilisateur_nom', 'utilisateur_prenom', 'utilisateur_courriel', 'date');
foreach ($champsMappingCom as $champ) {
if (!isset($this->mappingCommentaire[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour le commentaire est manquant ;';
}
}
}
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
} else {
$champsMappingFiltre = array('observation');
foreach ($champsMappingFiltre as $champ) {
if (!isset($this->mappingFiltre[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
//TODO: scinder ceci en fonctions réutilisables ?
// voir si c'est interessant par rapport à la recherche générale
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de tous les commentaires
* */
private function chargerCommentaires() {
$requeteLiaisons = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS * '.
'FROM '.$this->gestionBdd->formaterTable('del_commentaire', 'dc');
$requeteLiaisons .= $this->chargerClauseWhere();
$requeteLiaisons .= $this->gestionBdd->getLimitSql();
return $this->bdd->recupererTous($requeteLiaisons);
}
/**
* Compter le nombre total de commentaires dans la base pour affichage dans entete.
* */
private function compterCommentaires() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater les commentaires
* @param $commentaires les commentaires à mettre à jour
* @return $commentaires les commentaires mis à jour
* */
private function formaterCommentaires($commentaires) {
$retourCommentaires = array();
foreach ($commentaires as $id => $commentaire) {
$nouveauCommentaire = array();
foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
$nouveauCommentaire[$nomFinal] = $commentaire[$nomOriginal];
}
$retourCommentaires[$nouveauCommentaire['id_commentaire']] = $nouveauCommentaire;
}
return $retourCommentaires;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/commentaires/SupprimerCommentaire.php
New file
0,0 → 1,199
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class SupprimerCommentaire {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $commentaireId;
private $utilisateurId;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_commentaires.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function supprimer($ressources, $parametres) {
$resultat = new ResultatService();
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->configurer();
// la suppression est autorisée pour le propriétaire et l'admin sur un commentaire ou une proposition
// qui n'a jamais été commentée en retour
if ($this->sansEnfant() && $this->utilisateurEstAutorise()) {
$this->supprimerCommentaire();
$this->supprimerVotesAssocies();
} else {
$e = 'Impossible de supprimer le commentaire car il a des réponses ou ce n\'est pas le vôtre.';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/** sansEnfant permet de vérifier que le commentaire que l'on veut supprimer
* n'a aucune réponse
* */
public function sansEnfant() {
$requete = 'SELECT * FROM '.$this->gestionBdd->formaterTable('del_commentaire').
'WHERE (ce_proposition = '.$this->commentaireId.' '.
'OR ce_commentaire_parent = '.$this->commentaireId.')';
$resultats = $this->bdd->recupererTous($requete);
if (!empty($resultats)) {
$e = 'Impossible de supprimer le commentaire car il a des réponses';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
return true;
}
private function utilisateurEstAutorise() {
$autorise = true;
if(!$this->estProprietaire() && !$this->utilisateurEstAdmin()) {
$e = 'Impossible de supprimer le commentaire car l\'utilisateur n\'a pas les droits requis';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
$autorise = false;
}
return $autorise;
}
/**
* estProprietaire permet de vérifier que le commentaire appartient à l'utilisateur
*
* */
public function estProprietaire() {
$requete = 'SELECT * FROM '.$this->gestionBdd->formaterTable('del_commentaire').
'WHERE id_commentaire = '.$this->commentaireId.' '.
'AND ce_utilisateur = '.$this->utilisateurId;
$resultats = $this->bdd->recupererTous($requete);
return !empty($resultats);
}
/**
* Obtient l'utilisateur par le cookie
*/
public function getUtilisateurParCookie() {
//TODO: utiliser le cookie n'est pas très sécurisé
// ceci aussi devrait être dans une classe utilitaire
if(isset($_COOKIE['del_courriel']) && $_COOKIE['del_courriel'] != '') {
$courriel = $_COOKIE['del_courriel'];
$requete = 'SELECT id_utilisateur FROM '.$this->gestionBdd->formaterTable('del_utilisateur', 'du').
'WHERE courriel = '.$this->bdd->proteger($courriel);
$utilisateur = $this->bdd->recuperer($requete);
$this->utilisateurId = $utilisateur['id_utilisateur'];
}
}
/**
* Renvoie vrai si l'utilisateur existe dans la base de données et si son champ admin est à 1
*/
private function utilisateurEstAdmin() {
//TODO: déplacer ceci dans une classe utilitaire
$requete = 'SELECT admin FROM '.$this->gestionBdd->formaterTable('del_utilisateur_infos', 'dui').
'WHERE id_utilisateur = '.$this->bdd->proteger($this->utilisateurId);
$resultat = $this->bdd->recuperer($requete);
return ($resultat && $resultat['admin'] == 1);
}
/**
* Initialiser les attributs de la classe avec les parametres
* */
public function configurer() {
$erreurs= array();
if (isset($this->ressources[0]) && is_numeric($this->ressources[0])) {
$this->commentaireId = $this->ressources[0];
} else {
$erreurs[] = ' - l\identifiant du commentaire doit être un entier; ';
}
if(isset($_COOKIE['del_courriel']) && $_COOKIE['del_courriel'] != '') {
$this->getUtilisateurParCookie();
} else {
$erreurs[] = ' - Ce service nécessite d\'être identifié; ';
}
if (!empty($erreurs)) {
$e = 'Erreur de configuration :';
$e = implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/*-------------------------------------------------------------------------------
SUPPRESSION DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Requete effective de suppression
* */
public function supprimerCommentaire() {
$requete = 'DELETE FROM '.$this->gestionBdd->formaterTable('del_commentaire').' '.
'WHERE id_commentaire = '.$this->bdd->proteger($this->commentaireId).' '.
'AND ce_utilisateur = '.$this->bdd->proteger($this->utilisateurId);
$retour = $this->bdd->requeter($requete);
if (!$retour) {
$e = 'Erreur lors de la suppression';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Requete de suppression de votes associés au commentaire
*/
public function supprimerVotesAssocies() {
$requete = 'DELETE FROM '.$this->gestionBdd->formaterTable('del_commentaire_vote').' '.
'WHERE ce_proposition = '.$this->bdd->proteger($this->commentaireId);
$retour = $this->bdd->requeter($requete);
if (!$retour) {
$e = 'Erreur lors de la suppression des votes associés';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/commentaires/AjouterCommentaire.php
New file
0,0 → 1,261
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class AjouterCommentaire {
 
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
 
private $commentaire;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_commentaires.ini');
$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
 
/**
* Ajoute un commentaire/determination si les objets fournis en paramètres sont valides
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function ajouter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
 
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
$utilisateur = $this->chercherUtilisateur();
$proposition_initiale = $this->verifierEtCreerPropositionDeterminationInitiale();
$insertion = $this->insererCommentaire($utilisateur);
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('id_commentaire' => $insertion);
 
return $resultat;
}
 
//ce_observation=728176&texte=ABC&auteur.id=11623&auteur.prenom=grégoire&auteur.nom=duché&auteur.courriel=gregoire@tela-botanica.org
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Initialiser les attributs de la classe avec les parametres
* */
public function configurer() {
$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
//TODO : Gérer le cas d'une proposition
}
 
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
 
if (empty($this->mappingCommentaire)) {
$erreurs[] = "Le fichier de configuration ne contient pas le tableau [mapping_commentaire] ou celui-ci est vide.";
}
 
$parametresPossibles = array_values($this->mappingCommentaire);
foreach ($this->parametres as $nomParam => $valeur) {
if (!in_array($nomParam, $parametresPossibles)) {
$erreurs[] = "Paramètre « {$nomParam} » non autorisé.";
}
}
 
if (!isset($this->parametres['observation'])) {
$erreurs[] = "Impossible d'ajouter un commentaire sans observation.";
}
 
if (!isset($this->parametres['auteur.id'])) {
if (!isset($this->parametres['auteur.nom'])) {
$erreurs[] = "Pas de nom d'utilisateur donné.";
}
 
if (!isset($this->parametres['auteur.prenom'])) {
$erreurs[] = "Pas de prenom d'utilisateur donné.";
}
 
if (!isset($this->parametres['auteur.courriel'])) {
$erreurs[] = "Pas de courriel d'utilisateur donné.";
}
}
 
if (isset($this->parametres['nom_sel']) &&trim($this->parametres['nom_sel']) == '') {
$erreurs[] = "S'il est présent le paramètre «nom_sel» ne peut pas être vide.";
}
 
//TODO : Gérer le cas d'une proposition
 
if (!empty($erreurs)) {
$e = implode("\n", $erreurs);
$msg = "Erreur de configuration :\n $e";
echo $msg;
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/*-------------------------------------------------------------------------------
INSERTION DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Trouver l'utilisateur en fonction des paramètres ou de la base de données si le nom, prénom et courriel ne sont pas donnés
* @return le tableau utilisateur
* */
private function chercherUtilisateur() {
$utilisateur = array();
 
// Si l'id est fournit, on récupère les informations de l'utilisateur dans la base de données
// Sinon, on récupère les informations depuis le tableau Commentaire et on vérifie leur cohérence
if (isset($this->parametres['auteur.id'])) {
$requete = "SELECT id_utilisateur as 'auteur.id', nom as 'auteur.nom', prenom as 'auteur.prenom', courriel as 'auteur.courriel' ".
'FROM '.$this->gestionBdd->formaterTable('del_utilisateur').
'WHERE id_utilisateur = '.$this->bdd->proteger($this->parametres['auteur.id']);
$utilisateur = $this->bdd->recuperer($requete);
} else {
if (isset($this->parametres['auteur.nom']) && isset($this->parametres['auteur.prenom']) && isset($this->parametres['auteur.courriel'])) {
$utilisateur['auteur.nom'] = $this->parametres['auteur.nom'];
$utilisateur['auteur.prenom'] = $this->parametres['auteur.prenom'];
$utilisateur['auteur.courriel'] = $this->parametres['auteur.courriel'];
if ($utilisateur_recherche = $this->estValideDansBdd($utilisateur)) {
// si l'utilisateur est trouvé, on affecte son id au lieu du numéro temporaire
$utilisateur = $utilisateur_recherche;
$this->parametres['auteur.id'] = $utilisateur_recherche['auteur.id'];
}
// sinon pas d'erreur, on considere simplement que l'utilisateur
// est un homonyme ou bien qu'il a décidé d'utiliser un autre mail
}
}
 
return $utilisateur;
}
 
/**
* Vérifier que les informations saisies pour un utilisateur sont valides pour la bdd
* @param $utilisateur array le tableau de l'utilisateur
* */
private function estValideDansBdd($utilisateur) {
$nom = $this->bdd->proteger($utilisateur['auteur.nom']);
$prenom = $this->bdd->proteger($utilisateur['auteur.prenom']);
$courriel = $this->bdd->proteger($utilisateur['auteur.courriel']);
 
$requete = "SELECT id_utilisateur AS 'auteur.id', nom AS 'auteur.nom', prenom AS 'auteur.prenom', ".
" courriel AS 'auteur.courriel' ".
'FROM del_utilisateur '.
"WHERE courriel = $courriel ".
" AND nom = $nom ".
" AND prenom = $prenom ";
$utilisateurBdd = $this->bdd->recuperer($requete);
return $utilisateurBdd;
}
 
/**
* Insère un commentaire dans la table
* @param $utilisateur array la liste des paramètres utilisateur
* */
private function insererCommentaire($utilisateur) {
$requete = 'INSERT INTO del_commentaire '.
'('.$this->genererEntete($utilisateur).') '.
'VALUES ('.$this->genererValues().')';
 
$retour = $this->bdd->requeter($requete);
if ($retour == null) {
$e = "Erreur inopinée lors de l'insertion";
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
 
$retour = $this->bdd->recupererIdDernierAjout();
 
return $retour;
}
 
private function verifierEtCreerPropositionDeterminationInitiale() {
$id_observation = $this->parametres['observation'];
$proposition_existe = $this->propositionInitialeExiste($id_observation);
if (!$proposition_existe) {
$this->creerPropositionAPartirObservation($id_observation);
}
}
 
private function propositionInitialeExiste($id_observation) {
$requete = 'SELECT COUNT(*) >= 1 AS existe '.
'FROM del_commentaire '.
"WHERE ce_observation = $id_observation ".
' AND proposition_initiale = 1';
$resultat = $this->bdd->recuperer($requete);
return $resultat['existe'] == 1;
}
 
private function creerPropositionAPartirObservation($id_observation) {
$idObs = $this->bdd->proteger($id_observation);
$insertion = 'INSERT IGNORE INTO del_commentaire '.
'(ce_observation, ce_utilisateur, utilisateur_prenom, utilisateur_nom, utilisateur_courriel, '.
'nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, nt, famille, nom_referentiel, date, proposition_initiale) '.
'SELECT id_observation, ce_utilisateur, prenom, nom, '.
' courriel, nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, '.
" nt, famille, nom_referentiel, NOW(), '1' ".
'FROM del_observation AS do '.
' LEFT JOIN del_utilisateur AS du '.
' ON do.ce_utilisateur = du.id_utilisateur '.
"WHERE id_observation = $idObs ";
 
$resultat = $this->bdd->requeter($insertion);
return $resultat;
}
 
private function genererEntete($utilisateur) {
$this->parametres = array_merge($this->parametres, $utilisateur);
 
$entete = array('date');
foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
if (isset($this->parametres[$nomFinal]) && $this->parametres[$nomFinal] != null) {
$entete[] = $nomOriginal;
}
}
 
return implode(', ', $entete);
}
 
private function genererValues() {
$valeurs = array('NOW()');
foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
if (isset($this->parametres[$nomFinal]) && $this->parametres[$nomFinal] != null) {
$valeurs[] = $this->bdd->proteger($this->parametres[$nomFinal]);
}
}
 
return implode(', ', $valeurs);
}
}
?>
/branches/v1.1-helium/services/modules/0.1/communes/ListeCommunes.php
New file
0,0 → 1,81
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ListeCommunes {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $requete = null;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_communes.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Lancement du service
$communes = $this->chargerCommunes($this->masque->getMasque('nom'));
$total = $this->compterCommunes($communes);
$this->navigation->setTotal($total);
$this->conteneur->setSansLimite();
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $communes);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES COMMENTAIRES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis le serveur de toutes les communes
* */
private function chargerCommunes($requete) {
return json_decode(file_get_contents("http://www.tela-botanica.org/eflore/cel2/jrest/LocationSearch/".$requete));
}
/**
* Compter le nombre total de communes pour affichage dans entete.
* */
private function compterCommunes($communes) {
return sizeof($communes);
}
}
?>
/branches/v1.1-helium/services/modules/0.1/protocoles/ListeProtocoles.php
New file
0,0 → 1,144
<?php
// declare(encoding='UTF-8');
/**
* Le web service observations récupère tous les protocoles avec leur id, nom et descriptif
*
* @category php 5.2
* @package del
* @subpackage protocoles
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Protocoles
*/
 
class ListeProtocoles extends RestService {
 
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
protected $bdd;
private $parametres = array();
private $ressources = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
 
/**
* Méthode principale de la classe.
* Lance la récupération des protocoles dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
 
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
 
$protocoles = $this->chargerProtocoles();
$protocoles = $this->formaterProtocoles($protocoles);
$this->navigation->setTotal(count($protocoles));
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $protocoles);
return $resultat;
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
private function configurer() {
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
}
 
/**
* Vérifier que le service est bien configuré
* */
private function verifierConfiguration() {
if (empty($this->mappingVotes)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_votes] ou celui-ci est vide ;';
} else {
$champsMappingVotes = array('id_protocole', 'intitule', 'descriptif');
foreach ($champsMappingVotes as $champ) {
if (!isset($this->mappingVotes[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour les votes est manquant ;';
}
}
}
 
$parametres_autorises = array('navigation.depart', 'navigation.limite');
foreach ($this->parametres as $cle => $valeur) {
if($cle != 'navigation.depart' && $cle != 'navigation.limite') {
$erreurs[] = '- aucun masque n\'est autorisé pour ce service ;';
break;
}
}
 
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/*-------------------------------------------------------------------------------
CHARGEMENT DES PROTOCOLES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de tous les protocoles ainsi que leur descriptif
* */
private function chargerProtocoles() {
$requeteProtocoles = 'SELECT * FROM del_image_protocole';
return $this->bdd->recupererTous($requeteProtocoles);
}
 
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
 
/**
* Formater les protocoles
* @param array $protocoles les protocoles
* */
private function formaterProtocoles($protocoles) {
$protocolesRetour = array();
foreach ($protocoles as $protocole) {
$protocoleFormate = array();
$idProtocole = $protocole['id_protocole'];
foreach($protocole as $champProtocole => $valeur) {
$protocoleFormate[$this->mappingVotes[$champProtocole]] = $valeur;
}
$protocolesRetour[$idProtocole] = $protocoleFormate;
}
 
return $protocolesRetour;
}
 
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/DelTk.php
New file
0,0 → 1,448
<?php
/**
* DEL (Détermination en ligne [Pictoflora/Identiplante]) Toolkit
* Quelques fonctions utiles, utilisées et/ou utilisables aussi bien par images/*, observations/*
* et probablement d'autres, comme determination/*.
*
* Les domaines des fonctions tournent autour de 4 aspects:
* - gestions des paramètres d'entrée utilisateurs, valeurs par défaut et sanitization
* - génération de SQL
* - processing de tableau de pattern d'utilisation SQL assez commun
* - formattage basique de sortie (JSON)
* + quelques helpers basiques
*
* @category php 5.2
* @package del
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
*/
 
 
define('_LISTE_OBS_MAX_RESULT_LIMIT', 1000);
define('_LISTE_OBS_MAX_ID_OBS', 10e7);
// SELECT MAX(num_taxonomique) FROM bdtfx_v2_00;
define('_LISTE_OBS_MAX_BDTFX_NT', 1000000); // 44378 + 1000
// SELECT MAX(num_nom) FROM bdtfx_v2_00;
define('_LISTE_OBS_MAX_BDTFX_NN', 1000000); // 120816 + 10000
 
class DelTk {
static $parametres_autorises = array(
'masque', 'masque.famille', 'masque.nn', 'masque.referentiel', // taxon
'masque.genre', 'masque.espece', 'masque.ns', // nom_sel
'masque.commune', 'masque.departement', 'masque.id_zone_geo', // loc
'masque.auteur', 'masque.date', 'masque.tag', 'masque.type', // autres
// tri, offset
'navigation.depart', 'navigation.limite',
'tri', 'ordre', // TODO: 'total=[yes]', 'fields=[x,y,...]'
// TODO: masque.annee, masque.insee (!= departement)
);
 
static $default_params = array(
'navigation.depart' => 0, 'navigation.limite' => 10,
'tri' => 'date_transmission', 'ordre' => 'desc');
 
 
// input filtering
 
 
/* Construit un (vulgaire) abstract syntax tree:
"AND" => [ "tag1", "tag2" ]
Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
nous aurions:
"AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
 
Ici nous devons traiter les cas suivants:
tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
 
Théorie:
1) tags passés par "champ tag":
- support du ET/OU, et explode par virgule.
- si pas d'opérande détectée: "OU"
 
2) tags passés par "recherche générale":
- support du ET/OU, et explode par whitespace.
- si pas d'opérande détectée: "ET"
 
La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
* a,b,c => "a" $default_op "b" $default_op "c"
* a,b AND c => "a" AND "b" AND "c"
* a OR b AND c,d => "a" AND "b" AND "c" AND "d"
C'est à dire par ordre décroissant de priorité:
1) opérande contenu dans la chaîne
2) opérande par défaut
3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
 
// TODO: support des parenthèses, imbrications & co: "(", ")"
// http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
// http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
 
@param $str: la chaîne à "parser"
@param $default_op: "AND" ou "OR"
@param $additional_sep: séparateur de mots:
*/
static function buildTagsAST($str = NULL, $default_op, $additional_sep = ',') {
if(!$str) return;
$words = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
 
if(preg_match('/\b(ET|AND)\b/', $str)) $op = 'AND';
elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
else $op = $default_op;
 
if($additional_sep) {
array_walk($words,
create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
$additional_sep);
}
$words = DelTk::array_flatten($words);
$words = array_map('trim', $words);
return array($op => array_filter($words));
}
 
 
static function array_flatten($arr) {
$arr = array_values($arr);
while (list($k,$v)=each($arr)) {
if (is_array($v)) {
array_splice($arr,$k,1,$v);
next($arr);
}
}
return $arr;
}
 
// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
// au spectre passé par $values.
static function unsetIfInvalid(&$var, $index, $values) {
if(array_key_exists($index, $var)) {
if(!in_array($var[$index], $values)) unset($var[$index]);
else return $var[$index];
}
return NULL;
}
 
 
 
 
/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
Cette fonction est appelée:
- une fois sur les champs de recherche avancées
- une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
avec les traitements particuliers qui s'imposent
Par exemple: si l'on cherche "Languedoc", cela impliquera:
WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
qui s'assure d'un pattern particulier */
static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
if($parametres_autorises) { // filtrage de toute clef inconnue
$params = array_intersect_key($params, array_flip($parametres_autorises));
}
 
$p['tri'] = DelTK::unsetIfInvalid($params, 'tri', array('date_observation'));
$p['ordre'] = DelTK::unsetIfInvalid($params, 'ordre', array('asc','desc'));
$p['masque.referentiel'] = DelTK::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
 
// TODO: use filter_input(INPUT_GET);
// renvoie FALSE ou NULL si absent ou invalide
$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
FILTER_VALIDATE_INT,
array('options' => array('default' => NULL,
'min_range' => 1,
'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
FILTER_VALIDATE_INT,
array('options' => array('default' => NULL,
'min_range' => 0,
'max_range' => _LISTE_OBS_MAX_ID_OBS)));
if(isset($params['masque.departement'])) {
// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
// accept leading 0 ?
// TODO; filter patterns like 555.
if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
$p['masque.departement'] = $params['masque.departement'];
}
// cf configurations/config_departements_bruts.ini
elseif( !is_null($c) && ( $x = $c->getParametre(
strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
))) {
$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
}
}
 
if(isset($params['masque.date'])) {
// une année, TODO: masque.annee
if(is_numeric($params['masque.date'])) {
$p['masque.date'] = $params['masque.date'];
}
elseif(strpos($params['masque.date'], '/' !== false) &&
($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
$p['masque.date'] = $x;
}
elseif(strpos($params['masque.date'], '-' !== false) &&
($x = strtotime($params['masque.date'])) ) {
$p['masque.date'] = $x;
}
}
 
$p['masque.nn'] = filter_var(@$params['masque.nn'],
FILTER_VALIDATE_INT,
array('options' => array('default' => NULL,
'min_range' => 0,
'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
 
$p['masque.nt'] = filter_var(@$params['masque.nt'],
FILTER_VALIDATE_INT,
array('options' => array('default' => NULL,
'min_range' => 0,
'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
 
 
// TODO: should we really trim() ?
 
if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
 
if(isset($params['masque.famille'])) {
// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
"ASCII//TRANSLIT",
$params['masque.famille']));
}
 
// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
 
// idem pour id_zone_geo qui mappait à ce_zone_geo:
if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
}
 
// masque.commune (zone_geo)
// TODO: que faire avec des '%' en INPUT ?
// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
 
// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
// sera trimmé plus tard, cf sqlAddConstraint
if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
 
// masque.tag, idem que pour masque.genre et masque.commune
if(isset($params['masque.tag'])) {
$x = explode(',',$params['masque.tag']);
$x = array_map('trim', $x);
$p['masque.tag'] = implode('|', array_filter($x));
}
 
// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
if(isset($params['masque.type'])) {
$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
}
 
 
// TODO: masque (général)
 
 
// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
// TODO: PHP-5.3
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
}
 
 
 
// SQL helpers
 
/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
Il faut donc utiliser la syntaxe IFNULL(%s, "").
(Cette fonction effectue aussi l'implode() "final" */
static function sqlAddIfNullPourConcat($tab) {
// XXX: PHP-5.3
return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
}
 
 
 
/* Converti un tableau associatif et un préfix optionnel en une chaîne de champs adéquate
à un SELECT MySQL.
$select (optionnel) restreint les champs mappés aux valeurs de $select.
Si $select n'est pas fourni, toutes les clefs présentes dans $map seront présentes dans
le SELECT en sortie */
static function sqlFieldsToAlias($map, $select = NULL, $prefix = NULL) {
if($select) $arr = array_intersect_key($map, array_flip($select));
else $arr = $map;
$keys = array_keys($arr);
 
if($prefix) array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
else array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
 
return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
}
 
 
 
/*
Retourne une clause where du style:
CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
car cette information est redondante dans cel_image et devrait être supprimée.
*/
static function addAuteursConstraint($val, $db, &$where) {
@list($a, $b) = explode(' ', $val, 2);
// un seul terme
$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
'vdi.prenom_utilisateur', // info user anonyme de l'obs
/* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
$champs_p = array('du.nom', // idem pour le nom
'vdi.nom_utilisateur',
/* 'vdi.i_nom_utilisateur' */ );
 
/*
Note: pour l'heure, étant donnés:
- les CONVERT() de la VIEW del_utilisateur
- DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
- DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
et l'âge du capitaine...
- REGEXP est case-sensitive, et collate les caractères accentués
- LIKE est case-insensitive, et collate les caractères accentués
*/
if(! $b) {
$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
DelTk::sqlAddIfNullPourConcat($champs_n),
DelTk::sqlAddIfNullPourConcat($champs_p),
$db->proteger("%".$val."%"));
}
else {
$where[] = sprintf('(CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s)',
DelTk::sqlAddIfNullPourConcat($champs_n),
DelTk::sqlAddIfNullPourConcat($champs_p),
$db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
}
}
 
 
 
 
 
/**
* - Rempli le tableau des contraintes "where" et "join" nécessaire
* à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
*
* Attention, cela signifie que toutes les tables ne sont pas *forcément*
* join'ées, par exemple si aucune contrainte ne le nécessite.
* $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
* id d'observation qui match.
* Pour la récupération effective de "toutes" les données correspondante, il faut
* réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
*
* Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
* les JOIN qui sont utilisés.
* le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
* le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
*
* @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
* @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
*/
static function sqlAddConstraint($p, $db, &$req) {
if(!empty($p['masque.auteur'])) {
// id du poster de l'obs
$req['join'][] = 'LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = vdi.ce_utilisateur';
// id du poster de l'image... NON, c'est le même que le posteur de l'obs
// Cette jointure de table est ignoré ci-dessous pour les recherches d'auteurs
// $req['join'][] = 'LEFT JOIN del_utilisateur AS dui ON dui.id_utilisateur = vdi.i_ce_utilisateur';
 
if(is_numeric($p['masque.auteur'])) {
$req['where'][] = sprintf('(du.id_utilisateur = %1$d OR vdi.id_utilisateur = %1$d)', $p['masque.auteur']);
}
elseif(preg_match(';^.{5,}@[a-z0-9-.]{5,}$;i', $p['masque.auteur'])) {
$req['where'][] = sprintf('(du.courriel LIKE %1$s OR vdi.courriel LIKE %1$s )',
$db->proteger($p['masque.auteur'] . '%'));
}
else {
DelTk::addAuteursConstraint($p['masque.auteur'], $db, $req['where']);
}
}
 
if(!empty($p['masque.date'])) {
if(is_integer($p['masque.date']) && $p['masque.date'] < 2030 && $p['masque.date'] > 1600) {
$req['where'][] = sprintf("YEAR(vdi.date_observation) = %d", $p['masque.date']);
}
else {
$req['where'][] = sprintf("DATE_FORMAT(vdi.date_observation, '%%Y-%%m-%%d') = %s",
$db->proteger(strftime('%Y-%m-%d', $p['masque.date'])));
}
}
 
// TODO: avoir des champs d'entrée distinct
if(!empty($p['masque.departement'])) {
$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger('INSEE-C:'.$p['masque.departement']));
}
if(!empty($p['masque.id_zone_geo'])) {
$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger($p['masque.id_zone_geo']));
}
if(!empty($p['masque.genre'])) {
$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger('%' . $p['masque.genre'].'% %');
}
if(!empty($p['masque.famille'])) {
$req['where'][] = 'vdi.famille = '.$db->proteger($p['masque.famille']);
}
if(!empty($p['masque.ns'])) {
$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger($p['masque.ns'].'%');
}
if(!empty($p['masque.nn'])) {
$req['where'][] = sprintf('(vdi.nom_sel_nn = %1$d OR vdi.nom_ret_nn = %1$d)', $p['masque.nn']);
}
if(!empty($p['masque.referentiel'])) {
$req['where'][] = sprintf('vdi.nom_referentiel LIKE %s', $db->proteger($p['masque.referentiel'].'%'));
}
if(!empty($p['masque.commune'])) {
$req['where'][] = 'vdi.zone_geo LIKE '.$db->proteger($p['masque.commune'].'%');
}
}
 
 
 
// formatage de réponse HTTP
static function makeJSONHeader($total, $params, $url_service) {
$prev_url = $next_url = NULL;
$url_service_sans_slash = substr($url_service, 0, -1);
 
// aplatissons les params! - une seule couche cela dit, après débrouillez-vous
$params_a_plat = $params;
foreach ($params_a_plat as $cle_plate => $pap) {
if (is_array($pap)) {
$params_a_plat[$cle_plate] = implode(array_keys($pap), ',');
}
}
 
$next_offset = $params['navigation.depart'] + $params['navigation.limite'];
if($next_offset < $total) {
$next_url = $url_service_sans_slash . '?' . http_build_query(array_merge($params_a_plat, array('navigation.depart' => $next_offset)));
}
 
$prev_offset = $params['navigation.depart'] - $params['navigation.limite'];
if($prev_offset >= 0) {
$prev_url = $url_service_sans_slash . '?' . http_build_query(array_merge($params_a_plat, array('navigation.depart' => $prev_offset)));
}
 
return array(
'masque' => http_build_query(array_diff_key($params, array_flip(array('navigation.depart', 'navigation.limite')))),
'total' => $total,
'depart' => $params['navigation.depart'],
'limite' => $params['navigation.limite'],
'href.precedent' => $prev_url,
'href.suivant' => $next_url
);
}
}
/branches/v1.1-helium/services/modules/0.1/motscles/MotClePictoflora.php
New file
0,0 → 1,37
<?php
/**
* @author Raphaël Droz <raphael@tela-botanica.org>
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license Licence CECILL http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt
* @license Licence GNU-GPL http://www.gnu.org/licenses/gpl.html
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
class MotClePictoflora {
 
// cf: images/VotesImage.php
static function updateStats($db, $id_image) {
$id_image = intval($id_image);
if(!$id_image) throw new Exception("Ne peut mettre à jour les statistiques de vote",
RestServeur::HTTP_CODE_ERREUR);
 
// on utilise toujours cette table de stats (ListeImages) pour les mots-clef "actif".
$db->requeter(sprintf('UPDATE del_image_stat SET nb_tags = (select COUNT(id_tag) FROM del_image_tag WHERE ce_image = %1$d AND actif = 1) WHERE ce_image = %1$d', $id_image));
}
 
//TODO: déplacer les fonctions ci dessus et dessous dans une classe
// utilitaire
static function normaliserMotCle($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
// supprime les espaces, tirets et simple-quotes en underscores
return str_replace(array(" ", "-", "'"),
array('_', '_', '_'),
$str);
return mb_strtolower($str);
}
}
/branches/v1.1-helium/services/modules/0.1/motscles/ListeMotsCles.php
New file
0,0 → 1,184
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère des listes de mots clés associés aux images
*
* @category php 5.2
* @package del
* @subpackage motscles
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class ListeMotsCles {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_mots_cles.ini');
$this->conteneur->chargerConfiguration('config_mapping_mots_cles.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->configurer();
$this->verifierConfiguration();
// Lancement du service
$mots_cles = $this->chargerMotsCles();
$mots_cles = $this->formaterMotsCles($mots_cles);
$total = $this->compterMotsCles();
$this->navigation->setTotal($total);
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $mots_cles);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingMotsCles = $this->conteneur->getParametre('mapping_mots_cles');
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
 
$erreurs = array();
$tableau_mots_cles = $this->conteneur->getParametre('motscles');
if (empty($tableau_mots_cles)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [motscles] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
}
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$where[] = ' actif = 1 ';
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
//TODO: scinder ceci en fonctions réutilisables ?
// voir si c'est interessant par rapport à la recherche générale
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES MOTS CLES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de tous les mots clés
* */
private function chargerMotsCles() {
$requeteLiaisons = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS * '.
'FROM '.$this->gestionBdd->formaterTable('del_image_tag', 'dit');
$requeteLiaisons .= $this->chargerClauseWhere();
return $this->bdd->recupererTous($requeteLiaisons);
}
/**
* Compter le nombre total de mots clés dans la base pour affichage dans en tete.
* */
private function compterMotsCles() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater les mots clés
* @param $mots clés les mots clés à mettre à jour
* @return $mots clés les mots clés mis à jour
* */
private function formaterMotsCles($mots_cles) {
$retour = array();
foreach ($mots_cles as $id => $mot_cle) {
$mapping = array();
foreach ($this->mappingMotsCles as $nomOriginal => $nomFinal) {
$mapping[$nomFinal] = $mot_cle[$nomOriginal];
}
$retour[$mot_cle['id_tag']] = $mapping;
}
return $retour;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/motscles/SupprimerMotCle.php
New file
0,0 → 1,110
<?php
// declare(encoding='UTF-8');
/**
* Supprime un mot clé par son identifiant
*
* @category php 5.2
* @package del
* @subpackage images
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
require_once('MotClePictoflora.php');
 
class SupprimerMotCle {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $motCleId;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Supprime un mot-clé si les objets fournis en paramètres sont valides
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function supprimer($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->verifierConfiguration();
$insertion = $this->supprimerMotCle($this->motCleId);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
if (!isset($this->ressources[0])) {
$erreurs[] = ' - impossible de supprimer un mot clé sans l\'identifiant associé ;';
} else if(!is_numeric($this->ressources[0])) {
$erreurs[] = ' - l\'identifiant de mot clé doit être un entier ;';
}
if (!empty($erreurs)) {
$e = 'Erreur de configuration :';
$e = implode("\n", $erreurs);
echo var_dump($e);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
} else {
$this->motCleId = $this->ressources[0];
}
}
/*-------------------------------------------------------------------------------
SUPPRESSION DES MOTS CLES
--------------------------------------------------------------------------------*/
/**
* Supprime un mot clé dans la table
* @param $id_mot_cle string identifiant de mot clé
* */
private function supprimerMotCle($id_mot_cle) {
$retour = $this->bdd->requeter(sprintf('UPDATE del_image_tag '.
'SET actif = 0, date_modification = NOW() '.
'WHERE id_tag = %d',
intval($id_mot_cle)));
 
if ($retour == null) {
$e = 'Erreur inopinée lors de la suppression';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
 
MotClePictoflora::updateStats($this->bdd, $id_mot_cle);
return null;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/motscles/AjouterMotCle.php
New file
0,0 → 1,134
<?php
// declare(encoding='UTF-8');
/**
* Ajoute un mot clé en l'associant à un identifiant d'image
*
* @category php 5.2
* @package del
* @subpackage images
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
require_once('MotClePictoflora.php');
 
class AjouterMotCle {
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $parametres = array();
private $ressources = array();
private $commentaire;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Ajoute un mot-clé si les objets fournis en paramètres sont valides
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function ajouter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
// Gestion des configuration du script
$this->verifierConfiguration();
$insertion = $this->insererMotCle($this->parametres);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('id_mot_cle' => $insertion);
return $resultat;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
if (!isset($this->parametres['image'])) {
$erreurs[] = ' - impossible d\'ajouter un mot clé sans image associée ;';
} else if(!is_numeric($this->parametres['image'])) {
$erreurs[] = ' - l\'identifiant d\'image doit être un entier ;';
}
if (!isset($this->parametres['mot_cle'])) {
$erreurs[] = ' - impossible d\'ajouter un mot clé sans le mot clé ;';
}
if (!isset($this->parametres['auteur.id'])) {
$erreurs[] = ' - impossible d\'ajouter un mot clé sans auteur associé ;';
}
if (!empty($erreurs)) {
$e = 'Erreur de configuration :';
$e = implode("\n", $erreurs);
echo var_dump($e);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/*-------------------------------------------------------------------------------
INSERTION DES MOTS CLES
--------------------------------------------------------------------------------*/
/**
* Insère un mot clé dans la table
* @param $mot_cle array la liste des paramètres
* */
private function insererMotCle($mot_cle) {
$id_image = intval($mot_cle['image']);
$id_auteur = $mot_cle['auteur.id'];
$mots_cles = explode(",", $mot_cle['mot_cle']);
array_walk($mots_cles, create_function('&$val', '$val = trim($val);'));
 
$valeurs_a_inserer = array();
foreach ($mots_cles as $mot_cle_a_inserer) {
$mot_cle_normalise = MotClePictoflora::normaliserMotCle($mot_cle_a_inserer);
$valeurs_a_inserer[] = sprintf('(%d, %s, %s, %s, NOW(), 1, NOW())',
$id_image,
$this->bdd->proteger($id_auteur),
$this->bdd->proteger(trim($mot_cle_a_inserer)),
$this->bdd->proteger($mot_cle_normalise));
}
 
$retour = $this->bdd->requeter(sprintf(
'INSERT INTO del_image_tag (ce_image, ce_utilisateur, tag, tag_normalise, date, actif, date_modification) VALUES %s ',
implode(",", $valeurs_a_inserer)));
 
if ($retour == null) {
$e = 'Erreur inopinée lors de l\insertion';
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
 
MotClePictoflora::updateStats($this->bdd, $mot_cle['image']);
//TODO: voir si cela a vraiment du sens lors de l'ajout de plusieurs mots clés
return null;
}
 
}
?>
/branches/v1.1-helium/services/modules/0.1/Images.php
New file
0,0 → 1,254
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Images extends RestService {
 
/*
* url possibles :
* http://localhost/del/services/0.1/images/ => toutes les images, appel à la classe ListeImages
* http://localhost/del/services/0.1/images/#id => une image donnée => en test pour remplacer les appels à eflore/cel
* http://localhost/del/services/0.1/images/#id/votes/ => tous les votes d'une image classés par protocole
* http://localhost/del/services/0.1/images/#id/votes/protocole/#id => tous les votes d'une image et d'un protocole donné
* http://localhost/del/services/0.1/images/#id/votes/#id => un vote donné pour une image donnée.
* */
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$corps = $reponseHttp->getCorps();
return $corps;
}
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
public function supprimer($ressources) {
$requeteDonnees = array();
$this->methode = 'supprimer';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
echo $reponseHttp->getCorps();
}
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
if ($this->avoirRessourceService()) {
$retour = $this->initialiserService();
}
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->chargerNomDuProjet();
$this->chargerConfigProjet();
 
}
 
private function chargerNomDuProjet() {
$this->projetNom = 'images';
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function avoirRessourceService() {
/*
* url possibles :
* http://localhost/del/services/0.1/images/ => toutes les images, appel à la classe ListeImages
* http://localhost/del/services/0.1/images/#id => une image donnée => en test pour remplacer les appels à eflore/cel
* http://localhost/del/services/0.1/images/#id/votes/ => tous les votes d'une image classés par protocole
* http://localhost/del/services/0.1/images/#id/votes/protocole/#id => tous les votes d'une image et d'un protocole donné
* http://localhost/del/services/0.1/images/#id/votes/#id => un vote donné pour une image donnée.
* */
$presenceRessourceService = false;
if (isset($this->ressources[0])) {
if ($this->etreRessourceIdentifiant(0)) {
if (isset($this->ressources[1])) {
$presenceRessourceService = $this->avoirRessourceSousService();
} else {
$presenceRessourceService = true;
$this->serviceNom = 'liste-images';
}
}
} else {
$presenceRessourceService = true;
$this->serviceNom = 'liste-images';
}
return $presenceRessourceService;
}
private function avoirRessourceSousService() {
// TODO: utilité de votes-image et vote-image ?
// TODO: cette fonction est ignoble, la simplifier
$presenceRessourceService = false;
if (sizeof($this->ressources) >= 2) {
if ($this->ressources[1] == 'votes') {
$presenceRessourceService = true;
$this->serviceNom = 'votes-image';
} else {
$this->editerMessageErreurRessource();
}
} else {
if (isset($this->ressources[2]) && $this->etreRessourceIdentifiant(2)) {
$presenceRessourceService = true;
$this->serviceNom = 'vote-image';
} else {
$this->editerMessageErreurRessource();
}
}
return $presenceRessourceService;
}
private function editerMessageErreurRessource() {
$message = "Le service demandé '".$this->projetNom.'/'.implode('/', $this->ressources).
"' n'est pas disponible pour le projet ".$this->projetNom." !\n".
"Les services disponibles sont : images, images/#id/votes, images/#id/vote/#id_vote";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
private function etreRessourceIdentifiant($num) {
$presenceId = false;
if (is_numeric($this->ressources[$num])) {
$presenceId = true;
} else {
$message = "Le service demandé '$service' nécessite d'avoir un identifiant d'image valide";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $presenceId;
}
 
private function initialiserService() {
//$this->chargerNomDuService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
} elseif ($this->methode == 'supprimer') {
$retour = $service->supprimer($this->ressources);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/utilisateurs/Deconnecter.php
New file
0,0 → 1,36
<?php
// declare(encoding='UTF-8');
/**
* Le web service observations récupère toutes les observations et, pour chacune d'elle, les
* images qui lui sont associées.
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*/
 
class Deconnecter extends gestionUtilisateur {
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
// supprimer cookie & session
$this->oublierUtilisateur();
// renvoyer identification anonyme
$utilisateur = $this->getUtilisateurAnonyme();
return $utilisateur;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/utilisateurs/Connecter.php
New file
0,0 → 1,50
<?php
// declare(encoding='UTF-8');
/**
* Le web service observations récupère toutes les observations et, pour chacune d'elle, les
* images qui lui sont associées.
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*/
 
class Connecter extends gestionUtilisateur {
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$login = $ressources[0];
$motDePasse = $ressources[1];
$utilisateur = $this->identifierUtilisateur($login, $motDePasse);
$utilisateur = $this->verifierUtilisateur($utilisateur);
$utilisateur = $this->setUtilisateur($utilisateur);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $utilisateur;
return $resultat;
}
private function verifierUtilisateur($utilisateur) {
if ($utilisateur == null || sizeof($utilisateur) == 0 || sizeof($utilisateur) > 1) {
$e = 'Accès non autorisé';
throw new Exception($e, RestServeur::HTTP_CODE_ACCES_NON_AUTORISE);
} else {
return $utilisateur[0];
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/utilisateurs/IdentificationAnonyme.php
New file
0,0 → 1,42
<?php
// declare(encoding='UTF-8');
/**
* Le web service observations récupère toutes les observations et, pour chacune d'elle, les
* images qui lui sont associées.
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*/
 
class IdentificationAnonyme extends gestionUtilisateur {
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$utilisateur = $this->getUtilisateurAnonyme();
$utilisateurParCookie = $this->utilisateurEstIdentifie();
if ($utilisateurParCookie != null) {
$utilisateurParCookie['connecte'] = true;
$utilisateur = $utilisateurParCookie;
}
$resultat = new ResultatService();
$resultat->corps = $utilisateur;
return $resultat;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/utilisateurs/Preferences.php
New file
0,0 → 1,112
<?php
// declare(encoding='UTF-8');
/**
* Web service de gestion des préférences utilisateur
*
* @category php 5.2
* @package del
* @subpackage images
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
*/
 
class Preferences {
private $conteneur;
private $gestionBdd;
private $bdd;
public function __construct(Conteneur $conteneur = null) {
session_start();
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_utilisateurs.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Renvoie les préférences utilisateurs
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$id_utilisateur = $ressources[0];
$prefs = $this->obtenirPreferencesUtilisateur($id_utilisateur);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $prefs;
return $resultat;
}
/**
* Méthode principale de la classe.
* Modifie les préférences utilisateurs
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés dans le post
* */
public function modifier($ressources, $parametres) {
$id_utilisateur = $ressources[0];
$prefs = $this->modifierPreferencesUtilisateur($id_utilisateur, $parametres);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $prefs;
return $resultat;
}
private function obtenirPreferencesUtilisateur($id_utilisateur) {
$requete = 'SELECT * FROM del_utilisateur_infos WHERE id_utilisateur = '.$this->bdd->proteger($id_utilisateur);
$prefs_utilisateur = $this->bdd->recupererTous($requete);
if(empty($prefs_utilisateur)) {
$this->initialiserInfosUtilisateur($id_utilisateur);
$prefs_utilisateur = $this->renvoyerInfosUtilisateurDefaut($id_utilisateur);
} else {
$prefs_utilisateur = $prefs_utilisateur[0];
$prefs_utilisateur['preferences'] = json_decode($prefs_utilisateur['preferences']);
$prefs_utilisateur['admin'] = $prefs_utilisateur['admin'];
}
return $prefs_utilisateur;
}
private function modifierPreferencesUtilisateur($id_utilisateur, $prefs) {
$requete = 'UPDATE del_utilisateur_infos '.
'SET preferences = '.$this->bdd->proteger(json_encode($prefs)).' '.
'WHERE id_utilisateur = '.$this->bdd->proteger($id_utilisateur);
$resultat = $this->bdd->requeter($requete);
return $resultat;
}
private function initialiserInfosUtilisateur($id_utilisateur) {
$preferences_defaut = $this->obtenirTableauPreferenceDefaut();
$requete = 'INSERT INTO del_utilisateur_infos '.
'(id_utilisateur, admin, preferences, date_premiere_utilisation )'.
'VALUES '.
"(".$this->bdd->proteger($id_utilisateur).", 0, ".$this->bdd->proteger(json_encode($preferences_defaut)).", NOW()) ".
'ON DUPLICATE KEY UPDATE date_premiere_utilisation = NOW() ';
$resultat = $this->bdd->requeter($requete);
return $resultat;
}
private function renvoyerInfosUtilisateurDefaut($id_utilisateur) {
return array('id_utilisateur' => $id_utilisateur,
'admin' => "0",
'preferences' => $this->obtenirTableauPreferenceDefaut(),
'date_premiere_utilisation' => date('Y-m-d H:i:s'));
}
private function obtenirTableauPreferenceDefaut() {
return array('mail_notification_mes_obs' => "1",
'mail_notification_toutes_obs' => "0");
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Nomstaxons.php
New file
0,0 → 1,122
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Nomstaxons extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
$retour = $this->initialiserService();
return $retour;
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->projetNom = 'nomstaxons';
$this->chargerConfigProjet();
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$this->chargerNomService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} else {
//TODO : throw exception
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
private function chargerNomService() {
// si la méthode est POST, on ajouter un commentaire
$this->serviceNom = 'liste-taxons';
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
?>
/branches/v1.1-helium/services/modules/0.1/Observations.php
New file
0,0 → 1,222
<?php
/**
* Description :
* Classe principale de chargement des services d'eFlore.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 0.1
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
class Observations extends RestService {
 
/*
* url possibles :
* http://localhost/del/services/0.1/observations/ => toutes les observations (infos obs, infos images, infos propositions, infos nb commentaires)
* http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
* */
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = array();
private $serviceNom = array();
private $cheminCourant = null;
 
private $conteneur;
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
}
}
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
$this->conteneur = new Conteneur($this->parametres);
$resultat = $this->traiterRessources();
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
$reponseHttp->emettreLesEntetes();
}
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$retour = '';
$this->initialiserProjet();
if ($this->avoirRessourceService()) {
$retour = $this->initialiserService();
}
return $retour;
}
private function avoirRessourceIdentifiant($num) {
$presenceId = false;
if (is_numeric($this->ressources[$num])) {
$presenceId = true;
} else {
$message = "Le service demandé '$service' nécessite d'avoir un identifiant d'image valide";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $presenceId;
}
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU PROJET
------------------------------------------------------------------------------------------------------------------*/
private function initialiserProjet() {
$this->chargerNomDuProjet();
$this->chargerConfigProjet();
 
}
 
private function chargerNomDuProjet() {
$this->projetNom = 'observations';
}
 
private function chargerConfigProjet() {
$projet = $this->projetNom;
$chemin = Config::get('chemin_configurations')."config_$projet.ini";
Config::charger($chemin);
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function avoirRessourceService() {
/*
* url possibles :
* http://localhost/del/services/0.1/observations/ => toutes les observations (infos obs, infos images, infos propositions, infos nb commentaires)
* http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
* */
$presenceRessourceService = false;
if (isset($this->ressources[0])) {
if ($this->avoirRessourceIdentifiant(0)) {
if (sizeof($this->ressources) == 1) {
$presenceRessourceService = true;
$this->serviceNom = 'observation';
} else {
if (isset($this->ressources[1])) {
$presenceRessourceService = $this->avoirRessourceSousService();
}
}
}
} else {
if (isset($this->parametres['retour.format']) && $this->parametres['retour.format'] == 'widget') {
$this->methode = 'consulter';
$presenceRessourceService = true;
$this->serviceNom = 'liste-observations-widget';
} else {
$presenceRessourceService = true;
$this->serviceNom = 'liste-observations';
}
}
return $presenceRessourceService;
}
private function avoirRessourceSousService() {
$presenceRessourceService = false;
$servicesDispo = Outils::recupererTableauConfig('servicesDispo');
if ($this->avoirRessourceIdentifiant(1)) {
$service = $this->ressources[2];
if (in_array($service, $servicesDispo)) {
$presenceRessourceService = true;
$this->serviceNom = 'vote-observation';
} else {
$message = "Le service demandé '$service' n'est pas disponible pour le projet {$this->projetNom} !\n".
"Les services disponibles sont : ".implode(', ', $servicesDispo);
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
}
return $presenceRessourceService;
}
 
private function initialiserService() {
//$this->chargerNomDuService();
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
$this->conteneur->chargerConfiguration('config_'.$this->projetNom.'.ini');
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
}
}
}
if (is_null($service)) {
$message = "Le service demandé '{$this->serviceNom}' n'existe pas dans le projet {$this->projetNom} !";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
 
 
}
?>
/branches/v1.1-helium/services/modules/0.1/images/VoteImage.php
New file
0,0 → 1,175
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class VoteImage {
private $imageIds = array();
private $conteneur;
private $navigation;
private $masque;
private $gestionBdd;
private $bdd;
private $ressources;
private $parametres;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_votes.ini');
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
// Gestion des configuration du script
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->configurer();
$this->verifierConfiguration();
// Lancement du service
$votes = $this->chargerVotes();
$total = $this->compterVotes();
$this->navigation->setTotal($total);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $votes;
return $resultat;
}
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
$tableauImages = $this->conteneur->getParametre('images');
if (empty($tableauImages)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [images] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
}
 
if (empty($this->mappingVotes)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_observation] ou celui-ci est vide ;';
} else {
$champsMappingObs = array('ce_protocole','id_vote','valeur','ce_image', 'ce_utilisateur', 'nom', 'prenom');
foreach ($champsMappingObs as $champ) {
if (!isset($this->mappingVotes[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where[] = 'WHERE ce_image = '.$this->proteger($this->ressources[0]);
$where[] = 'id_vote = '.$this->proteger($this->ressources[2]);
return implode(' AND ', $where);
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES IMAGES
--------------------------------------------------------------------------------*/
/**
* Charger les votes pour chaque image
* */
private function chargerVotes() {
$requeteVotes = 'SELECT * FROM '.
$this->gestionBdd->formaterTable('del_image_vote').
$this->chargerClauseWhere();
$resultatsVotes = $this->bdd->recupererTous($requeteVotes);
$votes = $this->formaterVote($resultatsVotes[0]);
return $votes;
}
/**
* Compter le nombre total d'images dans la base pour affichage dans entete.
* */
private function compterVotes() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
 
 
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
 
/**
* Formater une observation depuis une ligne liaison
* @param $liaison liaison issue de la recherche
* @return $observation l'observation mise en forme
* */
private function formaterVote($vote) {
$retour = array();
foreach ($vote as $p=>$valeur) {
$retour[$this->mappingVotes[$p]] = $valeur;
}
return $retour;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/images/ListeImages.old.php
New file
0,0 → 1,1349
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
 
/**
* FONCTION TEMPORAIRE de debug pour afficher le contenu d'une variable en format lisible
* @param $r la variable à afficher
* */
function debug($r) {
echo '<pre>'.print_r($r, true).'</pre>';
}
 
 
/**
* Le service ListeImages récupère les données des tables observation et images
* et les mets au format JSON pour identiplante / pictoflora
* */
class ListeImages {
// Variables :
// Configuration générale du service
private $conteneur;
private $navigation;
private $bdd;
private $gestionBdd;
// Parametres
private $ressources = array();
private $parametres = array();
private $masque;
private $tri = 'date_transmission';
private $directionTri = 'desc';
private $formatRetour = 'XL';
private $imageIds = array();
/**
* Constructeur de l'application
* Initialisation des variables générale de l'application
* @param Conteneu $conteneur le conteneur de classes de l'application
* */
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->conteneur->chargerConfiguration('config_images.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->configurer();
$this->verifierConfiguration();
$this->verifierParametresTri();
$this->initialiserTri();
$this->verifierParametreFormatRetour();
$this->initialiserFormatRetour();
$parametres = $this->nettoyerParametres($parametres);
// En fonction des paramètres de recherche, on n'effectue
// pas la même requête, pour optimiser les jointures et les
// rapidités d'éxécution.
$type = $this->getTypeRequete($ressources, $parametres);
switch ($type) {
case 'obs' :
$liaisons = $this->chargerLiaisonsObs();
break;
case 'images' :
$liaisons = $this->chargerLiaisonsImages();
break;
case 'obs-images' :
$liaisons = $this->chargerLiaisons();
break;
case 'id':
$liaisons = $this->chargerLiaisonsParId();
break;
default : //case simple !
$liaisons = $this->chargerLiaisonsSimple();
}
$images = array();
if($liaisons) {
// Partie commune à tous les cas : on complète les liaisons avec les informations des votes
// et des images, puis on affiche sous forme de JSON
$images = $this->chargerImage($liaisons);
$images = $this->chargerVotes($images);
}
 
$resultat = new ResultatService();
if ($type == 'id') {
$clefsImages =array_keys($images);
$resultat->corps = $images[$clefsImages[0]];
} else {
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $images);
}
 
return $resultat;
}
/**************************************************************************************
* FONCTION LIEES AUX REQUETES *
**************************************************************************************/
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
 
$where = array();
$tableauMasque = $this->masque->getMasque();
if (!empty($tableauMasque)) {
foreach($tableauMasque as $idMasque => $valeurMasque) {
//TODO: scinder ceci en fonctions réutilisables ?
// voir si c'est interessant par rapport à la recherche générale
$idMasque = str_replace('masque.', '', $idMasque);
switch ($idMasque) {
// nom du masque => nom BDD
case 'auteur' :
$whereAuteur = ' '.$this->creerFiltreAuteur($this->masque->getMasque('auteur'));
if($whereAuteur != '') {
$where[] = $whereAuteur;
}
break;
case 'date' :
$whereDate = ' '.$this->creerFiltreDate($valeurMasque);
if($whereDate != '') {
$where[] = $whereDate;
}
break;
case 'departement' :
$where[] = ' '.$this->creerFiltreIdZoneGeo($valeurMasque);
break;
case 'genre' :
$where[] = ' '.$this->mappingFiltre['ns'].' LIKE '.$this->proteger($valeurMasque.' %');
break;
case 'tag' :
$where[] = ' '.$this->creerFiltreMotsCles($valeurMasque);
break;
case 'tag_cel' :
$where[] = ' '.$this->creerFiltreMotsClesCel($valeurMasque);
break;
case 'tag_pictoflora' :
$where[] = ' '.$this->creerFiltreMotsClesPictoflora($valeurMasque);
break;
case 'referentiel' :
$where[] = ' dob.nom_referentiel LIKE '.$this->proteger($valeurMasque.'%');
break;
case 'ns' :
$where[] = ' nom_sel LIKE '.$this->proteger($valeurMasque.'%');
break;
case 'nn' :
$num_noms = $valeurMasque;
$num_noms = explode(',', $num_noms);
$num_noms = array_map('intval', $num_noms);
$num_noms = array_filter($num_noms);
$num_noms = implode(',', $num_noms);
$where[] = "(nom_sel_nn IN ($num_noms) OR nom_ret_nn IN ($num_noms)) ";
break;
case 'commune' :
$where[] = ' '.$this->mappingFiltre[$idMasque].' LIKE '.$this->proteger(str_replace(array('-',' '), '_', $valeurMasque).'%');
break;
case 'masque' :
$where[] = ' '.$this->creerFiltreMasqueGeneral($valeurMasque);
break;
default:
$where[] = ' '.$this->mappingFiltre[$idMasque].' = '.$this->proteger($valeurMasque);
break;
}
}
}
if (!empty($where)) {
return ' WHERE '.implode('AND', $where);
} else {
return;
}
}
/**
* Créer un masque général lorsque l'on souhaite utiliser le passe partout
* @param la valeur du passe partout
* */
private function creerFiltreMasqueGeneral($valeurMasque) {
//TODO: affecter d'aborder les variables, puis les tester pour les
// ajouter à la chaine
$whereAuteur = $this->creerFiltreAuteur($valeurMasque);
$whereIdZoneGeo = $this->creerFiltreIdZoneGeo($valeurMasque);
$masqueGeneral = '( '.
(($whereAuteur != '') ? $whereAuteur.' OR ' : '' ).
(($whereIdZoneGeo != '') ? $whereIdZoneGeo.' OR ' : '' ).
'zone_geo LIKE '.$this->proteger($this->remplacerParJokerCaractere($valeurMasque).'%').' OR '.
$this->creerFiltreMotsCles($valeurMasque).' OR '.
'nom_sel LIKE '.$this->proteger($valeurMasque.'%').' OR '.
'famille LIKE '.$this->proteger($valeurMasque.'%').' OR '.
'milieu LIKE '.$this->proteger($valeurMasque).' OR '.
$this->mappingFiltre['ns'].' LIKE '.$this->proteger('%'.$valeurMasque.'% %').' OR '.
$this->creerFiltreDate($valeurMasque).
') ';
return $masqueGeneral;
}
/**
* Créer le filtre auteur en recherchant dans son nom, prénom, adresse email en fonction
* de la chaine donnée
* @param la valeur recherchée
* */
private function creerFiltreAuteur($valeurMasque) {
$masque = '';
$auteurId = $valeurMasque;
if (is_numeric($auteurId)) {
$masque = ' ce_utilisateur = '.$auteurId;
} else {
if (strpos($auteurId, '@') === false) {
$tableauNomPrenom = explode(' ',$auteurId, 2);
if(count($tableauNomPrenom) == 2) {
// on teste potentiellement un nom prenom ou bien un prénom nom
$masque = '('.
'(nom LIKE '.$this->proteger($tableauNomPrenom[0].'%').' AND '.
'prenom LIKE '.$this->proteger($tableauNomPrenom[1].'%').') OR '.
'(nom LIKE '.$this->proteger($tableauNomPrenom[1].'%').' AND '.
'prenom LIKE '.$this->proteger($tableauNomPrenom[0].'%').') OR '.
'(dob.nom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[0].'%').' AND '.
'dob.prenom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[1].'%').') OR '.
'(dob.nom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[1].'%').' AND '.
'dob.prenom_utilisateur LIKE '.$this->proteger($tableauNomPrenom[0].'%').') OR '.
'(nom LIKE '.$this->proteger($valeurMasque.'%').') OR '.
'(prenom LIKE '.$this->proteger($valeurMasque.'%').') OR '.
'(dob.nom_utilisateur LIKE '.$this->proteger($valeurMasque.'%').') OR '.
'(dob.prenom_utilisateur LIKE '.$this->proteger($valeurMasque.'%').') '.
')';
} else {
$masque = '(
(nom LIKE '.$this->proteger($auteurId.'%').' OR '.
'prenom LIKE '.$this->proteger($auteurId.'%').' OR '.
'dob.nom_utilisateur LIKE '.$this->proteger($auteurId.'%').' OR '.
'dob.prenom_utilisateur LIKE '.$this->proteger($auteurId.'%').')'.
')';
}
} else {
$masque = " ( courriel LIKE ".$this->proteger($valeurMasque.'%').
" OR dob.courriel_utilisateur LIKE ".$this->proteger($valeurMasque.'%').") ";
}
}
return $masque;
}
/**
* Obtenir une chaine de caractère concaténant nom et prénom séparé par une virgule
* @param String $auteurId l'identifiant de l'auteur
* @return String la chaine de concaténation
* */
private function getChaineNomPrenom($auteurId) {
$nomPrenom = explode(' ', $auteurId);
$nomPrenom = $this->proteger($nomPrenom);
$chaineNomPrenom = implode(', ', $nomPrenom);
return $chaineNomPrenom;
}
/**
* Créer le filtre de recherche par zone géographique en fonction du masque
* @param $valeurMasque le terme de la recherche
* */
private function creerFiltreIdZoneGeo($valeurMasque) {
$masque = '';
$dept = $valeurMasque;
if (is_numeric($dept)) {
$dept = sprintf('%02s', $dept);
$dept = sprintf("%-'_5s", $dept);
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$dept);
} else {
$deptId = $this->conteneur->getParametre($dept);
if ($deptId != null) {
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$deptId.'%');
} else {
$id = $this->obtenirIdDepartement($valeurMasque);
$masque = " ce_zone_geo LIKE ".$this->proteger('INSEE-C:'.$id.'%');
}
}
return $masque;
}
/**
* Générer la chaine de recherche pour la date en fonction du masque
* @param $valeurMasque la date recherchée (AAAA ou JJ/MM/AAAA)
* */
private function creerFiltreDate($valeurMasque) {
//TODO: définir dans le fichier de config un tableau contenant plusieurs format de date
// autorisés pour la recherche, qui seraient ajoutés au OR
$masque = '(';
$masque .= (is_numeric($valeurMasque)) ? ' YEAR(date_observation) = '.$this->proteger($valeurMasque).' OR ' : '';
$masque .= " DATE_FORMAT(date_observation, '%d/%m/%Y') = ".$this->proteger($valeurMasque).' '.
')';
return $masque;
}
/**
* Générer la chaine de recherche dans les mots clés en fonction du masque
* @param $valeurMasque le mot clé recherché
* */
private function creerFiltreMotsCles($valeurMasque) {
$mots_cles = explode(' ', $valeurMasque);
$requeteMotsClesImg = array();
$requeteMotsClesObs = array();
$requeteMotsClesImgPublic = array();
foreach($mots_cles as $mot_cle) {
//TODO: rechercher sur les mots clés normalisés dans tous les cas ?
$requeteMotsCles = $this->proteger('%'.$mot_cle.'%');
$motsCleProtege = $this->proteger($this->normaliserMotCle('%'.$mot_cle.'%'));
$requeteMotsClesImgPublic[] = 'di.id_image IN (SELECT ce_image FROM del_image_tag WHERE tag_normalise LIKE '.$motsCleProtege.' AND actif = 1)';
$requeteMotsClesImg[] = 'di.mots_cles_texte LIKE '.$requeteMotsCles;
$requeteMotsClesObs[] = 'dob.mots_cles_texte LIKE '.$requeteMotsCles;
}
$requeteMotsClesImgPublic = implode(' AND ', $requeteMotsClesImgPublic);
$requeteMotsClesImg = implode(' AND ', $requeteMotsClesImg);
$requeteMotsClesObs = implode(' AND ', $requeteMotsClesObs);
$masque = '('.
'('.$requeteMotsClesImgPublic.') OR '.
'('.$requeteMotsClesImg.') OR '.
'('.$requeteMotsClesObs.') '.
')';
return $masque;
}
/**
* Générer la chaine de recherche dans les mots clés en fonction du masque
* @param $valeurMasque le mot clé recherché
* */
private function creerFiltreMotsClesPictoflora($valeurMasque) {
$mots_cles = explode(' ', $valeurMasque);
$requeteMotsClesImg = array();
$requeteMotsClesObs = array();
$requeteMotsClesImgPublic = array();
foreach($mots_cles as $mot_cle) {
//TODO: rechercher sur les mots clés normalisés dans tous les cas ?
$requeteMotsCles = $this->proteger('%'.$mot_cle.'%');
$motsCleProtege = $this->proteger($this->normaliserMotCle('%'.$mot_cle.'%'));
$requeteMotsClesImgPublic[] = 'di.id_image IN (SELECT ce_image FROM del_image_tag WHERE tag_normalise LIKE '.$motsCleProtege.' AND actif = 1)';
}
$requeteMotsClesImgPublic = implode(' AND ', $requeteMotsClesImgPublic);
$masque = '('.$requeteMotsClesImgPublic.') ';
return $masque;
}
private function creerFiltreMotsClesCel($valeurMasque) {
$mots_cles = explode(' ', $valeurMasque);
$requeteMotsClesImg = array();
$requeteMotsClesObs = array();
foreach($mots_cles as $mot_cle) {
//TODO: rechercher sur les mots clés normalisés dans tous les cas ?
$requeteMotsCles = $this->proteger('%'.$mot_cle.'%');
$motsCleProtege = $this->proteger($this->normaliserMotCle('%'.$mot_cle.'%'));
$requeteMotsClesImg[] = 'di.mots_cles_texte LIKE '.$requeteMotsCles;
$requeteMotsClesObs[] = 'dob.mots_cles_texte LIKE '.$requeteMotsCles;
}
$requeteMotsClesImg = implode(' AND ', $requeteMotsClesImg);
$requeteMotsClesObs = implode(' AND ', $requeteMotsClesObs);
$masque = '('.
'('.$requeteMotsClesImg.') OR '.
'('.$requeteMotsClesObs.') '.
')';
return $masque;
}
// ??
private function assemblercomptageOccurencesMotsClesCel() {
$chaineMotsClesAffiches = $this->conteneur->getParametre('mots_cles_cel_affiches');
$tabMotsClesAffiches = explode(',',$chaineMotsClesAffiches);
$chaineSql = '';
// Comptage du nombre de mots clés officiels présents dans la chaine mots clés texte
foreach ($tabMotsClesAffiches as $motCle) {
if($chaineSql != '') {
$chaineSql .= ' + ';
}
$chaineSql .= 'IF(FIND_IN_SET('.$this->proteger($motCle).',di.mots_cles_texte) != 0, 1, 0)';
}
return '('.$chaineSql.')';
}
private function getTri() {
$order = '';
if($this->doitJoindreTableVotes()) {
$order = ' GROUP BY dvote.ce_image, dob.id_observation ORDER BY total_votes '.$this->directionTri.', date_transmission desc ';
} else if($this->doitJoindreTableTags()) {
$order = ' GROUP BY doi.id_image ORDER BY total_tags '.$this->directionTri.', date_transmission desc ';
} else {
$order = ' ORDER BY '.$this->tri.' '.$this->directionTri.' ';
}
return $order;
}
/**
* Compter le nombre total d'images dans la base pour affichage dans entete.
* */
private function getFoundRows() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
/**
* En fonction des paramètres, générer les conditions de recherche
* des observations
* */
private function getConditionsObs() {
$conditionsObs = array();
$masques = $this->masque->getMasque();
if (isset($masques['masque'])) {
$passe = $masques['masque'];
// Si on a saisi le masque passe partout, alors on doit chercher dans tous les champs
// de la table observation (OR)
$conditionLibre = array();
if (!isset($masques['masque.ns'])) {
$conditionsLibre[] = "nom_sel LIKE '$passe%'";
}
if (!isset($masques['masque.famille'])) {
$conditionsLibre[] = "famille LIKE '$passe%'";
}
if (!isset($masques['masque.milieu'])) {
$conditionsLibre[] = "nom_sel LIKE '$passe%'";
}
if (!isset($masques['masque.date'])) {
$conditionsLibre[] = $this->creerFiltreDate($passe);
}
if (!isset($masques['masque.auteur'])) {
$conditionsLibre[] = $this->creerFiltreAuteur($passe);
}
/*
* FIXME : remplacer par motcle projet !
* if (!isset($masques['masque.tag'])) {
$conditionsLibre[] = "mots_cles_texte LIKE '%$passe%'";
}*/
$conditionsObs[] = implode(' OR ', $conditionsLibre);
}
 
// referentiel
if (isset($masques['masque.referentiel'])) {
$ref = $masques['masque.referentiel'];
$conditionsObs[] = "dob.nom_referentiel LIKE '$ref%'";
}
// nom sel
if (isset($masques['masque.ns'])) {
$nom_sel = $masques['masque.ns'];
$conditionsObs[] = "nom_sel LIKE '$nom_sel%'";
}
// num nom
if (isset($masques['masque.nn'])) {
$num_noms = $masques['masque.nn'];
$num_noms = explode(',', $num_noms);
$num_noms = array_map('intval', $num_noms);
$num_noms = array_filter($num_noms);
$num_noms = implode(',', $num_noms);
$conditionsObs[] = "(nom_sel_nn IN ($num_noms) OR nom_ret_nn IN ($num_noms)) ";
}
// num taxon
if (isset($masques['masque.nt'])) {
$num_taxon = $masques['masque.nt'];
$conditionsObs[] = 'nt = '.intval($num_taxon);
}
// famille
if (isset($masques['masque.famille'])) {
$famille = $masques['masque.famille'];
$conditionsObs[] = "famille LIKE '$famille%'";
}
// genre
if (isset($masques['masque.genre'])) {
$genre = $masques['masque.genre'];
$conditionsObs[] = "nom_sel LIKE '$genre%'";
}
// milieu
if (isset($masques['masque.milieu'])) {
$milieu = $masques['masque.milieu'];
$conditionsObs[] = "nom_sel LIKE '$milieu%'";
}
// date
if (isset($masques['masque.date'])) {
$date = $masques['masque.date'];
$conditionsObs[] = $this->creerFiltreDate($date);
}
// utilisateur
if (isset($masques['masque.auteur'])) {
$auteur = $masques['masque.auteur'];
$conditionsObs[] = $this->creerFiltreAuteur($auteur);
}
// commune
if (isset($masques['masque.commune'])) {
$commune = $masques['masque.commune'];
$conditionsObs[] = " zone_geo LIKE ".$this->proteger(str_replace(array('-',' '), '_', $commune).'%');
}
// commune
if (isset($masques['masque.departement'])) {
$dept = $masques['masque.departement'];
$conditionsObs[] = $this->creerFiltreIdZoneGeo($dept);
}
if (isset($masques['masque.tag_cel'])) {
$motsCles = $masques['masque.tag_cel'];
$conditionsObs[] = $this->creerFiltreMotsClesCel($motsCles);
}
 
return $conditionsObs;
}
/**
* Obtenir le tableu de chaines de condition de requete images en fonction des masques
* */
private function getConditionsImages() {
$conditionsImg = array();
$masques = $this->masque->getMasque();
if (isset($masques['masque.tag_pictoflora'])) {
$tag = $masques['masque.tag_pictoflora'];
$conditionsImg[] = $this->creerFiltreMotsClesPictoflora($tag);
}
return $conditionsImg;
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES IMAGES
--------------------------------------------------------------------------------*/
/**
* Chargement depuis la bdd de toutes les liaisons entre images et observations
* Méthode appelée uniquement lorsque les paramètres sont vides
* */
private function chargerLiaisonsSimple() {
// On récupère d'abord les N images de del_obs_image, éventuellement triées,
// Et on complète avec les informations des observations associées
$requeteImages = ' SELECT *, di.mots_cles_texte as mots_cles_texte_image '.
' FROM del_obs_image doi '.
' INNER JOIN del_image di ON doi.id_image = di.id_image ';
 
// Si le tri se fait par date d'observation, on récupère les identifiants de N observations triées
if (isset($this->parametres['tri']) && $this->parametres['tri'] == 'date_observation') {
$ordre = isset($this->parametres['ordre']) ? $this->parametres['ordre'] : 'DESC';
$requeteIdObs = ' SELECT doi.id_image as id_image '.
' FROM del_obs_image doi '.
' INNER JOIN del_observation dob ON dob.id_observation = doi.id_observation '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' ORDER BY date_observation '.$ordre.', dob.id_observation '.$ordre;
$requeteIdObs .= $this->gestionBdd->getLimitSql();
// Récupérer les N observations triées par date
$observations = $this->bdd->recupererTous($requeteIdObs . ' -- ' . __FILE__ . ':' . __LINE__);
$idsImages = array();
foreach ($observations as $observation) {
$idsImages[] = $observation['id_image'];
}
$chaineIdImages = implode(',', $idsImages);
$requeteImages .= ' WHERE doi.id_image IN ('.$chaineIdImages.') '.
' GROUP BY doi.id_image, doi.id_observation '.
' ORDER BY FIELD(doi.id_image, '.$chaineIdImages.')'.
' LIMIT '.$this->navigation->getLimite(); // On limite sur le nombre car les obs peuvent avoir plusieurs images
} else {
$requeteImages .= ' GROUP BY doi.id_image, doi.id_observation ';
$requeteImages .= ' ORDER BY doi.id_observation DESC';
$requeteImages .= $this->gestionBdd->getLimitSql();
}
$liaisons = $this->bdd->recupererTous($requeteImages . ' -- ' . __FILE__ . ':' . __LINE__);
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
if (!empty($liaisons)) {
$idsObservations = array();
foreach ($liaisons as $image) {
$idObs = $image['id_observation'];
$idsObservations[$idObs] = $idObs;
}
$chaineIdObs = implode(',', $idsObservations);
// On récupère les observations qui complètent la requête précédente
$requeteObservations = ' SELECT * '.
' FROM del_observation dob '.
' LEFT JOIN del_utilisateur du ON dob.ce_utilisateur = du.id_utilisateur '.
' WHERE id_observation IN ('.$chaineIdObs.')';
$resultatsObservations = $this->bdd->recupererTous($requeteObservations . ' -- ' . __FILE__ . ':' . __LINE__);
// FIXME : Ca ne doit pas arriver, mais que se passe-t-il s'il n'y a pas d'observation pour l'image ?!
// On range les observations dans un tableau pour pouvoir les retrouver par leur id :
$observations = array();
foreach ($resultatsObservations as $id => $observation) {
$idObs = $observation['id_observation'];
$observations[$idObs] = $observation;
}
// Enfin, pour chaque image préalablement récupérées, on complète avec les informations de l'observation
// FIXME : peut-être peut-on utiliser un array_merge ici ?
foreach ($liaisons as $id => $liaison) {
$idObs = $liaison['id_observation'];
$observation = $observations[$idObs];
foreach ($observation as $cle => $valeur) {
$liaisons[$id][$cle] = $valeur;
}
}
// On compte à part les images issues de la jointure de del_obs_image et del_image car la fonction
// SQL_CALC_FOUND_ROWS dans la fonction requete image fait passer le temps d'éxécution de 0.0011 à 15s !
$requeteNbImages = 'SELECT SUM(t.nb) as nb FROM (SELECT count(DISTINCT doi.id_image) as nb '.
'FROM del_obs_image doi '.
'INNER JOIN del_image di '.
'ON di.id_image = doi.id_image '.
'GROUP BY doi.id_image, doi.id_observation) t ';
$resultatNbImages = $this->bdd->recupererTous($requeteNbImages . ' -- ' . __FILE__ . ':' . __LINE__);
 
$total = (int) $resultatNbImages[0]['nb'];
$this->navigation->setTotal($total);
}
 
return $liaisons;
}
 
/**
* Charge les liaisons pour une seule image dont l'id est spécifié dans l'URL
* Copie de chargerLiaisonsObs avec critère sur l'id_image uniquement
* Supporte seulement le masque sur referentiel
*/
private function chargerLiaisonsParId() {
 
$idImage = $this->ressources[0];
 
// Récupérer les liaisons
$requeteObs = ' SELECT SQL_CALC_FOUND_ROWS dob.id_observation as id_observation, dob.nom_referentiel, nom_sel, nom_sel_nn, nt, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, '.
' date_observation, dob.mots_cles_texte as mots_cles_texte, dob.commentaire as commentaire, di.mots_cles_texte as mots_cles_texte_image , date_transmission, '.
' doi.id_image as id_image, di.ce_utilisateur as ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, nom_original '.
'FROM del_observation dob '.
' INNER JOIN del_obs_image doi ON dob.id_observation = doi.id_observation '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' LEFT JOIN del_utilisateur du ON dob.ce_utilisateur = du.id_utilisateur '.
' WHERE doi.id_image = '.intval($idImage);
 
if (isset($this->parametres['masque.referentiel'])) {
$requeteObs .= " AND dob.nom_referentiel LIKE '" . $this->parametres['masque.referentiel'] . "%'";
}
 
$observations = $this->bdd->recupererTous($requeteObs . ' -- ' . __FILE__ . ':' . __LINE__);
 
$total = $this->getFoundRows();
$this->navigation->setTotal($total);
 
return $observations;
}
 
/**
* Chargement depuis la bdd de toutes les liaisons entre images et observations
* Méthode appelée uniquement lorsque les paramètres concernent une observation
* */
private function chargerLiaisonsObs() {
// Récupérer les liaisons
$requeteObs = ' SELECT SQL_CALC_FOUND_ROWS dob.id_observation as id_observation, dob.nom_referentiel, nom_sel, nom_sel_nn, nt, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, '.
' date_observation, dob.mots_cles_texte as mots_cles_texte, dob.commentaire as commentaire, di.mots_cles_texte as mots_cles_texte_image , date_transmission, '.
' doi.id_image as id_image, di.ce_utilisateur as ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, nom_original '.
'FROM del_observation dob '.
' INNER JOIN del_obs_image doi ON dob.id_observation = doi.id_observation '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' LEFT JOIN del_utilisateur du ON dob.ce_utilisateur = du.id_utilisateur ';
// Récupérer les conditions sous forme de tableau
$conditionsObs = $this->getConditionsObs();
if (!empty($conditionsObs)) {
$where = ' WHERE '.implode(' AND ', $conditionsObs);
$requeteObs .= $where;
}
// Gérer le tri (uniquement si c'est date_observation)
if (isset($this->parametres['tri']) && $this->parametres['tri'] == 'date_observation') {
$ordre = isset($this->parametres['ordre']) ? $this->parametres['ordre'] : 'DESC';
$tri = ' ORDER BY '.$this->parametres['tri'].' '.$ordre.', doi.id_observation '.$ordre.' ';
$requeteObs .= $tri;
}
$requeteObs .= $this->gestionBdd->getLimitSql();
$observations = $this->bdd->recupererTous($requeteObs . ' -- ' . __FILE__ . ':' . __LINE__);
 
$total = $this->getFoundRows();
$this->navigation->setTotal($total);
 
return $observations;
}
/**
* Chargement depuis la bdd de toutes les liaisons entre images et observations
* Méthode appelée uniquement lorsque les paramètres concernent les images
* */
private function chargerLiaisonsImages() {
// FIXME : si on faisait une requete à part pour compter, ca irait plus vite
// Récupérer tous les ID d'image en fonction des paramètres de recherche
$requeteImages = ' SELECT SQL_CALC_FOUND_ROWS '.
' doi.id_image as id_image, dob.id_observation as id_observation, dob.nom_referentiel, nom_sel, nom_sel_nn, nt, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, '.
' date_observation, dob.mots_cles_texte as mots_cles_texte, dob.commentaire as commentaire, di.mots_cles_texte as mots_cles_texte_image , date_transmission, '.
' di.ce_utilisateur as ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, nom_original '.
' FROM del_obs_image doi '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' INNER JOIN del_observation dob ON dob.id_observation = doi.id_observation '.
' LEFT JOIN del_image_tag dit ON dit.ce_image = di.id_image '.
' LEFT JOIN del_utilisateur du ON du.id_utilisateur = di.ce_utilisateur ';
 
$conditionsImg = $this->getConditionsImages();
 
if (!empty($conditionsImg)) {
$where = ' WHERE ('.implode(' OR ', $conditionsImg).') ';
$where .= ' AND dit.actif = 1 ';
$requeteImages .= $where;
}
// Gérer le tri, sur les votes ou sur les tags
if (isset($this->parametres['tri'])) {
$chaineTri = '';
$chaineOrdre = '';
if ($this->parametres['tri'] == 'votes') {
$protocole = isset($this->parametres['protocole']) ? $this->parametres['protocole'] : 1;
$requeteVotes = ' SELECT doi.id_image as id_image, IF(divo.ce_protocole = '.$protocole.', AVG(divo.valeur), 0) as total_votes '.
' FROM del_obs_image doi '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' INNER JOIN del_observation dob ON dob.id_observation = doi.id_observation '.
' LEFT JOIN del_image_vote divo ON doi.id_image = divo.ce_image '.
' AND ce_protocole = '.$protocole.' ';
// Et si on a cherché par tag ?
if (isset($this->parametres['masque.tag'])) {
$tag = $this->parametres['masque.tag'];
$requeteVotes .= ' LEFT JOIN del_image_tag dit ON dit.ce_image = di.id_image ';
$requeteVotes .= " WHERE (dit.tag_normalise LIKE '$tag%' OR di.mots_cles_texte LIKE '%$tag%') AND dit.actif = 1 ";
}
$requeteVotes .= ' GROUP BY doi.id_image, doi.id_observation '.
' ORDER by total_votes '.$this->directionTri .', doi.id_observation '.$this->directionTri.' '.
$this->gestionBdd->getLimitSql();
 
$resultatsVotes = $this->bdd->recupererTous($requeteVotes . ' -- ' . __FILE__ . ':' . __LINE__);
$tabVotes = array();
foreach ($resultatsVotes as $vote) {
$tabVotes[] = $vote['id_image'];
}
$strVotes = empty($tabVotes) ? "''" : implode(',', $tabVotes);
// Et si on a cherché par tag ?
if (isset($this->parametres['masque.tag'])) {
$chaineTri .= ' AND ';
} else {
$chaineTri .= ' WHERE ';
}
$chaineTri .= ' doi.id_image IN ('.$strVotes.') ';
$chaineOrdre = ' ORDER BY FIELD(doi.id_image, '.$strVotes.') ';
}
if ($this->parametres['tri'] == 'tags') {
$requetetags = ' SELECT SQL_CALC_FOUND_ROWS doi.id_image, COUNT(id_tag) as total_tags '.
' FROM del_obs_image doi LEFT JOIN del_image_tag dit ON dit.ce_image = doi.id_image AND dit.actif = 1 '.
' INNER JOIN del_image di ON doi.id_image = di.id_image '.
' INNER JOIN del_observation dob ON dob.id_observation = doi.id_observation ';
 
if (isset($this->parametres['masque.tag_pictoflora'])) {
// Et si on a cherché par tag ?
$tag = $this->parametres['masque.tag_pictoflora'];
$requetetags .= " WHERE (dit.tag_normalise LIKE '$tag%' OR di.mots_cles_texte LIKE '%$tag%') ";
}
$requetetags .= ' GROUP BY doi.id_image, doi.id_observation '.
' ORDER by total_tags '.$this->directionTri.', doi.id_observation '.$this->directionTri.
$this->gestionBdd->getLimitSql();
 
$resultatstags = $this->bdd->recupererTous($requetetags . ' -- ' . __FILE__ . ':' . __LINE__);
$tabtags = array();
foreach ($resultatstags as $tag) {
$tabtags[] = $tag['id_image'];
}
$strtags = empty($tabtags) ? "''" : implode(',', $tabtags);
// Et si on a cherché par tag ?
if (isset($this->parametres['masque.tag_pictoflora'])) {
$chaineTri .= ' AND ';
} else {
$chaineTri .= ' WHERE ';
}
$chaineTri .= ' doi.id_image IN ('.$strtags.') ';
$chaineOrdre = ' ORDER BY FIELD(doi.id_image, '.$strtags.') ';
}
$requeteImages .= $chaineTri.' GROUP BY doi.id_image, doi.id_observation '.$chaineOrdre;
} else {
$requeteImages .= ' GROUP BY doi.id_image, doi.id_observation'; // des fois, on a plusieurs observations pour la même image ...
$requeteImages .= $this->gestionBdd->getLimitSql();
}
 
$retour = $this->bdd->recupererTous($requeteImages . ' -- ' . __FILE__ . ':' . __LINE__);
$total = $this->getFoundRows();
$this->navigation->setTotal($total);
return $retour;
}
/**
* Chargement depuis la bdd de toutes les liaisons entre images et observations
* */
private function chargerLiaisons() {
$champs = array('dob.id_observation as id_observation', 'nom_sel', 'nom_sel_nn', 'nt', 'famille', 'dob.nom_referentiel', 'ce_zone_geo', 'zone_geo',
'lieudit', 'station', 'milieu', 'date_observation', 'dob.mots_cles_texte as mots_cles_texte', 'dob.commentaire as commentaire',
'di.mots_cles_texte as mots_cles_texte_image ', 'date_transmission', 'di.id_image as id_image', 'di.ce_utilisateur as ce_utilisateur',
'prenom', 'nom', 'courriel', 'dob.prenom_utilisateur', 'dob.nom_utilisateur', 'dob.courriel_utilisateur', 'nom_original');
// Attention le LEFT JOIN est indispensable pour ramener les images n'ayant pas de votes
// en cas de tri par votes
$requeteLiaisons = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS '.implode(', ',$champs).' '.
($this->doitJoindreTableVotes() ?
', IF(dvote.ce_protocole = '.$this->parametres['protocole'].', AVG(dvote.valeur), 0) as total_votes ' :
''
).
($this->doitJoindreTableTags() ?
// attention le DISTINCT est indispensable !
', (COUNT(DISTINCT dtag.id_tag) + '.$this->assemblercomptageOccurencesMotsClesCel().') as total_tags ' :
''
).
'FROM '.$this->gestionBdd->formaterTable('del_obs_image', 'doi').
'INNER JOIN del_image di '.
'ON doi.id_image = di.id_image '.
'INNER JOIN del_observation dob '.
'ON doi.id_observation = dob.id_observation '.
'LEFT JOIN del_utilisateur du '.
'ON du.id_utilisateur = di.ce_utilisateur '.
($this->doitJoindreTableTags() ?
'LEFT JOIN del_image_tag dtag '.
'ON doi.id_image = dtag.ce_image AND dtag.actif = 1 ' :
''
).
($this->doitJoindreTableVotes() ?
'LEFT JOIN del_image_vote dvote '.
'ON doi.id_image = dvote.ce_image AND dvote.ce_protocole = '.$this->parametres['protocole'] :
''
);
$requeteLiaisons .= $this->chargerClauseWhere();
$requeteLiaisons .= $this->getTri();
$requeteLiaisons .= $this->gestionBdd->getLimitSql();
 
$retour = $this->bdd->recupererTous($requeteLiaisons . ' -- ' . __FILE__ . ':' . __LINE__);
$total = $this->getFoundRows();
$this->navigation->setTotal($total);
return $retour;
}
/**
* Retourner un tableau d'images formaté en fonction des liaisons trouvées
* @param $liaisons les liaisons de la table del_obs_images
* */
private function chargerImage($liaisons) {
$images = array();
foreach ($liaisons as $liaison) {
$idImage = $liaison['id_image'];
if($liaison['ce_utilisateur'] == 0) {
$liaison['prenom'] = $liaison['prenom_utilisateur'];
$liaison['nom'] = $liaison['nom_utilisateur'];
}
// On enregistre l'ID de l'image pour n'effectuer qu'une seule requête par la suite
$this->imageIds[] = $idImage;
$index = $liaison['id_image'].'-'.$liaison['id_observation'];
$images[$index] = array('id_image' => $idImage, 'binaire.href' => $this->formaterLienImage($idImage),
'protocoles_votes' => array(),
'mots_cles_texte' => $liaison['mots_cles_texte_image'], 'observation' => $this->formaterObservation($liaison));
}
return $images;
}
/**
* Charger les votes pour chaque image
* */
private function chargerVotes($images) {
$requeteVotes = 'SELECT v.*, p.* FROM '.
$this->gestionBdd->formaterTable('del_image_vote', 'v').
' INNER JOIN del_image_protocole p '.
'ON v.ce_protocole = p.id_protocole '.
$this->chargerClauseWhereVotes();
$resultatsVotes = $this->bdd->recupererTous($requeteVotes . ' -- ' . __FILE__ . ':' . __LINE__);
//TODO : faire une méthode formater vote
$votes = $this->formaterVotes($resultatsVotes);
foreach ($images as $id => $image) {
if (isset($votes[$image['id_image']])) {
$images[$id]['protocoles_votes'] = $votes[$image['id_image']];
}
}
return $images;
}
private function chargerClauseWhereVotes() {
if (sizeof($this->imageIds) > 0) {
$chaineImageIds = implode(',', $this->imageIds);
$where[] = 'v.ce_image IN ('.$chaineImageIds.')';
}
if (isset($this->parametres['protocole'])) {
$where[] = 'v.ce_protocole = '.$this->proteger($this->parametres['protocole']);
}
$where = (!empty($where)) ? 'WHERE '.implode(' AND ', $where) : '';
return $where;
}
/**************************************************************************************
* FONCTION DE CONFIGURATION ET UTILITAIRES *
***************************************************************************************/
/**
* Enregistrer dans les variables de classe les paramètres et ressources
* @param $ressources
* @param $parametres de recherche
* */
private function initialiserRessourcesEtParametres($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingObservation = $this->conteneur->getParametre('mapping_observation');
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
$tableauImages = $this->conteneur->getParametre('images');
if (empty($tableauImages)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [images] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
if ($this->conteneur->getParametre('url_images') == null) {
$erreurs[] = '- paramètre "url_images" manquant ;';
}
}
if (empty($this->mappingObservation)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_observation] ou celui-ci est vide ;';
} else {
$champsMappingObs = array('id_observation', 'date_observation', 'date_transmission', 'famille', 'nom_sel', 'nom_sel_nn', 'nt',
'ce_zone_geo', 'lieudit', 'station', 'milieu', 'ce_utilisateur', 'nom', 'prenom');
foreach ($champsMappingObs as $champ) {
if (!isset($this->mappingObservation[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
if (empty($this->mappingFiltre)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_masque] ou celui-ci est vide ;';
} else {
$champsMappingFiltre = array('famille', 'ns', 'nn', 'date', 'tag', 'commune');
foreach ($champsMappingFiltre as $champ) {
if (!isset($this->mappingFiltre[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour l\'observation est manquant ;';
}
}
}
$tris_possibles = $this->conteneur->getParametre('tris_possibles');
if (empty($tris_possibles)) {
$erreurs[] = '- le fichier de configuration ne contient pas le parametre tris_possibles ou celui-ci est vide ;';
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Verifier que les paramètres de tri sont bien autorisés et qu'ils sont au bon format.
*/
private function verifierParametresTri() {
$erreurs = array();
$tris_possibles = $this->conteneur->getParametre('tris_possibles');
$tris_possibles_tableau = explode(',', $tris_possibles);
if(isset($this->parametres['tri']) && !in_array($this->parametres['tri'], $tris_possibles_tableau)) {
$erreurs[] = '- le type de tri demandé est incorrect, les valeurs possibles sont '.$tris_possibles.' ;';
}
if(isset($this->parametres['tri']) && $this->parametres['tri'] == "votes") {
if(!isset($this->parametres['protocole']) || !is_numeric($this->parametres['protocole'])) {
$erreurs[] = '- Le paramètre protocole est obligatoire en cas de tri par vote et doit être un entier ;';
}
}
$directions_tri = array('asc', 'desc');
if(isset($this->parametres['ordre']) && !in_array($this->parametres['ordre'], $directions_tri)) {
$erreurs[] = '- la direction du tri demandé est incorrecte, les valeurs supportées sont asc ou desc ;';
}
if (!empty($erreurs)) {
$e = 'Erreur lors de l\'analyse des parametres du tri : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
private function verifierParametreFormatRetour() {
$erreurs = array();
$formats_possibles_str = $this->conteneur->getParametre('formats_possibles');
$formats_possibles = explode(',',$formats_possibles_str);
 
if(isset($this->parametres['format']) && !in_array($this->parametres['format'], $formats_possibles)) {
$erreurs[] = "- le format d'image demandé n'est pas supporté ; Les format supportés sont : ".$formats_possibles_str;
}
if (!empty($erreurs)) {
$e = 'Erreur lors de l\'analyse du format de retour demandé : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
private function initialiserFormatRetour() {
$this->formatRetour = isset($this->parametres['format']) ? $this->parametres['format'] : $this->formatRetour;
}
 
/**
* Initialiser les variables de tri depuis les paramètres
* */
private function initialiserTri() {
$this->tri = isset($this->parametres['tri']) ? $this->parametres['tri'] : $this->tri;
$this->directionTri = isset($this->parametres['ordre']) ? $this->parametres['ordre'] : $this->directionTri;
}
/** Pour eviter les requêtes trop gourmandes, on supprime les caractères passe-partout
* @param les paramètres de l'application
* */
public function nettoyerParametres($parametres) {
$parametresRetour = array();
foreach ($parametres as $cle => $valeur) {
$valSansPourcent = trim($valeur, "% ");
if ($valSansPourcent != '') {
$parametresRetour[$cle] = $valeur;
}
}
return $parametresRetour;
}
/**
* Nettoyer les jokers
* @param la valeur du masque
* */
private function remplacerParJokerCaractere($valeurMasque) {
return str_replace(array('-',' '), '_', $valeurMasque);
}
 
//TODO: déplacer les fonctions ci dessus et dessous dans une classe
// utilitaire
/**
* Supprimer les accents des chaines de caractères
* */
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;
}
/**
* Normaliser en supprimant les accents et en mettant en minuscule
* @param $mot_cle le mot recherché
* */
private function normaliserMotCle($mot_cle) {
return mb_strtolower($this->supprimerAccents(trim($mot_cle)));
}
/**
* Récupérer le numéro du département d'un fichier de configuration
* */
private function obtenirIdDepartement($nomDpt) {
$nomDpt = $this->supprimerAccents($nomDpt);
$nomDpt = strtolower(str_replace(' ','-',$nomDpt));
$idDpt = $this->conteneur->getParametre($nomDpt);
if($idDpt == null || $idDpt == ' ') {
$idDpt = ' ';
}
return $idDpt;
}
/**
* Obtenir le type de requête à exécuter en fonction des paramètres de recherche
* @param $parametres les paramètres de l'application
* */
private function getTypeRequete($ressources, $parametres) {
 
// une image par id
if ((count($ressources) == 1) && is_numeric($ressources[0])) {
return 'id';
}
 
$typeRequete = 'simple';
// Dans ce cas précis, les informations concernant le depart, la limite ou l'ordre ne
// rentre pas en compte dans le type de requête ; ce ne sont que des compléments.
unset($parametres['navigation.depart']);
unset($parametres['navigation.limite']);
unset($parametres['ordre']);
// En revanche, chaque masque est associé à un type de requête particulier.
$masquesObservation = array('masque', 'masque.departement', 'masque.ns', 'masque.genre', 'masque.date', 'masque.commune', 'masque.famille', 'masque.auteur', 'masque.nn', 'masque.referentiel', 'masque.tag_cel');
$masquesImage = array('masque', 'masque.tag_pictoflora');
// Le type de requête est défini par les tables qu'il doit inclure (observation, image, ou les deux)
$requeteSimple = false;
$pourObservation = false;
$pourImage = false;
// S'il n'y a aucun paramètre, on lance une requête simple
if (empty($parametres)) {
$requeteSimple = true;
}
// Si l'un des masques demandé concerne l'observation
foreach ($masquesObservation as $masque) {
if (isset($parametres[$masque])) {
$pourObservation = true;
break;
}
}
// Si l'un des masques demandé concerne les images
foreach ($masquesImage as $masque) {
if (isset($parametres[$masque])) {
$pourImage = true;
break;
}
}
// Selon les tri
if (isset($parametres['tri'])) {
switch ($parametres['tri']) {
case 'votes' :
case 'tags' :
$pourImage = true;
break;
default : //case 'date_observation' :
if (sizeof($parametres) > 1) {
$pourObservation = true;
}
}
}
// Vérifier la combinaison des booléens pour en déduire le type de requête
if ($pourObservation && $pourImage) {
$typeRequete = 'obs-images';
} else {
if ($pourImage) {
$typeRequete = 'images';
} else if ($pourObservation) {
$typeRequete = 'obs';
} else { // if ($requeteSimple)
$typeRequete = 'simple';
}
}
return $typeRequete;
}
private function doitJoindreTableVotes() {
return ($this->tri == 'votes');
}
private function doitJoindreTableTags() {
return ($this->tri == 'tags');
}
 
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater une observation depuis une ligne liaison
* @param $liaison liaison issue de la recherche
* @return $observation l'observation mise en forme
* */
private function formaterObservation($liaison) {
$observation = array();
foreach ($this->mappingObservation as $nomOriginal => $nomFinal) {
$observation[$nomFinal] = $liaison[$nomOriginal];
}
 
return $observation;
}
/**
* Formater une observation depuis une ligne liaison
* @param $liaison liaison issue de la recherche
* @return $observation l'observation mise en forme
* */
private function formaterVotes($votes) {
$retour = array();
foreach ($votes as $vote) {
$retour_vote = array();
foreach ($vote as $param=>$valeur) {
if (strpos($this->mappingVotes[$param], 'protocole.') === 0) {
$retour_protocole[$this->mappingVotes[$param]] = $valeur;
} else {
$retour_vote[$this->mappingVotes[$param]] = $valeur;
}
}
if (!isset($retour[$vote['ce_image']][$vote['ce_protocole']])) {
$retour[$vote['ce_image']][$vote['ce_protocole']] = $retour_protocole;
}
$retour[$vote['ce_image']][$vote['ce_protocole']]['votes'][$vote['id_vote']] = $retour_vote;
}
return $retour;
}
/**
* Formater le lien de l'image en fonction du fichier de config et de l'identifiant de l'image
* */
private function formaterLienImage($idImage) {
$idImage = sprintf('%09s', $idImage);
$url = $this->conteneur->getParametre('url_images');
$urlImage = sprintf($url, $idImage, $this->formatRetour);
return $urlImage;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/images/VotesImage.php
New file
0,0 → 1,332
<?php
// declare(encoding='UTF-8');
/**
* Le web service image récupère toutes les données de la table del_obs_images
* pour retourner une liste d'images associée à une observation
*
* @category php 5.2
* @package del
* @subpackage images
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
*/
 
class VotesImage {
private $imageIds = array();
private $conteneur;
private $navigation;
private $masque;
protected $gestionBdd;
protected $bdd;
private $ressources;
private $parametres;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_votes.ini');
$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
$this->navigation = $conteneur->getNavigation();
$this->masque = $conteneur->getMasque();
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
/**
* Méthode principale de la classe.
* Lance la récupération des images dans la base et les place dans un objet ResultatService
* pour l'afficher.
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
* @param array $parametres les paramètres situés après le ? dans l'url
* */
public function consulter($ressources, $parametres) {
// Gestion des configuration du script
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->configurer();
$this->verifierConfiguration();
// Lancement du service
$votes = $this->chargerVotes();
$total = $this->compterVotes();
$this->navigation->setTotal($total);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->conteneur->getEntete(), 'resultats' => $votes);
return $resultat;
}
public function ajouter($ressources, $parametres) {
$this->verifierParametresAjoutModif($ressources, $parametres);
$insertion = 'INSERT INTO del_image_vote '.
'(ce_image, ce_protocole, ce_utilisateur, valeur, date) '.
'VALUES ('.
$this->proteger($ressources[0]).','.
$this->proteger($parametres['protocole']).','.
$this->proteger($parametres['utilisateur']).','.
$this->proteger($parametres['valeur']).', '.
'NOW()'.
');';
 
$resultat = $this->bdd->requeter($insertion);
if ($resultat == false) {
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
} else {
$retour = $this->bdd->recupererIdDernierAjout();
$resultat = new ResultatService();
$resultat->corps = array('id_vote' => $retour);
self::updateStats($this->bdd, $ressources[0],$parametres['protocole']);
 
return $resultat;
}
}
public function modifier($ressources, $parametres) {
$this->verifierParametresAjoutModif($ressources, $parametres);
//TODO: est il nécessaire de tester si ça existe et renoyer une erreur 404
// si l'on vote ? Cela peut ralentir considérablement les choses
// une contrainte d'unicité sur le triplet image, protocole, vote existe,
// ça suffira pour provoquer une erreur 500
$modification = 'UPDATE del_image_vote '.
'SET valeur = '.$this->proteger($parametres['valeur']).', '.
' date = NOW() '.
'WHERE '.
'ce_image = '.$this->proteger($ressources[0]).' AND '.
'ce_protocole = '.$this->proteger($parametres['protocole']).' AND '.
'ce_utilisateur = '.$this->proteger($parametres['utilisateur']).' ';
$resultat = $this->bdd->requeter($modification);
if ($resultat == false) {
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
} else {
self::updateStats($this->bdd, $ressources[0],$parametres['protocole']);
RestServeur::envoyerEnteteStatutHttp(RestServeur::HTTP_CODE_OK);
}
}
public function supprimer($ressources) {
$id_image = $ressources[0];
$id_vote = $ressources[2];
$id_vote_p = $this->proteger($id_vote);
 
$requete_infos_vote = 'SELECT * FROM del_image_vote '.
'WHERE id_vote = '.$id_vote_p;
$infos_vote = $this->bdd->recuperer($requete_infos_vote);
if ($infos_vote == false) {
throw new Exception("Aucun vote ne correspond à cet identifiant", RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
$controle_acces = new ControleAcces($this->conteneur);
$utilisateur = $controle_acces->getInfosUtilisateurConnecte();
if($utilisateur['id_utilisateur'] != $infos_vote['ce_utilisateur'] &&
$controle_acces->getIdAnonymeTemporaire() != $infos_vote['ce_utilisateur']) {
throw new Exception("Vous n'êtes pas autorisé à supprimer ce vote",
RestServeur::HTTP_CODE_ACCES_NON_AUTORISE);
}
$suppression = 'DELETE FROM del_image_vote '.
'WHERE id_vote = '.$id_vote_p;
$resultat = $this->bdd->requeter($suppression);
if ($resultat == false) {
throw new Exception("Impossible de supprimer le vote", RestServeur::HTTP_CODE_ERREUR);
} else {
self::updateStats($this->bdd, $ressources[0],$infos_vote['ce_protocole']);
}
}
static function updateStats($db, $id_image, $id_proto) {
$id_image = intval($id_image);
$id_proto = intval($id_proto);
if(!$id_image || !$id_proto) throw new Exception("Ne peut mettre à jour les statistiques de vote",
RestServeur::HTTP_CODE_ERREUR);
/* REPLACE ... SELECT: réinitalise les champs non-défini (MySQL 5.1)
REPLACE ... SET a=b, c=d: idem ou plusieurs requête
Du coup il faut récupérer les données actuelles mais REPLACE ne le permet pas
(http://dev.mysql.com/doc/refman/5.5/en/replace.html :
You cannot refer to values from the current row and use them in the new row.)
D'où INSERT ... ON DUPLICATE KEY UPDATE. Notons que VALUES() récupère la valeur
*associée* au champ passé en paramètre, c'est à dire VALUES(moyenne) == divo.moyenne */
$db->requeter(sprintf('INSERT INTO del_image_stat (ce_image, ce_protocole, moyenne, nb_votes)'.
' SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes'.
' FROM del_image_vote divo'.
' WHERE ce_image = %d AND ce_protocole = %d GROUP BY ce_image, ce_protocole'.
' ON DUPLICATE KEY UPDATE moyenne = VALUES(moyenne), nb_votes = VALUES(nb_votes)',
$id_image, $id_proto));
 
}
 
/*-------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
--------------------------------------------------------------------------------*/
/**
* Configuration du service en fonction du fichier de config config_del.ini
* */
public function configurer() {
$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
}
/**
* Vérifier que le service est bien configuré
* */
public function verifierConfiguration() {
$erreurs = array();
$tableauImages = $this->conteneur->getParametre('images');
if (empty($tableauImages)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [images] ou celui-ci est vide ;';
} else {
if ($this->conteneur->getParametre('url_service') == null) {
$erreurs[] = '- paramètre "url_service" manquant ;';
}
}
 
if (empty($this->mappingVotes)) {
$erreurs[] = '- le fichier de configuration ne contient pas le tableau [mapping_votes] ou celui-ci est vide ;';
} else {
$champsMappingObs = array('ce_protocole','id_vote','valeur','ce_image', 'ce_utilisateur', 'nom', 'prenom');
foreach ($champsMappingObs as $champ) {
if (!isset($this->mappingVotes[$champ])) {
$erreurs[] = '- le mapping du champ "'.$champ.'" pour le vote est manquant ;';
}
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
public function verifierParametresAjoutModif($ressources, $parametres) {
$erreurs = array();
if (!is_numeric($ressources[0])) {
$erreurs[] = '- le paramètre indiquant l\'identifiant de l\'image doit être numérique ;';
}
if (!isset($parametres['utilisateur'])) {
$erreurs[] = '- paramètre "utilisateur" manquant ;';
}
if (!isset($parametres['protocole'])) {
$erreurs[] = '- paramètre "id_protocole" manquant ;';
} else {
if (!is_numeric($parametres['protocole'])) {
$erreurs[] = '- le paramètre "protocole" doit être numérique ;';
}
}
if (!isset($parametres['valeur'])) {
$erreurs[] = '- paramètre "valeur" manquant ;';
} else {
if (!is_numeric($parametres['valeur'])) {
$erreurs[] = '- le paramètre "valeur" doit être numérique ;';
}
}
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
public function verifierParametresSuppression($ressources, $parametres) {
$erreurs = array();
if (!is_numeric($ressources[0])) {
$erreurs[] = '- le paramètre indiquant l\'identifiant de l\'image doit être numérique ;';
}
/*if (!is_numeric($parametres['id_protocole'])) {
$erreurs[] = '- le paramètre indiquant l\'identifiant du vote doit être numérique ;';
}*/
if (!empty($erreurs)) {
$e = 'Erreur lors de la configuration : '."\n";
$e .= implode("\n", $erreurs);
throw new Exception($e, RestServeur::HTTP_CODE_ERREUR);
}
}
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where[] = 'WHERE ce_image = '.$this->proteger($this->ressources[0]);
if (isset($this->parametres['protocole'])) {
$where[] = 'ce_protocole = '.$this->proteger($this->parametres['protocole']);
}
return implode(' AND ', $where);
}
/*-------------------------------------------------------------------------------
CHARGEMENT DES IMAGES
--------------------------------------------------------------------------------*/
/**
* Charger les votes pour chaque image
* */
private function chargerVotes() {
$requeteVotes = 'SELECT * FROM '.
$this->gestionBdd->formaterTable('del_image_vote').
$this->chargerClauseWhere();
$resultatsVotes = $this->bdd->recupererTous($requeteVotes);
$votes = $this->formaterVotes($resultatsVotes);
return $votes;
}
/**
* Compter le nombre total d'images dans la base pour affichage dans entete.
* */
private function compterVotes() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
 
/*-------------------------------------------------------------------------------
FORMATER ET METTRE EN FORME
--------------------------------------------------------------------------------*/
/**
* Formater une observation depuis une ligne liaison
* @param $liaison liaison issue de la recherche
* @return $observation l'observation mise en forme
* */
private function formaterVotes($votes) {
$retour = array();
foreach ($votes as $vote) {
foreach ($vote as $p=>$valeur) {
$retour[$vote['id_vote']][$this->mappingVotes[$p]] = $valeur;
}
}
return $retour;
}
private function proteger($valeur) {
if (is_array($valeur)) {
return $this->bdd->protegerTableau($valeur);
} else {
return $this->bdd->proteger($valeur);
}
}
}
?>
/branches/v1.1-helium/services/modules/0.1/images/ListeImages.php
New file
0,0 → 1,649
<?php
/**
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
* @see http://www.tela-botanica.org/wikini/identiplante/wakka.php?wiki=IdentiPlante_PictoFlora_MoteurRecherche
*
* Backend pour PictoFlora (del.html#page_recherche_images)
*
*
* == Notes ==
*
* tri=votes et tri=tags: affectent le choix des images affichées (donc getIdImages())
* Cependant ce total ne nous intéresse même pas (MoyenneVotePresenteur.java s'en occupe).
* Seul tri=date_transmission nous évite l'AVG() + GROUP BY
*
* protocole: il affecte l'affichage des information, mais le JSON contient déjà
* l'intégralité (chercher les données de vote pour 1 ou plusieurs protocoles) est quasi-identique.
* Par contre, le tri par moyenne des votes, sous-entend "pour un protocole donné".
* Dès lors le choix d'un protocole doit avoir été fait afin de régler le JOIN et ainsi l'ORDER BY.
* (cf requestFilterParams())
*
* Histoire: auparavant (pré-r142x) un AVG + GROUP BY étaient utilisés pour générer on-the-fly les valeurs
* utilisées ensuite pour l'ORDER BY. La situation à base de del_image_stat
* est déjà bien meilleure sans être pour autant optimale. cf commentaire de sqlAddConstraint()
*
*
* Tags:
* Le comportement habituel dans le masque *général*: les mots sont séparés par des espaces,
* implod()ed par des AND (tous les mots doivent matcher).
* Et le test effectué doit matcher sur:
* %(les tags d'observations)% *OU* %(les tags d'images)% *OU* %(les tags publics)%
*
* Le comportement habituel dans le masque *tag*: les mots ne sont *pas* splittés (1 seule expression),
* Et le test effectué doit matcher sur:
* ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
*
* Par défaut les tags sont comma-separated (OU logique).
* Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
* des tags séparés par des espaces recherche
*
* TODO:
* -affiner la gestion de passage de mots-clefs dans le masque général.
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
* (http://www.mysqlperformanceblog.com/2007/04/06/using-delayed-join-to-optimize-count-and-limit-queries/)
* - éviter de dépendre d'une jointure systématique sur `cel_obs`, uniquement pour `(date_)transmission
* (cf VIEW del_image)
* - poursuivre la réorganisation des méthodes statiques parmis Observation, ListeObservations et ListeImages2
* - *peut-être*: passer requestFilterParams() en méthode de classe
*
*
* MySQL sux:
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
* MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT 3);
* PRIMARY
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT MIN(3));
* DEPENDENT SUBQUERY ... ... ... mwarf !
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1);
* 5.5: MATERIALIZED del_image_tag ALL ce_image NULL NULL NULL 38276 Using where
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
*
*/
 
require_once(dirname(__FILE__) . '/../DelTk.php');
require_once(dirname(__FILE__) . '/../observations/Observation.php');
restore_error_handler();
restore_exception_handler();
error_reporting(E_ALL);
 
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&masque=plop
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
 
class ListeImages {
 
// TODO: PHP-x.y, ces variables devrait être des "const"
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
 
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags');
 
// en plus de ceux dans DelTk
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
 
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
'tri' => 'date_transmission', 'ordre' => 'desc',
// spécifiques à PictoFlora:
'format' => 'XL');
 
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes))
 
static $mappings = array(
'observations' => array( // v_del_image
"id_observation" => 1,
"date_observation" => 1,
"date_transmission" => 1,
"famille" => "determination.famille",
"nom_sel" => "determination.ns",
"nom_sel_nn" => "determination.nn",
"nom_referentiel" => "determination.referentiel",
"nt" => "determination.nt",
"ce_zone_geo" => "id_zone_geo",
"zone_geo" => 1,
"lieudit" => 1,
"station" => 1,
"milieu" => 1,
"mots_cles_texte" => "mots_cles_texte",
"commentaire" => 1,
"ce_utilisateur" => "auteur.id",
"nom_utilisateur" => "auteur.nom",
"prenom_utilisateur" => "auteur.prenom",
),
'images' => array( // v_del_image
'id_image' => 1,
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
'i_mots_cles_texte' => 1
));
 
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_images.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
 
public function consulter($ressources, $parametres) {
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur.
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
// pour les votes, les mappings de "Observation" nous suffisent
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
 
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité
// de `ce_image` dans ces tables jointes.
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array());
 
 
$db = $this->bdd;
 
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
// même si ceux-ci sont improbables (pas d'<input> pour cela).
$params_ip = DelTk::requestFilterParams($parametres,
array_diff(DelTk::$parametres_autorises,
array('masque.type')),
$this->conteneur);
 
// notre propre filtrage sur l'INPUT
$params_pf = self::requestFilterParams($parametres,
array_merge(DelTk::$parametres_autorises,
self::$parametres_autorises));
 
/* filtrage des tags + sémantique des valeurs multiples:
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
if(!isset($parametres['masque.tag_pictoflora']) && isset($parametres['masque.tag'])) {
$parametres['masque.tag_pictoflora'] = $parametres['masque.tag'];
}
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
 
$params = array_merge(
DelTk::$default_params, // paramètre par défaut Identiplante
self::$default_params, // paramètres par défaut PictoFlora
$params_ip, // les paramètres passés, traités par Identiplante
$params_pf); // les paramètres passés, traités par PictoFlora
 
if(isset($parametres['format'])) {
$params['format'] = $parametres['format'];
}
 
// création des contraintes (génériques de DelTk)
DelTk::sqlAddConstraint($params, $db, $req);
// création des contraintes spécifiques (sur les tags essentiellement)
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
// création des contraintes spécifiques impliquées par le masque général
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
// l'ORDER BY s'avére complexe
self::sqlOrderBy($params, $db, $req);
 
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db);
$idobs_tab = self::getIdImages($params, $req, $db);
 
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
if(!$idobs_tab) {
$resultat = new ResultatService();
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
'resultats' => array());
return $resultat;
/*
header('HTTP/1.0 404 Not Found');
// don't die (phpunit)
throw(new Exception()); */
}
 
 
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
 
$liaisons = self::chargerImages($db, $idobs);
/*
// Q&D
$images = array();
$o = new Observation($this->conteneur);
foreach($idobs as $i) {
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
}
*/
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
$liaisons,
$this->conteneur->getParametre('images.url_images'),
$params['format']);
 
// on charge les votes pour ces images et pour *tous* les protocoles
$votes = Observation::chargerVotesImage($db, $liaisons, NULL);
 
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation.
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation)
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque
// c'est encore possible.
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
 
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
$resultat = new ResultatService();
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
'resultats' => $images);
return $resultat;
}
 
/**
* TODO: partie spécifique liées à la complexité de PictoFlora:
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req)
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
*/
static function sqlOrderBy($p, $db, &$req) {
// parmi self::$tri_possible
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
return;
}
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
return;
}
 
if($p['tri'] == 'date_observation') {
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
return;
}
 
// tri == 'date_transmission'
// avant cel:r1860, date_transmission pouvait être NULL
// or nous voulons de la consistence (notamment pour phpunit)
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
}
 
/*
* in $p: un tableau de paramètres, dont:
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
*
* in/ou: $req: un tableau de structure de requête MySQL
*
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
* Soit directement $this->consulter() si des masque.tag* sont passés
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
* Soit via sqlAddMasqueConstraint():
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
* équivalent à:
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
*
*/
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
// TODO implement dans DelTk ?
if(!empty($p['masque.milieu'])) {
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
}
 
 
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
celui-ci indique sur quels votes porte l'AVG.
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
/* TODO: perf problème:
1) SQL_CALC_FOUND_ROWS: fixable en:
- dissociant le comptage de la récup d'id + javascript async
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
(paramètre booléen "with-total" par exemple)
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
(cel_images/cel_obs_images/cel_obs/del_image_stat)
Cependant c'est à l'optimiseur de définir son ordre préféré. */
if($p['tri'] == 'votes') {
// $p['protocole'] *est* défini (cf requestFilterParams())
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
if($p['ordre'] == 'desc') {
// pas de group by nécessaire pour cette jointure
// PRIMARY KEY (`ce_image`, `ce_protocole`)
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
' ON vdi.id_image = dis.ce_image'.
' AND dis.ce_protocole = %d',
$p['protocole']);
} else {
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
' ON vdi.id_image = dis.ce_image'.
' AND dis.ce_protocole = %d',
$p['protocole']);
// nécessaire (dup ce_image dans del_image_stat)
$req['groupby'][] = 'vdi.id_observation';
}
}
 
if($p['tri'] == 'tags') {
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
// nécessaire (dup ce_image dans del_image_stat)
$req['groupby'][] = 'vdi.id_observation';
}
 
// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
// sont toujours présentes; bien que parfois NULL.
if($p['masque.tag_cel']) {
if(isset($p['masque.tag_cel']['AND'])) {
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
// et auquel cas laisser au client le choix du couteux "%" ?
$tags = $p['masque.tag_cel']['AND'];
array_walk($tags, create_function('&$val, $k, $db',
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
$db->proteger("%".$val."%"));'),
$db);
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
}
else {
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
}
}
 
if($p['masque.tag_pictoflora']) {
// inutilisable pour l'instant
// self::sqlAddPictoFloraTagConstraint1($p, $db, $req);
 
// intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery)
// self::sqlAddPictoFloraTagConstraint2($p, $db, $req);
 
// approche fiable mais sous-optimale
self::sqlAddPictoFloraTagConstraint3($p, $db, $req);
}
}
 
/* approche intéressante si les deux problèmes suivants peuvent être résolu:
- LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
- dans le cas d'un ET logique, comment chercher les observations correspondantes ? */
static function sqlAddPictoFloraTagConstraint1($p, $db, &$req) {
// XXX: utiliser tag plutôt que tag_normalise ?
$req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
$req['where'][] = 'dit.actif = 1';
$req['groupby'][] = 'vdi.id_image'; // TODO: nécessaire (car dup') mais risque de conflict en cas de tri (multiple GROUP BY)
// XXX: en cas de ET, possibilité du GROUP_CONCAT(), mais probablement sans grand intérêt, d'où une boucle
if(isset($p['masque.tag_pictoflora']['AND'])) {
// TODO/XXX : comment matcher les observations ayant tous les mots-clef passés ?
// ... le LEFT-JOIN n'y semble pas adapté
}
else {
$protected_tags = array();
foreach($p['masque.tag_pictoflora']['OR'] as $tag) $protected_tags[] = $db->proteger(strtolower($tag));
$req['where'][] = sprintf('tag_normalise IN (%s)', implode(',', $protected_tags));
}
}
 
// inutilisé pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro)
static function sqlAddPictoFloraTagConstraint2($p, $db, &$req) {
// Note à propos des 4 "@ instruction" ci-dessous (notamment sur recupererTous())
// REGEXP permet un puissant mécanisme de sélection des obs/image à qui sait
// l'utiliser, mais peut sortir une erreur en cas de REGEXP invalide
// ex: REGEX "^(".
// Pour l'heure nous ignorons ce type d'erreur car aucun de nos champ de recherche
// ne peuvent (ou ne devrait) comporter des meta-caractères
// ([])?*+\\
if(isset($p['masque.tag_pictoflora']['AND'])) {
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($p['masque.tag_pictoflora']['AND']);
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
}
else {
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
}
}
 
// si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction
static function sqlAddPictoFloraTagConstraint3($p, $db, &$req) {
if(isset($p['masque.tag_pictoflora']['AND'])) {
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($p['masque.tag_pictoflora']['AND']);
 
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ...
$ids = @$db->recupererTous(sprintf(
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s",
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND']))));
 
// puis:
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false';
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids);
 
}
else {
$ids = @$db->recupererTous(sprintf(
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s",
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR']))));
 
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false';
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids);
}
}
 
static function getIdImages($p, $req, $db) {
return $db->recupererTous(sprintf(
'SELECT SQL_CALC_FOUND_ROWS id_image' .
' FROM v_del_image vdi'.
' %s' . // LEFT JOIN if any
' WHERE %s'. // where-clause ou TRUE
' %s'. // group-by
' ORDER BY %s'.
' LIMIT %d, %d -- %s',
$req['join'] ? implode(' ', array_unique($req['join'])) : '',
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
$req['orderby'],
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
}
 
static function chargerImages($db, $idImg) {
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
return $db->recupererTous(sprintf('SELECT '.
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
' %1$s, %2$s FROM v_del_image '.
' WHERE %3$s'.
' -- %4$s',
$obs_fields, $image_fields,
sprintf('id_image IN (%s)', implode(',', $idImg)),
__FILE__ . ':' . __LINE__));
 
}
 
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
de manière identique à la seule différence que:
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
Tous les mots-clefs doivent matcher et sont séparés par des espaces
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
if(!empty($p['masque'])) {
$or_params = array('masque.auteur' => $p['masque'],
'masque.departement' => $p['masque'],
'masque.commune' => $p['masque'], // TODO/XXX ?
'masque.id_zone_geo' => $p['masque'],
 
/* tous-deux remplacent masque.tag
mais sont traité séparément des requestFilterParams() */
// 'masque.tag_cel' => $p['masque'],
// 'masque.tag_pictoflora' => $p['masque'],
 
'masque.ns' => $p['masque'],
'masque.famille' => $p['masque'],
'masque.date' => $p['masque'],
'masque.genre' => $p['masque'],
'masque.milieu' => $p['masque'],
'masque.tag_cel' => $p['masque'],
'masque.tag_pictoflora' => $p['masque'],
 
// tri est aussi nécessaire car affecte les contraintes de JOIN
'tri' => $p['tri'],
'ordre' => $p['ordre']);
 
/* Cependant les champs spécifiques ont priorité sur le masque général.
Pour cette raison nous supprimons la génération de SQL du masque général sur les
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
if(isset($p['masque.date'])) unset($or_params['masque.date']);
if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
 
$or_masque = array_merge(
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
self::requestFilterParams($or_params));
 
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
postulés comme séparés par des espaces, et doivent être tous matchés. */
if(isset($or_params['masque.tag_cel']))
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
if(isset($or_params['masque.tag_pictoflora']))
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
 
 
// pas de select, groupby & co ici: uniquement 'join' et 'where'
$or_req = array('join' => array(), 'where' => array());
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
self::sqlAddConstraint($or_masque, $db, $or_req);
 
if($or_req['where']) {
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
// utile au cas ou des jointures seraient rajoutées
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
}
}
}
 
 
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
// XXX: cf Observation.php::consulter(), nous pourriouns ici
// conserver les valeurs vides (pour les phptests notamment, ou non)
// $obs = array_map('array_filter', $obs);
$obs_merged = $obs_keyed_by_id_image = array();
foreach($obs as $o) {
// ceci nous complique la tâche pour le reste du processing...
$id = $o['jsonindex'];
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
// mais tout deux partage leur référence à "protocole"
$image = array(
'id_image' => $o['id_image'],
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
);
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
$obs_merged[$id]['observation'] = $o;
$obs_merged[$id]['protocoles_votes'] = array();
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
}
 
return array($obs_merged,$obs_keyed_by_id_image);
}
 
 
 
// complete & override DelTk::requestFilterParams() (même usage)
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
if($parametres_autorises) { // filtrage de toute clef inconnue
$params = array_intersect_key($params, array_flip($parametres_autorises));
}
 
$p = array();
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
 
// "milieu" inutile pour IdentiPlantes ?
if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
 
// compatibilité
if(isset($params['masque.tag'])) {
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
}
 
if($p['tri'] == 'votes' || $p['tri'] == 'tags') {
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
if(!isset($params['protocole']) || !is_numeric($params['protocole']))
$p['protocole'] = self::$default_proto;
else
$p['protocole'] = intval($params['protocole']);
}
 
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
}
 
 
 
// met à jour *toutes* les stats de nombre de tags et de moyenne des votes
static function _update_statistics($db) {
$db->requeter("TRUNCATE TABLE del_image_stat");
$db->requeter(<<<EOF
INSERT INTO `del_image_stat` (
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags
FROM `tb_cel`.`cel_images` ci
LEFT JOIN
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes FROM del_image_vote
GROUP BY ce_image, ce_protocole ) AS divo
ON ci.id_image = divo.ce_image
LEFT JOIN
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag
GROUP BY ce_image ) AS dit
ON ci.id_image = dit.ce_image )
EOF
);
}
 
static function revOrderBy($orderby) {
return $orderby == 'asc' ? 'desc' : 'asc';
}
}
/branches/v1.1-helium/services/modules/0.1/images/ListeImages2.php
New file
0,0 → 1,643
<?php
/**
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
* @see http://www.tela-botanica.org/wikini/identiplante/wakka.php?wiki=IdentiPlante_PictoFlora_MoteurRecherche
*
* Backend pour PictoFlora (del.html#page_recherche_images)
*
*
* == Notes ==
*
* tri=votes et tri=tags: affectent le choix des images affichées (donc getIdImages())
* Cependant ce total ne nous intéresse même pas (MoyenneVotePresenteur.java s'en occupe).
* Seul tri=date_transmission nous évite l'AVG() + GROUP BY
*
* protocole: il affecte l'affichage des information, mais le JSON contient déjà
* l'intégralité (chercher les données de vote pour 1 ou plusieurs protocoles) est quasi-identique.
* Par contre, le tri par moyenne des votes, sous-entend "pour un protocole donné".
* Dès lors le choix d'un protocole doit avoir été fait afin de régler le JOIN et ainsi l'ORDER BY.
* (cf requestFilterParams())
*
* Histoire: auparavant (pré-r142x) un AVG + GROUP BY étaient utilisés pour générer on-the-fly les valeurs
* utilisées ensuite pour l'ORDER BY. La situation à base de del_image_stat
* est déjà bien meilleure sans être pour autant optimale. cf commentaire de sqlAddConstraint()
*
*
* Tags:
* Le comportement habituel dans le masque *général*: les mots sont séparés par des espaces,
* implod()ed par des AND (tous les mots doivent matcher).
* Et le test effectué doit matcher sur:
* %(les tags d'observations)% *OU* %(les tags d'images)% *OU* %(les tags publics)%
*
* Le comportement habituel dans le masque *tag*: les mots ne sont *pas* splittés (1 seule expression),
* Et le test effectué doit matcher sur:
* ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
*
* Par défaut les tags sont comma-separated (OU logique).
* Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
* des tags séparés par des espaces recherche
*
* TODO:
* -affiner la gestion de passage de mots-clefs dans le masque général.
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
* (http://www.mysqlperformanceblog.com/2007/04/06/using-delayed-join-to-optimize-count-and-limit-queries/)
* - éviter de dépendre d'une jointure systématique sur `cel_obs`, uniquement pour `(date_)transmission
* (cf VIEW del_image)
* - poursuivre la réorganisation des méthodes statiques parmis Observation, ListeObservations et ListeImages2
* - *peut-être*: passer requestFilterParams() en méthode de classe
*
*
* MySQL sux:
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
* MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT 3);
* PRIMARY
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT MIN(3));
* DEPENDENT SUBQUERY ... ... ... mwarf !
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1);
* 5.5: MATERIALIZED del_image_tag ALL ce_image NULL NULL NULL 38276 Using where
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
*
*/
 
require_once(dirname(__FILE__) . '/../DelTk.php');
require_once(dirname(__FILE__) . '/../observations/Observation.php');
restore_error_handler();
restore_exception_handler();
error_reporting(E_ALL);
 
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&masque=plop
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
 
class ListeImages2 {
 
// TODO: PHP-x.y, ces variables devrait être des "const"
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
 
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags');
 
// en plus de ceux dans DelTk
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
 
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
'tri' => 'date_transmission', 'ordre' => 'desc',
// spécifiques à PictoFlora:
'format' => 'XL');
 
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes))
 
static $mappings = array(
'observations' => array( // v_del_image
"id_observation" => 1,
"date_observation" => 1,
"date_transmission" => 1,
"famille" => "determination.famille",
"nom_sel" => "determination.ns",
"nom_sel_nn" => "determination.nn",
"nom_referentiel" => "determination.referentiel",
"nt" => "determination.nt",
"ce_zone_geo" => "id_zone_geo",
"zone_geo" => 1,
"lieudit" => 1,
"station" => 1,
"milieu" => 1,
"mots_cles_texte" => "mots_cles_texte",
"commentaire" => 1,
"ce_utilisateur" => "auteur.id",
"nom_utilisateur" => "auteur.nom",
"prenom_utilisateur" => "auteur.prenom",
),
'images' => array( // v_del_image
'id_image' => 1,
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
'i_mots_cles_texte' => 1
));
 
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_images.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
 
public function consulter($ressources, $parametres) {
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur.
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
// pour les votes, les mappings de "Observation" nous suffisent
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
 
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité
// de `ce_image` dans ces tables jointes.
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array());
 
 
$db = $this->bdd;
 
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
// même si ceux-ci sont improbables (pas d'<input> pour cela).
$params_ip = DelTk::requestFilterParams($parametres,
array_diff(DelTk::$parametres_autorises,
array('masque.type')),
$this->conteneur);
 
// notre propre filtrage sur l'INPUT
$params_pf = self::requestFilterParams($parametres,
array_merge(DelTk::$parametres_autorises,
self::$parametres_autorises));
 
/* filtrage des tags + sémantique des valeurs multiples:
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
 
$params = array_merge(
DelTk::$default_params, // paramètre par défaut Identiplante
self::$default_params, // paramètres par défaut PictoFlora
$params_ip, // les paramètres passés, traités par Identiplante
$params_pf); // les paramètres passés, traités par PictoFlora
 
// XXX: temp tweak
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('images.url_images'),
"%09d", $params['format']));*/
 
// création des contraintes (génériques de DelTk)
DelTk::sqlAddConstraint($params, $db, $req);
// création des contraintes spécifiques (sur les tags essentiellement)
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
// création des contraintes spécifiques impliquées par le masque général
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
// l'ORDER BY s'avére complexe
self::sqlOrderBy($params, $db, $req);
 
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db);
$idobs_tab = self::getIdImages($params, $req, $db);
 
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
if(!$idobs_tab) {
$resultat = new ResultatService();
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
'resultats' => array());
return $resultat;
/*
header('HTTP/1.0 404 Not Found');
// don't die (phpunit)
throw(new Exception()); */
}
 
 
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
 
$liaisons = self::chargerImages($db, $idobs);
 
/*
// Q&D
$images = array();
$o = new Observation($this->conteneur);
foreach($idobs as $i) {
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
}
*/
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
$liaisons,
$this->conteneur->getParametre('images.url_images'),
$params['format']);
 
 
// on charge les votes pour ces images et pour *tous* les protocoles
$votes = Observation::chargerVotesImage($db, $liaisons, NULL);
 
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation.
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation)
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque
// c'est encore possible.
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
 
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
$resultat = new ResultatService();
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
'resultats' => $images);
return $resultat;
}
 
/**
* TODO: partie spécifique liées à la complexité de PictoFlora:
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req)
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
*/
static function sqlOrderBy($p, $db, &$req) {
// parmi self::$tri_possible
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
return;
}
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
return;
}
 
if($p['tri'] == 'date_observation') {
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
return;
}
 
// tri == 'date_transmission'
// avant cel:r1860, date_transmission pouvait être NULL
// or nous voulons de la consistence (notamment pour phpunit)
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
}
 
/*
* in $p: un tableau de paramètres, dont:
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
*
* in/ou: $req: un tableau de structure de requête MySQL
*
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
* Soit directement $this->consulter() si des masque.tag* sont passés
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
* Soit via sqlAddMasqueConstraint():
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
* équivalent à:
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
*
*/
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
// TODO implement dans DelTk ?
if(!empty($p['masque.milieu'])) {
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
}
 
 
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
celui-ci indique sur quels votes porte l'AVG.
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
/* TODO: perf problème:
1) SQL_CALC_FOUND_ROWS: fixable en:
- dissociant le comptage de la récup d'id + javascript async
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
(paramètre booléen "with-total" par exemple)
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
(cel_images/cel_obs_images/cel_obs/del_image_stat)
Cependant c'est à l'optimiseur de définir son ordre préféré. */
if($p['tri'] == 'votes') {
// $p['protocole'] *est* défini (cf requestFilterParams())
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
if($p['ordre'] == 'desc') {
// pas de group by nécessaire pour cette jointure
// PRIMARY KEY (`ce_image`, `ce_protocole`)
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
' ON vdi.id_image = dis.ce_image'.
' AND dis.ce_protocole = %d',
$p['protocole']);
} else {
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
' ON vdi.id_image = dis.ce_image'.
' AND dis.ce_protocole = %d',
$p['protocole']);
// nécessaire (dup ce_image dans del_image_stat)
$req['groupby'][] = 'vdi.id_observation';
}
}
 
if($p['tri'] == 'tags') {
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
// nécessaire (dup ce_image dans del_image_stat)
$req['groupby'][] = 'vdi.id_observation';
}
 
// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
// sont toujours présentes; bien que parfois NULL.
if($p['masque.tag_cel']) {
if(isset($p['masque.tag_cel']['AND'])) {
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
// et auquel cas laisser au client le choix du couteux "%" ?
$tags = $p['masque.tag_cel']['AND'];
array_walk($tags, create_function('&$val, $k, $db',
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
$db->proteger("%".$val."%"));'),
$db);
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
}
else {
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
}
}
 
if($p['masque.tag_pictoflora']) {
// inutilisable pour l'instant
// self::sqlAddPictoFloraTagConstraint1($p, $db, $req);
 
// intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery)
// self::sqlAddPictoFloraTagConstraint2($p, $db, $req);
 
// approche fiable mais sous-optimale
self::sqlAddPictoFloraTagConstraint3($p, $db, $req);
}
}
 
/* approche intéressante si les deux problèmes suivants peuvent être résolu:
- LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
- dans le cas d'un ET logique, comment chercher les observations correspondantes ? */
static function sqlAddPictoFloraTagConstraint1($p, $db, &$req) {
// XXX: utiliser tag plutôt que tag_normalise ?
$req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
$req['where'][] = 'dit.actif = 1';
$req['groupby'][] = 'vdi.id_image'; // TODO: nécessaire (car dup') mais risque de conflict en cas de tri (multiple GROUP BY)
// XXX: en cas de ET, possibilité du GROUP_CONCAT(), mais probablement sans grand intérêt, d'où une boucle
if(isset($p['masque.tag_pictoflora']['AND'])) {
// TODO/XXX : comment matcher les observations ayant tous les mots-clef passés ?
// ... le LEFT-JOIN n'y semble pas adapté
}
else {
$protected_tags = array();
foreach($p['masque.tag_pictoflora']['OR'] as $tag) $protected_tags[] = $db->proteger(strtolower($tag));
$req['where'][] = sprintf('tag_normalise IN (%s)', implode(',', $protected_tags));
}
}
 
// inutilisé pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro)
static function sqlAddPictoFloraTagConstraint2($p, $db, &$req) {
// Note à propos des 4 "@ instruction" ci-dessous (notamment sur recupererTous())
// REGEXP permet un puissant mécanisme de sélection des obs/image à qui sait
// l'utiliser, mais peut sortir une erreur en cas de REGEXP invalide
// ex: REGEX "^(".
// Pour l'heure nous ignorons ce type d'erreur car aucun de nos champ de recherche
// ne peuvent (ou ne devrait) comporter des meta-caractères
// ([])?*+\\
if(isset($p['masque.tag_pictoflora']['AND'])) {
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($p['masque.tag_pictoflora']['AND']);
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
}
else {
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
}
}
 
// si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction
static function sqlAddPictoFloraTagConstraint3($p, $db, &$req) {
if(isset($p['masque.tag_pictoflora']['AND'])) {
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($p['masque.tag_pictoflora']['AND']);
 
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ...
$ids = @$db->recupererTous(sprintf(
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s",
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND']))));
 
// puis:
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
 
}
else {
$ids = @$db->recupererTous(sprintf(
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s",
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR']))));
 
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
}
}
 
static function getIdImages($p, $req, $db) {
return $db->recupererTous(sprintf(
'SELECT SQL_CALC_FOUND_ROWS id_image' .
' FROM v_del_image vdi'.
' %s' . // LEFT JOIN if any
' WHERE %s'. // where-clause ou TRUE
' %s'. // group-by
' ORDER BY %s'.
' LIMIT %d, %d -- %s',
$req['join'] ? implode(' ', array_unique($req['join'])) : '',
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
$req['orderby'],
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
 
}
 
static function chargerImages($db, $idImg) {
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
return $db->recupererTous(sprintf('SELECT '.
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
' %1$s, %2$s FROM v_del_image '.
' WHERE %3$s'.
' -- %4$s',
$obs_fields, $image_fields,
sprintf('id_image IN (%s)', implode(',', $idImg)),
__FILE__ . ':' . __LINE__));
 
}
 
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
de manière identique à la seule différence que:
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
Tous les mots-clefs doivent matcher et sont séparés par des espaces
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
if(!empty($p['masque'])) {
$or_params = array('masque.auteur' => $p['masque'],
'masque.departement' => $p['masque'],
'masque.commune' => $p['masque'], // TODO/XXX ?
'masque.id_zone_geo' => $p['masque'],
 
/* tous-deux remplacent masque.tag
mais sont traité séparément des requestFilterParams() */
// 'masque.tag_cel' => $p['masque'],
// 'masque.tag_pictoflora' => $p['masque'],
 
'masque.ns' => $p['masque'],
'masque.famille' => $p['masque'],
'masque.date' => $p['masque'],
'masque.genre' => $p['masque'],
'masque.milieu' => $p['masque'],
 
// tri est aussi nécessaire car affecte les contraintes de JOIN
'tri' => $p['tri'],
'ordre' => $p['ordre']);
 
/* Cependant les champs spécifiques ont priorité sur le masque général.
Pour cette raison nous supprimons la génération de SQL du masque général sur les
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
if(isset($p['masque.date'])) unset($or_params['masque.date']);
if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
 
$or_masque = array_merge(
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
self::requestFilterParams($or_params));
 
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
postulés comme séparés par des espaces, et doivent être tous matchés. */
if(isset($or_params['masque.tag_cel']))
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
if(isset($or_params['masque.tag_pictoflora']))
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
 
 
// pas de select, groupby & co ici: uniquement 'join' et 'where'
$or_req = array('join' => array(), 'where' => array());
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
self::sqlAddConstraint($or_masque, $db, $or_req);
 
if($or_req['where']) {
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
// utile au cas ou des jointures seraient rajoutées
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
}
}
}
 
 
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
// XXX: cf Observation.php::consulter(), nous pourriouns ici
// conserver les valeurs vides (pour les phptests notamment, ou non)
// $obs = array_map('array_filter', $obs);
$obs_merged = $obs_keyed_by_id_image = array();
foreach($obs as $o) {
// ceci nous complique la tâche pour le reste du processing...
$id = $o['jsonindex'];
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
// mais tout deux partage leur référence à "protocole"
$image = array(
'id_image' => $o['id_image'],
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
);
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
$obs_merged[$id]['observation'] = $o;
$obs_merged[$id]['protocoles_votes'] = array();
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
}
 
return array($obs_merged,$obs_keyed_by_id_image);
}
 
 
 
// complete & override DelTk::requestFilterParams() (même usage)
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
if($parametres_autorises) { // filtrage de toute clef inconnue
$params = array_intersect_key($params, array_flip($parametres_autorises));
}
 
$p = array();
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
 
// "milieu" inutile pour IdentiPlantes ?
if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
 
// compatibilité
if(isset($params['masque.tag'])) {
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
}
 
if($p['tri'] == 'votes' || $p['tri'] == 'tags') {
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
if(!isset($params['protocole']) || !is_numeric($params['protocole']))
$p['protocole'] = self::$default_proto;
else
$p['protocole'] = intval($params['protocole']);
}
 
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
}
 
 
 
// met à jour *toutes* les stats de nombre de tags et de moyenne des votes
static function _update_statistics($db) {
$db->requeter("TRUNCATE TABLE del_image_stat");
$db->requeter(<<<EOF
INSERT INTO `del_image_stat` (
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags
FROM `tb_cel`.`cel_images` ci
LEFT JOIN
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes FROM del_image_vote
GROUP BY ce_image, ce_protocole ) AS divo
ON ci.id_image = divo.ce_image
LEFT JOIN
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag
GROUP BY ce_image ) AS dit
ON ci.id_image = dit.ce_image )
EOF
);
}
 
static function revOrderBy($orderby) {
return $orderby == 'asc' ? 'desc' : 'asc';
}
}
/branches/v1.1-helium/services/configurations/config_syndication.ini
New file
0,0 → 1,22
; +------------------------------------------------------------------------------------------------------+
servicesDispo = "commentaires,votesParProtocole,tagsParProtocole";
formatsRss = "rss1,rss2,atom";
 
; +------------------------------------------------------------------------------------------------------+
; Editeur du flux
editeur = "Tela Botanica";
 
; Format du Guid de DEL pour le flux de syndication
guidObsTpl = "urn:lsid:tela-botanica.org:del:%s";
voteParProtocole = "urn:lsid:tela-botanica.org:picto:vote%s"
tagParProtocole = "urn:lsid:tela-botanica.org:picto:tag%s"
 
; Lien de base vers l'appli DEL
delAppliLien = "http://www.tela-botanica.org/appli:identiplante";
 
; Lien de base vers la fiche de l'observation dans DEL
delFicheObsTpl = "{ref:delAppliLien}#page_validation~%s";
pictofloraFicheObsTpl = "{ref:delAppliLien}#page_recherche_images";
 
; Squelette d'Url permettant d'afficher une image du CEL (remplace %s par l'id de l'image sans underscore)
celImgUrlTpl = "http://api.tela-botanica.org/img:%s.jpg"
/branches/v1.1-helium/services/configurations/config_determinations.ini
New file
0,0 → 1,13
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "protocole,valeur_vote_min";
protocoles_possibles = "1,2,3,4";
mots_cles_cel_affiches = "fleur,fleurs,feuille,feuilles,ecorce,fruit,fruits,port,plantnet,plantscan_new";
 
[images]
; URL de base des services de ce projet
url_service = "{ref:url_base}del/services/0.1/determinations/images-determinations-probables";
url_images = "http://api.tela-botanica.org/img:%09dS.jpg";
url_fiche_eflore = "http://www.tela-botanica.org/nn%s";
/branches/v1.1-helium/services/configurations/config_commentaires.ini
New file
0,0 → 1,22
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "proposition,observation"
 
[commentaires]
; URL de base des services de ce projet
url_service="{ref:url_base}del/services/0.1/commentaires"
 
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "votes"
 
[mapping_service]
votes = "votes-image"
[mapping_masque]
observation = "ce_observation"
proposition = "ce_proposition"
/branches/v1.1-helium/services/configurations/config_communes.ini
New file
0,0 → 1,15
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "nom"
 
[commentaires]
; URL de base des services de ce projet
url_service="{ref:url_base}del/services/0.1/communes"
 
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "liste-communes"
/branches/v1.1-helium/services/configurations/config.defaut.ini
New file
0,0 → 1,79
; Encodage : UTF-8
 
; +------------------------------------------------------------------------------------------------------+
; URLs
; Le séparateur utilisé par le framework lorsqu'il génère des URL pour séparer les arguments.
; Pour remettre les valeurs par défaut, utitliser : "php:ini_get('arg_separator.output')"
url_arg_separateur_sortie = "&"
 
; +------------------------------------------------------------------------------------------------------+
; Info sur l'application
info.nom = Services d'Identiplante
; Abréviation de l'application
info.abr = del-services
; Version du Framework nécessaire au fonctionnement de cette application
info.framework.version = 0.3
;Encodage de l'application
encodage_appli = "UTF-8"
 
; +------------------------------------------------------------------------------------------------------+
; Débogage
; Indique si oui ou non on veut afficher le débogage.
debogage = true
; Indique sous quelle forme les méssages de débogage doivent s'afficher :
; - "php:Debug::MODE_ECHO" : le message est affiché en utilisant echo
; - "php:Debug::MODE_NOTICE" : le message est affiché en utilisant une erreur de type notice
; - "php:Debug::MODE_ENTETE_HTTP" : les messages sont envoyés dans un entête HTTP "X_REST_DEBOGAGE".
; - "Autre valeur" : les messages sont formatés puis retournés par la méthode de débogage utilisée.
debogage_mode = "php:Debug::MODE_ECHO"
; Indique si oui ou non on veut lancer le chronométrage
chronometrage = false
 
 
; +------------------------------------------------------------------------------------------------------+
; Paramètrage de la base de données.
; Abstraction de la base de données.
bdd_abstraction = pdo
; Protocole de la base de données.
bdd_protocole = mysql
; Nom du serveur de bases de données.
bdd_serveur = localhost
; Nom de l'utilisateur de la base de données.
bdd_utilisateur = ""
; Mot de passe de l'utilisateur de la base de données.
bdd_mot_de_passe = ""
; Nom de la base de données principale.
bdd_nom = "del"
; Encodage de la base de données principale au format base de données (ex. pour l'utf-8 ne pas mettre le tiret!).
bdd_encodage = "utf8"
 
; +------------------------------------------------------------------------------------------------------+
; Infos sur les services
;chemin direct aux services
serveur.baseURL = /del/services/
;URL à rediriger
serveur.baseAlternativeURL = /service:del:0.1/
 
; Version des service web du projet
service_version = 0.1
; Standard utilisé pour les codes de langue
langue_source = "ISO-639-1";
; Standard utilisé pour les codes de zones géographiques
zone_geo_source = "ISO-3166-1";
 
; URL de base des services
url_base="http://localhost/"
; URL de base des services
url_service_base='{ref:url_base}service:del:0.1/'
; URL de base du service appelé lors de la validation
; un POST est effectué à cette adresse, ce qui tolère les réécritures d'URL
; mais non les 30x. En cas de Cel distinct d'Eflore, et indépendemment de l'URI,
; pointer vers l'hôte final, eg: http://moncel/jrest/CelValidationObservation/
url_service_validation_base='{ref:url_base}service:cel:CelValidationObservation/';
 
; URL de base du service appelé pour autocompléter les noms de taxons
url_service_completion_base="{ref:url_base}service:eflore:0.1/%s/noms?masque=%s&recherche=etendue&retour.format=min&navigation.limite=50&ns.structure=au";
 
; +------------------------------------------------------------------------------------------------------+
; Administrateurs de del
admins = "accueil@tela-botanica.org,aurelien@tela-botanica.org,delphine@tela-botanica.org,gregoire@tela-botanica.org,jpm@tela-botanica.org";
/branches/v1.1-helium/services/configurations/config_mots_cles.ini
New file
0,0 → 1,17
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "image,auteur.id"
 
[motscles]
; URL de base des services de ce projet
url_service="{ref:url_base}del/services/0.1/motscles"
 
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
[mapping_masque]
image = "ce_image"
auteur.id = "ce_utilisateur"
/branches/v1.1-helium/services/configurations/config_protocoles.ini
New file
0,0 → 1,7
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
 
; pour le moment le fichier est quasiment vide mais il servira lors de l'ajout
; d'une interface d'admin des protocoles
/branches/v1.1-helium/services/configurations/config_syndication_commentaires.ini
New file
0,0 → 1,7
masques_possibles = "espece,auteur,observation";
url_service="{ref:url_base}eflore-test/del/services/0.1/syndication/commentaires"
 
; +------------------------------------------------------------------------------------------------------+
[mapping_masque]
espece = "nom_sel";
observation = "ce_observation";
/branches/v1.1-helium/services/configurations/config_departements.ini
New file
0,0 → 1,101
Ain = 01
Aisne = 02
Allier = 03
Alpes-de-Haute-Provence = 04
Hautes-Alpes = 05
Alpes-Maritimes = 06
Ardèche = 07
Ardennes = 08
Ariège = 09
Aube = 10
Aude = 11
Aveyron = 12
Bouches-du-Rhône = 13
Calvados = 14
Cantal = 15
Charente = 16
Charente-Maritime = 17
Cher = 18
Corrèze = 19
Corse-du-Sud = 2A
Haute-Corse = 2B
Côte-d'Or = 21
Côtes-d'Armor = 22
Creuse = 23
Dordogne = 24
Doubs = 25
Drôme = 26
Eure = 27
Eure-et-Loir = 28
Finistère = 29
Gard = 30
Haute-Garonne = 31
Gers = 32
Gironde = 33
Hérault = 34
Ille-et-Vilaine = 35
Indre = 36
Indre-et-Loire = 37
Isère = 38
Jura = 39
Landes = 40
Loir-et-Cher = 41
Loire = 42
Haute-Loire = 43
Loire-Atlantique = 44
Loiret = 45
Lot = 46
Lot-et-Garonne = 47
Lozère = 48
Maine-et-Loire = 49
Manche = 50
Marne = 51
Haute-Marne = 52
Mayenne = 53
Meurthe-et-Moselle = 54
Meuse = 55
Morbihan = 56
Moselle = 57
Nièvre = 58
Nord = 59
Oise = 60
Orne = 61
Pas-de-Calais = 62
Puy-de-Dôme = 63
Pyrénées-Atlantiques = 64
Hautes-Pyrénées = 65
Pyrénées-Orientales = 66
Bas-Rhin = 67
Haut-Rhin = 68
Rhône = 69
Haute-Saône = 70
Saône-et-Loire = 71
Sarthe = 72
Savoie = 73
Haute-Savoie = 74
Paris = 75
Seine-Maritime = 76
Seine-et-Marne = 77
Yvelines = 78
Deux-Sèvres = 79
Somme = 80
Tarn = 81
Tarn-et-Garonne = 82
Var = 83
Vaucluse = 84
Vendée = 85
Vienne = 86
Haute-Vienne = 87
Vosges = 88
Yonne = 89
Territoire de Belfort = 90
Essonne = 91
Hauts-de-Seine = 92
Seine-Saint-Denis = 93
Val-de-Marne = 94
Val-d'Oise = 95
Guadeloupe = 971
Martinique = 972
Guyane = 973
La Réunion = 974
Mayotte = 976
/branches/v1.1-helium/services/configurations/config_syndication_tagsparprotocole.ini
New file
0,0 → 1,8
masques_possibles = "image,protocole";
url_service="{ref:url_base}eflore-test/del/services/0.1/syndication/tagsParProtocole"
pictoAppliLien = http://www.tela-botanica.org/appli:test:pictoflora
; +------------------------------------------------------------------------------------------------------+
[mapping_masque]
image = "id_image";
protocole = "ce_protocole"
/branches/v1.1-helium/services/configurations/config_utilisateurs.ini
New file
0,0 → 1,44
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "famille,genre,ns,nn,auteur,date,commune,departement,tag,espece"
 
['utilisateurs']
; URL de base des services de ce projet
url_service = "{ref:url_base}del/services/0.1/utilisateurs/"
url_images = "http://api.tela-botanica.org/img:%sXL.jpg"
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "identification-anonyme,connecter,deconnecter"
 
 
[mapping_observation]
id_observation = "id_observation"
date_observation = "date_observation"
date_transmission = "date_transmission"
famille = "determination.famille"
nom_sel = "determination.ns"
nom_sel_nn = "determination.nn"
nt = "determination.nt"
courriel = "observateur"
ce_zone_geo = "id_zone_geo"
zone_geo = "zone_geo"
lieudit = "lieudit"
station = "station"
milieu = "milieu"
nom = "auteur.nom"
prenom = "auteur.prenom"
ce_utilisateur = "auteur.id"
 
[mapping_masque]
famille = "famille"
ns = "nom_sel"
nn = "nom_sel_nn"
date = "date_observation"
tag = "mots_cles_texte"
commune = "zone_geo"
id_zone_geo = "ce_zone_geo"
espece = "nom_sel"
/branches/v1.1-helium/services/configurations/config_syndication_votesparprotocole.ini
New file
0,0 → 1,8
masques_possibles = "image,protocole";
url_service="{ref:url_base}eflore-test/del/services/0.1/syndication/votesParProtocole"
pictoAppliLien = http://www.tela-botanica.org/appli:test:pictoflora
; +------------------------------------------------------------------------------------------------------+
[mapping_masque]
image = "ce_image";
protocole = "ce_protocole"
/branches/v1.1-helium/services/configurations/config_votes.ini
New file
0,0 → 1,14
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "id_image"
 
['images']
; URL de base des services de ce projet
url_service = "{ref:url_service_base}votes/Images"
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "Images, Observations"
/branches/v1.1-helium/services/configurations/config_mapping_commentaires.ini
New file
0,0 → 1,17
; +------------------------------------------------------------------------------------------------------+
[mapping_commentaire]
id_commentaire = "id_commentaire"
ce_observation = "observation"
ce_proposition = "proposition"
ce_commentaire_parent = "id_parent"
ce_utilisateur = "auteur.id"
texte = "texte"
utilisateur_nom = "auteur.nom"
utilisateur_prenom = "auteur.prenom"
utilisateur_courriel = "auteur.courriel"
date = "date"
nom_sel = "nom_sel"
nom_sel_nn = "nom_sel_nn"
nom_ret_nn = nom_ret_nn
nom_referentiel = nom_referentiel
proposition_initiale = proposition_initiale;
/branches/v1.1-helium/services/configurations/config_determination.ini
New file
0,0 → 1,40
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
 
[images]
; URL de base des services de ce projet
url_service = "{ref:url_base}del/services/0.1/projets/del/images"
url_images = "http://api.tela-botanica.org/img:%sXL.jpg"
 
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "Determinations"
 
[mapping_observation]
id_observation = "id_observation"
date_observation = "date_observation"
date_transmission = "date_transmission"
famille = "determination.famille"
nom_sel = "determination.ns"
nom_sel_nn = "determination.nn"
nt = "determination.nt"
ce_zone_geo = "id_zone_geo"
zone_geo = "zone_geo"
lieudit = "lieudit"
station = "station"
milieu = "milieu"
ce_utilisateur = "auteur.id"
nom = "auteur.nom"
prenom = "auteur.prenom"
 
[mapping_masque]
famille = "famille"
ns = "nom_sel"
nn = "nom_sel_nn"
date = "date_observation"
tag = "mots_cles_texte"
commune = "zone_geo"
/branches/v1.1-helium/services/configurations/config_mapping_mots_cles.ini
New file
0,0 → 1,7
; +------------------------------------------------------------------------------------------------------+
[mapping_mots_cles]
id_tag = "id_mot_cle"
ce_image = "image"
ce_utilisateur = "auteur.id"
date = "date"
tag = "mot_cle"
/branches/v1.1-helium/services/configurations/config_images.ini
New file
0,0 → 1,51
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "masque,famille,genre,ns,nn,nt,auteur,date,commune,departement,tag,espece,referentiel"
formats_possibles = "O,CRX2S,CRS,CXS,CS,XS,S,M,L,XL,X2L,X3L"
tris_possibles = "date_observation,votes,tags"
mots_cles_cel_affiches = "fleur,fleurs,feuille,feuilles,ecorce,fruit,fruits,port,defiphoto,plantnet"
 
[images]
; URL de base des services de ce projet
url_service = "{ref:url_service_base}images"
url_images = "http://api.tela-botanica.org/img:%09d%s.jpg";
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "votes"
[mapping_service]
votes = "votes-image"
 
[mapping_observation]
id_observation = "id_observation"
date_observation = "date_observation"
date_transmission = "date_transmission"
famille = "determination.famille"
nom_sel = "determination.ns"
nom_sel_nn = "determination.nn"
nom_referentiel = "determination.referentiel"
nt = "determination.nt"
ce_zone_geo = "id_zone_geo"
zone_geo = "zone_geo"
lieudit = "lieudit"
station = "station"
milieu = "milieu"
ce_utilisateur = "auteur.id"
nom = "auteur.nom"
prenom = "auteur.prenom"
mots_cles_texte = "mots_cles_texte"
commentaire = "commentaire"
 
[mapping_masque]
famille = "famille"
ns = "nom_sel"
nn = "nom_sel_nn"
date = "date_observation"
tag = "mots_cles_texte"
tag_cel = "mots_cles_texte"
tag_pictoflora = "mots_cles_texte"
commune = "zone_geo"
referentiel = "nom_referentiel"
/branches/v1.1-helium/services/configurations/config_nomstaxons.ini
New file
0,0 → 1,15
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "nom,referentiel"
 
[commentaires]
; URL de base des services de ce projet
url_service="{ref:url_base}del/services/0.1/nomstaxons"
 
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "liste-taxons"
/branches/v1.1-helium/services/configurations/config_departements_bruts.ini
New file
0,0 → 1,101
ain = 01
aisne = 02
allier = 03
alpes-de-haute-provence = 04
hautes-alpes = 05
alpes-maritimes = 06
ardeche = 07
ardennes = 08
ariege = 09
aube = 10
aude = 11
aveyron = 12
bouches-du-rhone = 13
calvados = 14
cantal = 15
charente = 16
charente-maritime = 17
cher = 18
correze = 19
corse-du-sud = 2a
haute-corse = 2b
cote-d'or = 21
cotes-d'armor = 22
creuse = 23
dordogne = 24
doubs = 25
drome = 26
eure = 27
eure-et-loir = 28
finistere = 29
gard = 30
haute-garonne = 31
gers = 32
gironde = 33
herault = 34
ille-et-vilaine = 35
indre = 36
indre-et-loire = 37
isere = 38
jura = 39
landes = 40
loir-et-cher = 41
loire = 42
haute-loire = 43
loire-atlantique = 44
loiret = 45
lot = 46
lot-et-garonne = 47
lozere = 48
maine-et-loire = 49
manche = 50
marne = 51
haute-marne = 52
mayenne = 53
meurthe-et-moselle = 54
meuse = 55
morbihan = 56
moselle = 57
nievre = 58
nord = 59
oise = 60
orne = 61
pas-de-calais = 62
puy-de-dome = 63
pyrenees-atlantiques = 64
hautes-pyrenees = 65
pyrenees-orientales = 66
bas-rhin = 67
haut-rhin = 68
rhone = 69
haute-saone = 70
saone-et-loire = 71
sarthe = 72
savoie = 73
haute-savoie = 74
paris = 75
seine-maritime = 76
seine-et-marne = 77
yvelines = 78
deux-sevres = 79
somme = 80
tarn = 81
tarn-et-garonne = 82
var = 83
vaucluse = 84
vendee = 85
vienne = 86
haute-vienne = 87
vosges = 88
yonne = 89
territoire-de-belfort = 90
essonne = 91
hauts-de-seine = 92
seine-saint-denis = 93
val-de-marne = 94
val-d-oise = 95
guadeloupe = 971
martinique = 972
guyane = 973
la-reunion = 974
mayotte = 976
/branches/v1.1-helium/services/configurations/config_mapping_votes.ini
New file
0,0 → 1,17
; +------------------------------------------------------------------------------------------------------+
[mapping_votes]
ce_protocole = "protocole.id"
ce_proposition = "proposition.id"
id_vote = "vote.id"
valeur = "vote"
ce_image = "image.id"
ce_utilisateur = "auteur.id"
nom = "auteur.nom"
prenom = "auteur.prenom"
courriel = "auteur.courriel"
date = "date"
id_protocole = "protocole.id"
intitule = "protocole.intitule"
descriptif = "protocole.descriptif"
tag = "protocole.tag"
mots_cles = "protocole.mots_cles"
/branches/v1.1-helium/services/configurations/config_observations.ini
New file
0,0 → 1,52
; Encodage : UTF-8
 
; Nom du projet
nom_projet = "del"
masques_possibles = "masque,famille,genre,ns,nn,auteur,date,commune,departement,tag,espece,type,referentiel"
tag_adeterminer = aDeterminer
nb_commentaires_discussion = 1
 
[observations]
; URL de base des services de ce projet
; url_service = "{ref:url_base}del/services/0.1/observations/"
url_service = "{ref:url_base}service:del:0.1/observations/"
url_images = "http://api.tela-botanica.org/img:%09dXL.jpg";
 
; +------------------------------------------------------------------------------------------------------+
; Config spécifique au projet
; Noms des services disponibles pour ce projet
servicesDispo = "observation, vote"
 
 
[mapping_observation]
id_observation = "id_observation"
date_observation = "date_observation"
date_transmission = "date_transmission"
famille = "determination.famille"
nom_sel = "determination.ns"
nom_sel_nn = "determination.nn"
nt = "determination.nt"
nom_referentiel = "determination.referentiel"
courriel = "observateur"
ce_zone_geo = "id_zone_geo"
zone_geo = "zone_geo"
lieudit = "lieudit"
station = "station"
milieu = "milieu"
nom = "auteur.nom"
prenom = "auteur.prenom"
ce_utilisateur = "auteur.id"
mots_cles_texte = "mots_cles_texte"
dob_commentaire = commentaire
 
 
[mapping_masque]
famille = "famille"
ns = "nom_sel"
nn = "nom_sel_nn"
date = "date_observation"
tag = "mots_cles_texte"
commune = "zone_geo"
id_zone_geo = "ce_zone_geo"
espece = "nom_sel"
referentiel = "nom_referentiel"
/branches/v1.1-helium/services/configurations
New file
Property changes:
Added: svn:ignore
+config.ini
/branches/v1.1-helium/services/framework.defaut.php
New file
0,0 → 1,6
<?php
// Inclusion du Framework
// Renomer ce fichier en "framework.php"
// Indiquer ci-dessous le chemin absolu vers le fichier autoload.inc.php de la bonne version du Framework
require_once '/home/delphine/web/framework-0.3/framework/Framework.php';
?>
/branches/v1.1-helium/services/.htaccess
New file
0,0 → 1,9
<files *.ini>
order deny,allow
deny from all
</files>
 
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ index.php
/branches/v1.1-helium/services/README.txt
New file
0,0 → 1,5
Configurer le .htacces de la racine du serveur pour placer la ligne :
 
# Règle de ré-ecriture d'url pour l'accès au format eFlore
RewriteRule ^service:del:([0-9]\.[0-9])/([^/]+)/(v[0-9]+[_.][0-9]+|[+*]|v[0-9]+)/([^/]+)/?(.*)?$ /del/services/$1/projets/$2/$4/$5?version.projet=$3
RewriteRule ^service:del:([0-9]\.[0-9])/([^/]+)/(.+)$ /del/services/$1/projets/$2/$3
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/v1.1-helium/services/bibliotheque/gestionUtilisateur.php
New file
0,0 → 1,88
<?php
class gestionUtilisateur {
private $conteneur;
private $gestionBdd;
private $bdd;
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->conteneur->chargerConfiguration('config_utilisateurs.ini');
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
$this->demarrerSession();
}
private function demarrerSession() {
if(session_id() == '') {
// modifier ce test lors du passage en php 5.4
session_start();
}
}
protected function getUtilisateurAnonyme() {
return array('connecte' => false,
'id_utilisateur' => session_id(),
'courriel' => '',
'mot_de_passe' => '',
'nom' => '',
'prenom' => '',
'session_id' => session_id()
);
}
protected function utilisateurEstIdentifie() {
$utilisateur = null;
$utilisateur = $this->utilisateurEstIdentifieCookie();
if ($utilisateur != null) {
$utilisateur['session_id'] = session_id();
}
return $utilisateur;
}
protected function utilisateurEstIdentifieCookie() {
$retour = null;
if(isset($_COOKIE["del_courriel"]) && isset($_COOKIE["del_mot_de_passe"])) {
$retour = $this->identifierUtilisateurSansEncryptionMotDePasse($_COOKIE["del_courriel"], $_COOKIE["del_mot_de_passe"]);
}
return $retour;
}
protected function identifierUtilisateur($login, $motDePasse) {
$requete = 'SELECT id_utilisateur, nom, prenom, courriel, mot_de_passe FROM '.$this->gestionBdd->formaterTable('del_utilisateur', 'du').
'WHERE courriel = '.$this->bdd->proteger($login).' '.
'AND mot_de_passe = MD5('.$this->bdd->proteger($motDePasse).')';
$utilisateur = $this->bdd->recupererTous($requete);
return $utilisateur;
}
protected function identifierUtilisateurSansEncryptionMotDePasse($login, $mot_de_passe) {
$requete = 'SELECT id_utilisateur, nom, prenom, courriel, mot_de_passe FROM '.$this->gestionBdd->formaterTable('del_utilisateur', 'du').
'WHERE courriel = '.$this->bdd->proteger($login).' '.
'AND mot_de_passe = '.$this->bdd->proteger($mot_de_passe);
$utilisateur = $this->bdd->recuperer($requete);
return $utilisateur;
}
protected function setUtilisateur($utilisateur) {
$utilisateur['session_id'] = session_id();
$utilisateur['connecte'] = true;
$this->setPersistentCookie("del_courriel", $utilisateur['courriel'], 1);
$this->setPersistentCookie("del_mot_de_passe", $utilisateur['mot_de_passe'], 1);
return $utilisateur;
}
protected function setPersistentCookie($name, $value, $remember = 1) {
setcookie($name, $value, time() + ($remember ? (60*60*24*100) : (60*60)),'/');
}
protected function oublierUtilisateur() {
setcookie("del_courriel", $_COOKIE['del_courriel'], time()-3600, '/');
setcookie("del_mot_de_passe", $_COOKIE['del_mot_de_passe'], time()-3600, '/');
unset($_COOKIE['del_courriel']);
unset($_COOKIE['del_mot_de_passe']);
}
}
?>
/branches/v1.1-helium/services/bibliotheque/ControleAcces.php
New file
0,0 → 1,143
<?php
/**
* Classe de controle d'accès utilisant l'authentification http pour bloquer ou autoriser l'accès.
* Elle offre des méthodes permettant de réserver l'accès à une section aux admins ou bien aux utilisateurs identifiés
*
* @category php 5.2
* @package del
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id$
*/
class ControleAcces {
private $conteneur;
private $gestionBdd;
private $bdd;
public function __construct($conteneur) {
$this->conteneur = $conteneur;
$this->gestionBdd = $conteneur->getGestionBdd();
$this->bdd = $this->gestionBdd->getBdd();
}
public function demanderAuthentificationAdmin() {
if(!$this->etreAdminAutorise()) {
$this->authentifierAdmin();
}
}
public function demanderAuthentificationUtilisateur() {
if(!$this->etreUtilisateurAutorise()) {
$this->authentifierUtilisateur();
}
}
public function etreUtilisateurAutorise() {
$identifiant = $this->getAuthIdentifiant();
$mdp = $this->getAuthMotDePasse();
$existe = $this->obtenirUtilisateur($identifiant, $mdp);
$autorisation = (isset($existe) && $existe) ? true :false;
return $autorisation;
}
private function obtenirUtilisateur($login, $motDePasse) {
$requete = 'SELECT id_utilisateur, nom, prenom, courriel, mot_de_passe FROM '.$this->gestionBdd->formaterTable('del_utilisateur', 'du').
'WHERE courriel = '.$this->gestionBdd->getBdd()->proteger($login).' '.
'AND mot_de_passe = MD5('.$this->gestionBdd->getBdd()->proteger($motDePasse).')';
$utilisateur = $this->gestionBdd->getBdd()->recuperer($requete . ' -- ' . __FILE__ . ':' . __LINE__);
return $utilisateur;
}
private function obtenirUtilisateurSansEncryptionMdp($login, $motDePasseEncrypte) {
$requete = 'SELECT id_utilisateur, nom, prenom, courriel, mot_de_passe FROM '.$this->gestionBdd->formaterTable('del_utilisateur', 'du').
'WHERE courriel = '.$this->gestionBdd->getBdd()->proteger($login).' '.
'AND mot_de_passe = '.$this->gestionBdd->getBdd()->proteger($motDePasseEncrypte).' ';
$utilisateur = $this->gestionBdd->getBdd()->recuperer($requete . ' -- ' . __FILE__ . ':' . __LINE__);
return $utilisateur;
}
public function etreAdminAutorise() {
$identifiant = $this->getAuthIdentifiant();
$autorisation = ($this->etreAdminDel($identifiant) && $this->etreUtilisateurAutorise()) ? true : false;
return $autorisation;
}
public function etreAdminDel($courriel) {
$admins = Config::get('admins');
$courriels_autorises = explode(',', $admins);
$autorisation = (in_array($courriel, $courriels_autorises)) ? true : false ;
return $autorisation;
}
private function getAuthIdentifiant() {
$id = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : null;
return $id;
}
private function getAuthMotDePasse() {
$mdp = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : null;
return $mdp;
}
//TODO: externaliser noms et adresses spécifiques à Tela Botanica
private function authentifierAdmin() {
$message_accueil = "Veuillez vous identifier avec votre compte administrateur Tela Botanica.";
$message_echec = "Accès limité aux administrateurs de DEL.\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');
}
private function authentifierUtilisateur() {
$message_accueil = "Veuillez vous identifier avec votre compte Tela Botanica.";
$message_echec = "Accès limité aux utilisateurs de DEL.\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');
}
private function authentifier($message_accueil, $message_echec, $type) {
$id = $this->getAuthIdentifiant();
if (!isset($id)) {
$this->envoyerAuth($message_accueil, $message_echec);
} else {
if ($type == 'Utilisateur' && $this->getAuthMotDePasse() == 'debug') {
$autorisation = true;
} else {
$methodeAutorisation = "etre{$type}Autorise";
$autorisation = $this->$methodeAutorisation();
}
if ($autorisation == false) {
$this->envoyerAuth($message_accueil, $message_echec);
}
}
return true;
}
public function getInfosUtilisateurConnecte() {
if(isset($_COOKIE["del_courriel"])) {
$utilisateur = array('del_courriel' => $_COOKIE["del_courriel"],
'del_mot_de_passe' => $_COOKIE["del_mot_de_passe"]);
$utilisateur = $this->obtenirUtilisateurSansEncryptionMdp($utilisateur['del_courriel'], $utilisateur['del_mot_de_passe']);
}
return $utilisateur;
}
public function getIdAnonymeTemporaire() {
$this->demarrerSession();
return session_id();
}
private function demarrerSession() {
if(session_id() == '') {
// modifier ce test lors du passage en php 5.4
session_start();
}
}
}
/branches/v1.1-helium/services/bibliotheque/EnteteHttp.php
New file
0,0 → 1,7
<?php
class EnteteHttp {
public $code = RestServeur::HTTP_CODE_OK;
public $encodage = 'utf-8';
public $mime = 'application/json';
}
?>
/branches/v1.1-helium/services/bibliotheque/ResultatService.php
New file
0,0 → 1,7
<?php
class ResultatService {
public $mime = 'application/json';
public $encodage = 'utf-8';
public $corps = '';
}
?>
/branches/v1.1-helium/services/bibliotheque/Masque.php
New file
0,0 → 1,80
<?php
/**
* Navigation gère les url de navigation en fonction d'un départ et d'une limite
 
* @category php 5.2
* @package del
* @author Grégoire Duché <gregoire@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
*/
class Masque {
const prefixe = 'masque.';
const masqueGeneral = 'masque';
private $masquesPossibles;
private $parametres;
private $masque;
/**
* Constructeur de la classe Masque
* @param $masquesPossibles la liste des masques autorisés séparé par des ','
* */
public function __construct($masquesPossibles, $parametres = null) {
if ($masquesPossibles != null && trim($masquesPossibles) == '') {
$message = 'La liste des masques possibles est obligatoire';
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
$this->masquesPossibles = explode(',', $masquesPossibles);
$this->parametres = $parametres;
$this->chargerMasque();
}
/**
* Parcourir le tableau Paramètres pour trouver tous les champs masque
*/
public function chargerMasque() {
if ($this->parametres != null) {
foreach ($this->parametres as $id => $parametre) {
if (strpos($id, self::prefixe) === 0 || $id == self::masqueGeneral) {
if (in_array(str_replace(self::prefixe, '', $id), $this->masquesPossibles)) {
$this->masque[$id] = $parametre;
}
}
}
}
}
/**
* Retourner les masques sous forme de chaine
* @return String la chaine de caractère sous la forme masque=valeur&masque2=valeur*/
public function getChaineMasque() {
if (!empty($this->masque)) {
$chaine = array();
foreach ($this->masque as $id => $valeur) {
$chaine[] = $id.'='.$valeur;
}
return implode('&', $chaine);
} else {
return '';
}
}
/**
* Récupérer tout ou partie du masque
* @param String $id (optionnel) l'idenfiant du masque
* @return une chaine de caractère si l'identifiant est passé en paramètre, un tableau sinon
* */
public function getMasque($id = null) {
if (isset($id)) {
return $this->masque[self::prefixe.$id];
} else {
return $this->masque;
}
}
}
?>
/branches/v1.1-helium/services/bibliotheque/ReponseHttp.php
New file
0,0 → 1,62
<?php
class ReponseHttp {
 
private $resultatService = null;
private $erreurs = array();
 
public function __construct() {
$this->resultatService = new ResultatService();
}
 
public function setResultatService($resultat) {
if (!($resultat instanceof ResultatService)) {
$this->resultatService->corps = $resultat;
} else {
$this->resultatService = $resultat;
}
}
 
public function getCorps() {
if ($this->etreEnErreur()) {
$this->resultatService->corps = $this->erreurs[0]['message'];
} else {
$this->transformerReponseCorpsSuivantMime();
}
return $this->resultatService->corps;
}
 
public function ajouterErreur(Exception $e) {
$this->erreurs[] = array('entete' => $e->getCode(), 'message' => $e->getMessage());
}
 
public function emettreLesEntetes() {
$enteteHttp = new EnteteHttp();
if ($this->etreEnErreur()) {
$enteteHttp->code = $this->erreurs[0]['entete'];
$enteteHttp->mime = 'text/html';
} else {
$enteteHttp->encodage = $this->resultatService->encodage;
$enteteHttp->mime = $this->resultatService->mime;
}
header("Content-Type: $enteteHttp->mime; charset=$enteteHttp->encodage");
RestServeur::envoyerEnteteStatutHttp($enteteHttp->code);
}
 
private function etreEnErreur() {
$enErreur = false;
if (count($this->erreurs) > 0) {
$enErreur = true;
}
return $enErreur;
}
 
private function transformerReponseCorpsSuivantMime() {
switch ($this->resultatService->mime) {
case 'application/json' :
$this->resultatService->corps = json_encode($this->resultatService->corps);
break;
}
}
 
}
?>
/branches/v1.1-helium/services/bibliotheque/Commun.php
New file
0,0 → 1,419
<?php
/**
* Classe Commun.php est une classe abstraite qui contient les méthodes de base communes à tous les
* sous-services des projets.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package eflore-projets
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 1.0
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
*/
 
abstract class Commun {
 
/** Objet Bdd. */
private $Bdd = null;
/** Objet Rest Client. */
private $RestClient = null;
 
/** Contients les paramètres. Doit remplacer table_param. */
protected $parametres = array();
/** Contients les ressources. Doit remplacer table_ressources. */
protected $ressources = array();
/** Le nom du service courrant. */
protected $serviceNom = null;
 
//Classe commune à tous les services web d'un projet. Contient par exemple les fonctions permettant de
//renvoyer la réponse http...
protected $entete_http; // Entete de la réponse correspondant au code de réponse de la requete http */
protected $corps_http; // Tableau de résultat à retourner au format json ou la description de l'erreur si elle existe */
protected $service; // Nom du service appelé
/** Stocke la version du projet demandée dans la requete
* - "*" : (/#projet/* /meta-donnees) Renvoi les meta-données de toutes les versions du projet
* - "numero de la version" : (/#projet/2.00/meta-donnees) Renvoi les meta-données de la version 2.00 du projet */
protected $version_projet = '+';
protected $table_version; //Stocke les noms des tables de toutes les versions du projet disponibles
/** tableau contenant tous les champs d'une table (est rempli par la fonction Commun::recupererNomChamp($table)) */
protected $champs_table = array();
private static $tri_multi_dimension = array();
private static $tri_type = '';
 
public function consulter($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->chargerNomDuService();
 
$this->traiterParametres();
$this->traiterVersionProjet();
 
$resultats = '';
foreach ($this->table_version as $version) {
$this->table = $version; //on stocke le nom de la table correspondant à la version du projet en cours
$this->recupererNomChamp($this->table); //on récupère les noms des champs disponibles (Ds Commun.php)
$this->traiterRessources(); //dans CommunNomsTaxons.php
$requete = $this->assemblerLaRequete();
$resultat = $this->getBdd()->recupererTous($requete);
$versionResultat = $this->traiterResultat($resultat, $version, $requete);
if (count($this->table_version) > 1) {
$resultats[$version] = $versionResultat;
} else {
$resultats = $versionResultat;
}
}
 
return $resultats;
}
 
private function chargerNomDuService() {
$this->serviceNom = get_class($this);
}
 
public function traiterResultat($resultat, $version, $requete) {
$versionResultat = null;
if ($resultat == '') {
//cas ou la requete comporte des erreurs
$message = 'La requête SQL formée comporte une erreur!';
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
} elseif ($resultat) {
$versionResultat = $this->retournerResultatFormate($resultat, $version);
} else {
$message = 'Les données recherchées sont introuvables.';
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $versionResultat;
}
 
//+------------------------------------------------------------------------------------------------------+
// Méthodes concernant les paramètres
/**
* Permet de récupérer une liste des noms des champs de la table passée en paramètre
* @param $table : Nom de la table dont on souhaite connaitre les champs
*/
public function recupererNomChamp($table) {
$requete = 'SHOW FIELDS FROM '.$table;
 
$resultat = $this->getBdd()->recupererTous($requete);
if ($resultat == '') {
$e = 'La requête SQL formée comporte une erreur!';
$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e);
} elseif ($resultat) {
foreach ($resultat as $info) {
$this->champs_table[] = $info['Field'];
}
} else {
$m = "La table recherchée n'existe pas";
$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m);
}
}
 
/**
* Fonction permettant de creer la table dont le nom est passé en paramètre (champs_api, champs_bdtfx,
* correspondance_champs...). Les données de chaque table sont présentes dans le fichier de configuration config.ini
* @param String $table : Peut contenir plusieurs nom de table dont on souhaite récupérer les données : table,table,table.
* Ex : recupererTableConfig('champs_api,champs_bdtfx')
*/
public function recupererTableConfig($table) {
$tables = explode(',', $table);
foreach ($tables as $tab) {
$tableau = explode(',', Config::get($tab));
$tableau = array_map('trim', $tableau);
foreach ($tableau as $champ) {
list($code, $rang) = explode('=', $champ);
$tab_tampon[$code] = $rang;
}
$this->$tab = $tab_tampon;
$tab_tampon = array();
}
}
 
public function renvoyerErreur($entete, $message) {
throw new Exception($message, $entete);
}
 
/**
* Permet de remplir la variable version_projet et de retirer cette donnée du tableau des ressources
* @param $ressources
*/
public function traiterVersionProjet() {
if (isset($this->parametres['version.projet'])) {
if (preg_match('/^[0-9]+(?:[._][0-9]+|)$/', $this->parametres['version.projet'])) {
$this->version_projet = $this->parametres['version.projet'];
$this->version_projet = 'v'.str_replace('.', '_', $this->version_projet);
} else {
$this->version_projet = $this->parametres['version.projet'];
}
}
//si la liste des noms est demandée pr toutes les versions, on affiche seulement la dernière version :
if ($this->version_projet == '*' && $this->ressources == array()) {
$message = "L'affichage de plusieurs versions ne fonctionne que pour les ressources de type /ressources/#id";
$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
throw new Exception($message, $code);
}
//on recupère les versions du projet disponible dans la table des meta-donnees (utilisation service MetaDonnees)
$table_num_version = $this->recupererVersionDisponible();
//on recupere la liste des noms des tables de la bdd correspondant aux differentes versions du projet en fct de la ou les versions demandées
$this->recupererListeNomTablePrChaqueVersion($table_num_version);
}
 
/**
* Recupération des versions disponibles par appel du service metaDonnees
* Verification de l'existance du service recherché dans la requete (si précisé : hors *)
* @return array : tableau contenant le numéro de chaque version disponible
*/
public function recupererVersionDisponible() {
$versions_dispo = '';
$req_version = 'SELECT version FROM '.Config::get('bdd_table_meta');
$res_version = $this->getBdd()->recupererTous($req_version);
if ($res_version == '') { //cas ou la requete comporte des erreurs
$e = "La requête SQL de versionnage formée comporte une erreur : $req_version";
$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e);
} elseif ($res_version) {
foreach ($res_version as $version) {
$versions_dispo[] = $version['version'];
}
} else {
$m = 'Versions introuvables dans la table des méta-données';
$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m);
}
return $versions_dispo;
}
 
public function recupererListeNomTablePrChaqueVersion($table_num_version) {
switch ($this->serviceNom) {
case 'Ontologies' :
$prefixe_table = 'bdd_table_ontologies';
break;
default:
$prefixe_table = 'bdd_table';
}
switch ($this->version_projet) {
case '+' :
$derniere_version = $table_num_version[count($table_num_version) - 1];
$this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $derniere_version);
break;
case '*' :
foreach ($table_num_version as $num_version) {
$this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $num_version);
}
break;
default :
$this->table_version[] = Config::get($prefixe_table).'_'.$this->version_projet;
break;
}
}
 
/**
* Est appelée pour former l'url complete des resultats precedants ou suivants.
* @param int : Permet de connaitre le nombre de noms obtenus par la requete
* @return string Retourne l'url complete des resultats precedents ou suivant sous la forme d'un tableau
*/
public function formulerUrl($nb_resultat, $id = null) {
$url = array();
$debut_url = Config::get('url_service').$id.'?';
//on recré l'url sans les parametres de navigation qui seront rajouter ci-apres. On les enlève dc de la table des parametres
foreach($this->parametres as $cle => $val) {
$param_url[str_replace('_', '.', $cle)] = $val;
}
 
$this->recupererLesLimitesSuivantes($nb_resultat, $param_url);
if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) {
$url['suivant'] = $debut_url.http_build_query($param_url);
}
 
$this->recupererLesLimitesPrecedentes($param_url);
if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) {
$url['precedent'] = $debut_url.http_build_query($param_url);
}
return $url;
}
 
public function supprimerNavigation(&$param_url) {
unset($param_url['navigation.depart']);
unset($param_url['navigation.limite']);
}
 
/**
* Description :
* Permet de former les limites de la requete retournant les résultats suivants.
* Cette url sera afficher dans l'entete de la reponse retournée en format JSON (retour.format=defaut).
* @param int : $nb_resultat : Permet de connaitre le nombre de résultats obtenus par la requete
* @return string : la fin de l'url decrivant les limites des resultats suivants. Si aucun résultats ne suient,
* une chaine de caractère vide est retournée
*/
public function recupererLesLimitesSuivantes($nb_resultat, &$param_url_suiv) {
$this->supprimerNavigation($param_url);
$depart = $this->limite_requete['depart'];
$limite = $this->limite_requete['limite'];
$depart_suivant = $depart + $limite;
$limite_suivant = $limite;
if ($nb_resultat > $depart_suivant) {
$param_url_suiv['navigation.depart'] = $depart_suivant;
$param_url_suiv['navigation.limite'] = $limite_suivant;
} else {
$param_url_suiv['navigation.depart'] = null;
$param_url_suiv['navigation.limite'] = null;
}
}
 
/**
* Description :
* Permet de former les limites de la requete retournant les résultats precedents.
* Cette url sera afficher dans l'entete de la taxons/105reponse retournée en format JSON (retour.format=defaut)
* @return string : la fin de l'url decrivant les limites des resultats precedents.
* Si aucun résultats ne precedent, une chaine de caractère vide est retournée
*/
public function recupererLesLimitesPrecedentes(&$param_url) {
$this->supprimerNavigation($param_url);
$depart = $this->limite_requete['depart'];
$limite = $this->limite_requete['limite'];
if ($depart == 0) {
$url_precedente = '';
} else {
if (($depart - $limite) < 0) {
$depart_precedent = 0;
} else {
$depart_precedent = $depart - $limite;
}
$param_url['navigation.depart'] = $depart_precedent;
$param_url['navigation.limite'] = $limite;
}
}
 
public function ajouterHref($service, $val) {
// http://tela-botanica.org/service:eflore:0.1/[projet]/[version_projet]/[service]/[ressource]:[valeur]
if ($this->version_projet == '+') {
$url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val;
} else {
$url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val.'?version.projet='.ltrim($this->version_projet, 'v');
}
return $url;
}
 
public function ajouterHrefAutreProjet($service, $ressource, $valeur, $projet = null, $param = null) {
//on enleve les GA et Co, les meta ou les "_"
$this->transliterer($service, $valeur);
//on définit les nom des projets, des services et des ressources de l'url (dans les méta-donnees)
$tab = array(
'langue' => array('service' => 'langues', 'projet' => 'iso-639-1', 'ressource' => ''),
'couverture_spatiale' => array('service' => 'zone-geo', 'projet' => 'iso-3166-1', 'ressource' => ''),
'type' => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'contactType:'),
'datum' => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'datum:')
);
if (array_key_exists($service, $tab)) {
extract($tab[$service]);
} else {
if (strpos(Config::get('nom_projet'), 'bd') === 0 && $projet == null) {
$projet = 'bdnt';
$service = 'ontologies';
$ressource = '';
}
}
$param = ($param) ? "?".$param : "";
$url = Config::get('url_service_base').$projet.'/'.$service.'/'.$ressource.$valeur.$param;
return $url;
}
 
/**Permet de consulter une url et retourne le résultat ou une erreur
* @param $url */
public function consulterHref($url) {
$res = $this->getRestClient()->consulter($url);
$entete = $this->getRestClient()->getReponseEntetes();
//Si le service meta-donnees fonctionne correctement, l'entete comprend la clé wrapper_data
if (isset($entete['wrapper_data'])) {
$res = json_decode($res);
return $res;
} else {
$u = 'L\'url <a href="'.$url.'">'.$url.'</a> lancée via RestClient renvoie une erreur';
$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $u);
}
}
 
public function transliterer(&$service, &$val) {
if (preg_match('/^.+:(.+)$/', $val, $match)) {
$val = $match[1];
}
$service = str_replace(array('_Ga','_Co','_meta'), '', $service);
if ($service == 'rang') {
$ressource = 'rangTaxo';
} elseif (preg_match('/^(statut)(?:_|-)([^_-]+)$/', $service, $match)) {
$service = $match[1].ucfirst($match[2]);
} elseif (strrpos($service, 'datum') !== false) {
$service = 'datum';
}
}
 
// prend en valeur la valeur de la recherche, les résultats approchés, le paramétre recherche
// retourne le tableau trié en fonction de la ressemble entre le résultat approché et la valeur recherchée
public function trierRechercheFloue($nom_demande, $tab_approchee, $nom) {
$trie = '';
$resultat = array();
foreach ($tab_approchee as $id => $tab) {
$nom_demande_ss = strtolower(Chaine::supprimerAccents($nom_demande));
$nom_flou_ss = strtolower(Chaine::supprimerAccents($tab[$nom]));
$stat = array();
// Prime pour la ressemblance globale :
$score = 500 - levenshtein($nom_flou_ss, $nom_demande_ss);
// On affine
$score = $score + (similar_text($nom_demande_ss, $nom_flou_ss) * 3);
$stat['score'] = $score;
foreach ($tab as $key => $valeur) {
$stat[$key] = $valeur;
}
$resultat[] = $stat;
}
 
 
// Vérification que nous avons bien trouvé un nom approché
if (count($resultat) > 0) {
$trie = Tableau::trierMD($resultat, array('score' => SORT_DESC));
}
return $trie;
}
 
protected function recupererTableauConfig($param) {
$tableau = array();
$tableauPartiel = explode(',', Config::get($param));
$tableauPartiel = array_map('trim', $tableauPartiel);
foreach ($tableauPartiel as $champ) {
if (strpos($champ, '=') === false) {
$tableau[] = $champ;
} else {
list($cle, $val) = explode('=', $champ);
$tableau[$cle] = $val;
}
}
return $tableau;
}
 
//+------------------------------------------------------------------------------------------------------+
// Méthodes d'accès aux objets du Framework
/**
* Méthode de connection à la base de données sur demande.
* Tous les services web n'ont pas besoin de s'y connecter.
*/
protected function getBdd() {
if (! isset($this->Bdd)) {
$this->Bdd = new Bdd();
}
return $this->Bdd;
}
 
/**
* Méthode permettant de faire appel à un client REST en fonction des besoins du service.
*/
protected function getRestClient() {
if (! isset($this->RestClient)) {
$this->RestClient = new RestClient();
}
return $this->RestClient;
}
}
?>
Property changes:
Added: svn:executable
+*
\ No newline at end of property
/branches/v1.1-helium/services/bibliotheque/Navigation.php
New file
0,0 → 1,131
<?php
/**
* Navigation gère les url de navigation en fonction d'un départ et d'une limite
 
* @category php 5.2
* @package del
* @author Grégoire Duché <gregoire@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
*/
class Navigation {
 
private $parametres;
private $urlNavigation;
private $total;
 
/**
* Constructeur de la classe Navigation
* @param Array $parametres (optionnel) la liste des paramètre issus du Conteneur
* */
public function __construct($parametres = null) {
$this->parametres = $parametres;
}
 
/**
* Obtenir la valeur courante de départ
* */
public function getDepart() {
return isset($this->parametres['navigation.depart']) ? $this->parametres['navigation.depart'] : 0;
}
 
/**
* Obtenir la limite courante
* */
public function getLimite() {
$limite = 10;
if (isset($this->parametres['navigation.limite']) && is_numeric($this->parametres['navigation.limite'])) {
$limite = $this->parametres['navigation.limite'];
$limite = ($limite < 1000) ? $limite : 1000;// Pour éviter les abus !
}
return $limite;
}
 
/**
* Configurer l'Url de navigation en fonction du fichier de configuration
* */
public function chargerUrl() {
$this->urlNavigation = new Url(Config::get('url_service'));
$this->urlNavigation->setOption(Url::OPTION_ENCODER_VALEURS, true);
}
 
/**
* Obtenir l'url en fonction d'un départ et d'une limite donnée
* @param int $depart l'entier de départ
* @param int $limite le nombre d'éléments limite
* */
public function getUrl($depart = null, $limite = null) {
if ($depart == null && $limite == null) {
return $this->urlNavigation;
} else {
return $this->obtenirUrlNavigation($depart, $limite);
}
}
 
/**
* Récupérer l'url de navigation en concaténant d'éventuels paramètres
* @param $depart l'entier de départ de la recherche
* @param $limite le nombre de résultats à retourner
* @param $parametresAdditionnels le tableau contenant les parametres => valeurs additionnels
* */
private function obtenirUrlNavigation($depart, $limite) {
 
$parametres = $this->parametres;
$parametres['navigation.depart'] = $depart;
$parametres['navigation.limite'] = $limite;
 
$this->urlNavigation->setRequete($parametres);
$url = $this->urlNavigation->getURL();
return $url;
}
 
/**
* Récupérer le lien pour afficher les images précédentes en fonction des paramètres
* */
public function recupererHrefPrecedent() {
$departActuel = $this->getDepart();
$limite = $this->getLimite();
$departPrecedent = $departActuel - $limite;
 
$url = null;
 
if ($departActuel > 0) {
$url = $this->getUrl($departPrecedent, $limite);
}
return $url;
}
 
/**
* Récupérer le lien pour afficher les images suivantes en fonction des paramètres
* */
public function recupererHrefSuivant() {
 
$departActuel = $this->getDepart();
$limite = $this->getLimite();
$departSuivant = $departActuel + $limite;
$url = null;
if ($departSuivant < $this->total) {
$url = $this->getUrl($departSuivant, $limite);
}
return $url;
}
 
/**
* Retourner le nombre total d'éléments
* */
public function getTotal() {
return $this->total;
}
 
/**
* Enregistrer le nombre total d'éléments
* @param int $total le nombre d'éléments
* */
public function setTotal($total) {
$this->total = $total;
}
 
}
?>
/branches/v1.1-helium/services/bibliotheque/Outils.php
New file
0,0 → 1,21
<?php
class Outils {
 
public static function recupererTableauConfig($parametres) {
$tableau = array();
$tableauPartiel = explode(',', Config::get($parametres));
$tableauPartiel = array_map('trim', $tableauPartiel);
foreach ($tableauPartiel as $champ) {
if (strpos($champ, '=') === false) {
$tableau[] = $champ;
} else {
list($cle, $val) = explode('=', $champ);
$clePropre = trim($cle);
$valeurPropre = trim($val);
$tableau[$clePropre] = $valeurPropre;
}
}
return $tableau;
}
}
?>
/branches/v1.1-helium/services/bibliotheque/Conteneur.php
New file
0,0 → 1,168
<?php
/**
* Le conteneur encapsule les classe Masque, Navigation et GestionBdd
* Il gère leur instanciation, ainsi que la récupération des paramètres depuis l'url ou
* les fichiers de configuration
*
* @category php 5.2
* @package del
* @author Grégoire Duché <gregoire@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
*/
class Conteneur {
protected $parametres;
protected $navigation;
protected $masque;
protected $gestionBdd;
protected $sansLimite = false;
//TODO : initialiser tous les objets dans le conteneur
//TODO : créer un tableau de partage
/**
* Constructeur de la classe
* @param Array $parametres (optionnel) les paramètres additionnels à ajouter à ceux des fichiers de config
* */
public function __construct(array $parametres = null) {
$this->parametres = is_null($parametres) ? array() : $parametres;
}
/**
* Charger la configuration depuis le fichier
* @param String $chemin le chemin relatif depuis le dossier configurations du fichier
* */
public function chargerConfiguration($chemin) {
$cheminConfigurations = Config::get('chemin_configurations');
if ($cheminConfigurations == null || $cheminConfigurations == '') {
$message = 'Le chemin vers le répertoire Configurations n\'est pas renseigné';
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
Config::charger($cheminConfigurations.DS.$chemin);
if ($this->masque = $this->creerMasque()) {
$this->masque->chargerMasque();
} else {
$message = 'Erreur lors de la création du Masque';
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
if ($this->navigation = $this->creerNavigation()) {
$this->navigation->chargerUrl();
} else {
$message = 'Erreur lors de la création de la Navigation';
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
$this->creerGestionBdd($this->navigation, Config::get('schemaBdd'));
}
/**
* Créer l'objet Masque en fonction des configurations
* */
private function creerMasque() {
$this->masque = new Masque(Config::get('masques_possibles'), $this->parametres);
return $this->masque;
}
/**
* Créer l'objet navigation avec les paramètres
* */
private function creerNavigation() {
return ($this->navigation = new Navigation($this->parametres));
}
/**
* Créer l'objet Gestion BDD
* */
private function creerGestionBdd($navigation, $schemaBdd) {
$this->gestionBdd = new GestionBdd($navigation, $schemaBdd);
}
/**
* Récupérer l'objet GestionBdd
* */
public function getGestionBdd() {
return $this->gestionBdd;
}
 
/**
* Récupérer l'objet Navigation
* */
public function getNavigation() {
return $this->navigation;
}
/**
* Changer la valeur de sans limite pour ne pas l'afficher dans l'entete
* */
public function setSansLimite() {
$this->sansLimite = true;
}
/**
* Créer l'entête en fonction des paramètres donnés
* */
public function getEntete() {
$entete = array();
$entete['masque'] = $this->masque->getChaineMasque();
$entete['total'] = $this->navigation->getTotal();
if ($this->sansLimite == false) {
$entete['depart'] = $this->navigation->getDepart();
$entete['limite'] = $this->navigation->getLimite();
$lienPrecedent = $this->navigation->recupererHrefPrecedent();
if ($lienPrecedent != null) {
$entete['href.precedent'] = $lienPrecedent;
}
$lienSuivant = $this->navigation->recupererHrefSuivant();
if ($lienSuivant) {
$entete['href.suivant'] = $lienSuivant;
}
}
return $entete;
}
public function getRestClient() {
if (!isset($this->restClient)) {
$this->restClient = new RestClient();
}
return $this->restClient;
}
/**
* Récupérer l'objet Masque
* */
public function getMasque() {
return $this->masque;
}
/**
* Obtenir un paramètre depuis le tableau de paramètres ou depuis le fichier de config
* @param String $cle le nom du paramètre
* @return la valeur du paramètre
* */
public function getParametre($cle) {
$valeur = isset($this->parametres[$cle]) ? $this->parametres[$cle] : Config::get($cle);
return $valeur;
}
/**
* Enregistrer la valeur d'un paramètre
* */
public function setParametre($cle, $valeur) {
$this->parametres[$cle] = $valeur;
}
}
?>
/branches/v1.1-helium/services/bibliotheque/GestionBdd.php
New file
0,0 → 1,91
<?php
/**
* Gestion BDD permet l'appel à l'objet BDD du framework,
* gère les noms de table en fonction du schéma de la table
* ainsi que la limite SQL en fonction de l'objet Navigation
 
* @category php 5.2
* @package del
* @author Grégoire Duché <gregoire@tela-botanica.org>
* @copyright Copyright (c) 2012, Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version $Id: Bdd.php 403 2012-02-22 14:35:20Z gduche $
*/
class GestionBdd {
private $bdd;
private $schemaBdd;
private $navigation;
/**
* Constructeur de la classe GestionBdd
* @param Navigation $navigation l'objet navigation issu du Conteneur, et contenant les informations de départ et limite
* @param String $schemaBdd (optionnel) le schéma de la base issu du fichier de config
* @param Bdd $bdd (optionnel) un objet Bdd pour la connection à la base de données
* */
public function __construct($navigation, $schemaBdd = null, $bdd = null) {
if ($navigation == null) {
$message = 'L\'objet Navigation est nécessaire pour configurer l\'objet GestionBdd';
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
} else {
$this->navigation = $navigation;
$this->schemaBdd = $schemaBdd;
if ($bdd == null) {
$this->bdd = new Bdd();
} else {
$this->bdd = $bdd;
}
}
}
/**
* Créer la chaine de limite de requête en fonction des paramètres donnés
* */
public function getLimitSql() {
return ' LIMIT '.$this->navigation->getDepart().', '.$this->navigation->getLimite();
}
/**
* Récupérer l'objet Bdd ou le créer s'il n'existe pas
* @return Bdd l'objet Bdd
* */
public function getBdd() {
if (!isset($this->bdd)){
$this->bdd = new Bdd();
}
return $this->bdd;
}
/** Formater un nom de table avec le schéma de la base de données, s'il existe
* @param String $table le nom de la table
* @param String $as (optionnel) l'alias de la table
* @return String la chaine de caractère sous la forme "schema.nomtable as nt"
* */
public function formaterTable($table, $as = null) {
$chaineTable = $table;
if ($this->schemaBdd != null) {
$chaineTable = $this->schemaBdd.'.'.$table;
}
if ($as != null && $as != '') {
$chaineTable .= ' as '.$as;
}
return $chaineTable.' ';
}
/** Formater des noms de table avec le schéma de la base de données, s'il existe
* @param Array $tables un tableau à deux dimensions contenant le nom et l'alias de la table
* Ex : Array("nom" => "as", "table" => "")
* @return String la chaine de caractère sous la forme "schema.nomtable as nt, schema.nomtable2 as nt2"
* */
public function formaterTables($tables) {
$tablesFormatees = array();
foreach ($tables as $nom => $as) {
$tablesFormatees[] = $this->formaterTable($nom, $as);
}
return implode(',', $tablesFormatees);
}
}
?>
/branches/v1.1-helium/services/index.php
New file
0,0 → 1,43
<?php
// Encodage : UTF-8
// Permet d'afficher le temps d'execution du service
$temps_debut = (isset($_GET['chrono']) && $_GET['chrono'] == 1) ? microtime(true) : '';
// +-------------------------------------------------------------------------------------------------------------------+
/**
* Serveur
*
* Description : initialise le chargement et l'exécution des services web.
*
//Auteur original :
* @author auteur <aut@tela-botanica.org>
* @copyright Tela-Botanica 1999-2008
* @licence GPL v3 & CeCILL v2
* @version $Id$
*/
// +-------------------------------------------------------------------------------------------------------------------+
 
// Le fichier autoload.inc.php du Framework de Tela Botanica doit être appelée avant tout autre chose dans l'application.
// Sinon, rien ne sera chargé.
// Chemin du fichier chargeant le framework requis
$framework = dirname(__FILE__).DIRECTORY_SEPARATOR.'framework.php';
if (!file_exists($framework)) {
$e = "Veuillez paramétrer l'emplacement et la version du Framework dans le fichier $framework";
trigger_error($e, E_USER_ERROR);
} else {
// Inclusion du Framework
require_once $framework;
// Ajout d'information concernant cette application
Framework::setCheminAppli(__FILE__);// Obligatoire
Framework::setInfoAppli(Config::get('info'));
// Initialisation et lancement du serveur
$Serveur = new RestServeur();
$Serveur->executer();
// Affiche le temps d'execution du service
if (isset($_GET['chrono']) && $_GET['chrono'] == 1) {
$temps_fin = microtime(true);
echo 'Temps d\'execution : '.round($temps_fin - $temps_debut, 4);
}
}
?>
/branches/v1.1-helium/services/tests/0.1/observations/masque.type=endiscussion.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque.type%5Bendiscussion%5D=0","total":610,"depart":0,"limite":10,"href.precedent":null,"href.suivant":"http:\/\/eflore?navigation.depart=10&navigation.limite=10&tri=date_transmission&ordre=asc&masque.type%5Bendiscussion%5D=0"},"resultats":{"\"8654\"":{"id_observation":"8654","determination.ns":"Pulmonaria officinalis L.","determination.nt":"1453","determination.nn":"54018","determination.famille":"Boraginaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:80021","zone_geo":"Amiens","station":"Centre ville, jardin.","date_observation":"2008-03-05 00:00:00","date_transmission":"2008-07-18 10:35:51","auteur.id":"7890","auteur.prenom":"Laurent","auteur.nom":"PETIT","observateur":"yanyan80@free.fr","images":[{"id_image":"49865","date":"0000-00-00 00:00:00","hauteur":"1870","largeur":"1320","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000049865XL.jpg"},{"id_image":"49866","date":"2007-03-09 10:39:40","hauteur":"768","largeur":"1024","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000049866XL.jpg"},{"id_image":"49867","date":"2007-03-09 10:43:37","hauteur":"1024","largeur":"768","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000049867XL.jpg"}],"commentaires":[{"957":{"nb_commentaires":"0","id_commentaire":"957","observation":"8654","proposition":"0","id_parent":"0","auteur.id":"7890","auteur.nom":"Petit","auteur.prenom":"Laurent","auteur.courriel":"yanyan80@free.fr","date":"2008-07-18 10:35:51","nom_sel":"Pulmonaria officinalis L.","nom_sel_nn":"54018","nom_ret_nn":"54018","proposition_initiale":"1","votes":{"1253":{"vote.id":"1253","proposition.id":"957","auteur.id":"f85a381e68eda1c1c2e300700735d407","vote":"1","date":"2012-11-29 13:43:06"}}}}]},"\"16117\"":{"id_observation":"16117","determination.ns":"Ophrys scolopax Cav.","determination.nt":"30471","determination.nn":"75479","determination.famille":"Orchidaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34131","zone_geo":"Lauret","date_observation":"2009-05-20 00:00:00","mots_cles_texte":"Mes observations,aurelienperonnet","date_transmission":"2009-09-03 22:54:35","auteur.id":"11623","auteur.prenom":"Gr\u00e9goire","auteur.nom":"DUCH\u00c9","observateur":"gregoire@tela-botanica.org","images":[{"id_image":"6969","date":"2009-05-20 18:09:59","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006969XL.jpg"},{"id_image":"6968","date":"2009-05-20 18:09:28","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006968XL.jpg"},{"id_image":"6970","date":"2009-05-20 18:11:22","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006970XL.jpg"},{"id_image":"6971","date":"2009-05-20 18:13:29","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006971XL.jpg"},{"id_image":"6972","date":"2009-05-20 18:14:22","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006972XL.jpg"},{"id_image":"6973","date":"2009-05-20 18:15:15","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006973XL.jpg"},{"id_image":"6974","date":"2009-05-20 18:15:39","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006974XL.jpg"},{"id_image":"6975","date":"2009-05-20 18:16:15","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006975XL.jpg"},{"id_image":"6976","date":"2009-05-20 18:16:50","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006976XL.jpg"},{"id_image":"6977","date":"2009-05-20 18:16:58","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006977XL.jpg"},{"id_image":"6978","date":"2009-05-20 18:25:52","hauteur":"2304","largeur":"1759","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006978XL.jpg"}],"commentaires":[{"3911":{"nb_commentaires":"0","id_commentaire":"3911","observation":"16117","proposition":"0","id_parent":"0","auteur.id":"11623","auteur.nom":"DUCH\u00c9","auteur.prenom":"Gr\u00e9goire","auteur.courriel":"gregoire@tela-botanica.org","date":"2013-05-13 11:09:03","nom_sel":"Ophrys scolopax Cav.","nom_sel_nn":"75479","nom_ret_nn":"75479","proposition_initiale":"1","votes":{"5151":{"vote.id":"5151","proposition.id":"3911","auteur.id":"11623","vote":"0","date":"2013-05-13 11:09:07"}}}},{"3912":{"nb_commentaires":"0","id_commentaire":"3912","observation":"16117","proposition":"0","id_parent":"0","auteur.id":"11623","texte":"je crois","auteur.nom":"DUCH\u00c9","auteur.prenom":"Gr\u00e9goire","auteur.courriel":"gregoire@tela-botanica.org","date":"2013-05-13 11:09:03","nom_sel":"Ophrys apifera Huds.","nom_sel_nn":"45064","proposition_initiale":"0","votes":{"5150":{"vote.id":"5150","proposition.id":"3912","auteur.id":"11623","vote":"1","date":"2013-05-13 11:09:06"}}}}]},"\"19507\"":{"id_observation":"19507","determination.ns":"Pulmonaria","determination.nt":"50636","determination.nn":"100636","determination.famille":"Boraginaceae","determination.referentiel":"bdtfx:v1.01","zone_geo":"Saint-Antonin-Noble-Val","lieudit":"Montpalach","date_observation":"2010-04-10 00:00:00","date_transmission":"2010-04-12 15:46:54","auteur.id":"51af35965b5e53f4073081fe2c0c5d94","observateur":"nscotto@club-internet.fr","images":[{"id_image":"12904","date":"2008-11-19 19:06:19","hauteur":"2736","largeur":"3648","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000012904XL.jpg"}],"commentaires":[{"3277":{"nb_commentaires":"0","id_commentaire":"3277","observation":"19507","proposition":"0","id_parent":"0","auteur.id":"0","auteur.nom":"","auteur.prenom":"","auteur.courriel":"","date":"2013-04-22 21:06:41","nom_sel":"Pulmonaria","nom_sel_nn":"100636","nom_ret_nn":"100636","proposition_initiale":"1","votes":{"4546":{"vote.id":"4546","proposition.id":"3277","auteur.id":"4657","vote":"1","date":"2013-04-30 12:34:04"},"5436":{"vote.id":"5436","proposition.id":"3277","auteur.id":"2819","vote":"1","date":"2013-05-22 14:48:34"}}}},{"4644":{"nb_commentaires":"0","id_commentaire":"4644","observation":"19507","proposition":"0","id_parent":"0","auteur.id":"13389","auteur.nom":"SABBAG","auteur.prenom":"Claude","auteur.courriel":"sabbag.claude13@gmail.com","date":"2013-06-04 15:41:57","nom_sel":"Pulmonaria longifolia subsp. longifolia","nom_sel_nn":"54005","proposition_initiale":"0"}}]},"\"15282\"":{"id_observation":"15282","determination.ns":"Mespilus germanica L.","determination.nt":"4763","determination.nn":"42397","determination.famille":"Rosaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:30033","zone_geo":"Beauvoisin","lieudit":"La Passeronne","station":"43\u00b043'02.2\"N 04\u00b019'02.2\"E","date_observation":"2009-08-06 00:00:00","date_transmission":"2010-11-04 15:46:31","auteur.id":"7303","auteur.prenom":"Ruddy","auteur.nom":"BENEZET","observateur":"ruddy.benezet@gmail.com","images":[{"id_image":"6410","date":"0000-00-00 00:00:00","hauteur":"1494","largeur":"1528","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006410XL.jpg"},{"id_image":"6409","date":"0000-00-00 00:00:00","hauteur":"1918","largeur":"1719","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006409XL.jpg"},{"id_image":"6408","date":"0000-00-00 00:00:00","hauteur":"2162","largeur":"1371","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006408XL.jpg"},{"id_image":"6407","date":"0000-00-00 00:00:00","hauteur":"3510","largeur":"2226","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006407XL.jpg"},{"id_image":"6406","date":"0000-00-00 00:00:00","hauteur":"3092","largeur":"1975","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006406XL.jpg"},{"id_image":"6405","date":"0000-00-00 00:00:00","hauteur":"3057","largeur":"1989","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006405XL.jpg"},{"id_image":"6404","date":"0000-00-00 00:00:00","hauteur":"1755","largeur":"1275","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006404XL.jpg"},{"id_image":"6881","date":"0000-00-00 00:00:00","hauteur":"1755","largeur":"1275","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006881XL.jpg"},{"id_image":"6882","date":"0000-00-00 00:00:00","hauteur":"2739","largeur":"1509","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006882XL.jpg"},{"id_image":"6883","date":"0000-00-00 00:00:00","hauteur":"2671","largeur":"1995","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006883XL.jpg"},{"id_image":"6884","date":"0000-00-00 00:00:00","hauteur":"3201","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006884XL.jpg"},{"id_image":"6885","date":"0000-00-00 00:00:00","hauteur":"2559","largeur":"2374","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006885XL.jpg"},{"id_image":"6886","date":"0000-00-00 00:00:00","hauteur":"2466","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006886XL.jpg"},{"id_image":"6887","date":"0000-00-00 00:00:00","hauteur":"2829","largeur":"1539","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006887XL.jpg"},{"id_image":"6888","date":"0000-00-00 00:00:00","hauteur":"3254","largeur":"1495","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006888XL.jpg"},{"id_image":"6889","date":"0000-00-00 00:00:00","hauteur":"2933","largeur":"1969","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006889XL.jpg"},{"id_image":"6890","date":"0000-00-00 00:00:00","hauteur":"2994","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006890XL.jpg"}],"commentaires":[{"3937":{"nb_commentaires":"0","id_commentaire":"3937","observation":"15282","proposition":"0","id_parent":"0","auteur.id":"7303","auteur.nom":"BENEZET","auteur.prenom":"Ruddy","auteur.courriel":"ruddy.benezet@gmail.com","date":"2013-05-13 22:49:10","nom_sel":"Mespilus germanica L.","nom_sel_nn":"42397","nom_ret_nn":"42397","proposition_initiale":"1"}},{"3938":{"nb_commentaires":"0","id_commentaire":"3938","observation":"15282","proposition":"0","id_parent":"0","auteur.id":"121","auteur.nom":"BARTH\u00c9L\u00c9MY","auteur.prenom":"Daniel","auteur.courriel":"daniel.barthelemy@cirad.fr","date":"2013-05-13 22:49:10","nom_sel":"Eriobotrya japonica (Thunb.) Lindl.","nom_sel_nn":"24979","proposition_initiale":"0"}}]},"\"167375\"":{"id_observation":"167375","determination.ns":"Carduus tenuiflorus Curtis","determination.nt":"545","determination.nn":"13358","determination.famille":"Asteraceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:84007","zone_geo":"Avignon","lieudit":"Avenue de Tarascon","station":"Culture abandonn\u00e9e","milieu":"Prairie","date_observation":"2009-05-03 00:00:00","mots_cles_texte":"Vigie-Flore,Maille 4202-G","date_transmission":"2010-12-28 23:23:13","auteur.id":"5","auteur.prenom":"Daniel","auteur.nom":"MATHIEU","observateur":"dmathieu@tela-botanica.org","images":[{"id_image":"15974","date":"2009-05-03 10:49:16","hauteur":"800","largeur":"535","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000015974XL.jpg"}],"commentaires":[{"3465":{"nb_commentaires":"0","id_commentaire":"3465","observation":"167375","proposition":"0","id_parent":"0","auteur.id":"5","auteur.nom":"MATHIEU","auteur.prenom":"Daniel","auteur.courriel":"dmathieu@tela-botanica.org","date":"2013-04-28 01:35:25","nom_sel":"Carduus tenuiflorus Curtis","nom_sel_nn":"13358","nom_ret_nn":"13358","proposition_initiale":"1","votes":{"4479":{"vote.id":"4479","proposition.id":"3465","auteur.id":"7163","vote":"0","date":"2013-04-28 01:35:28"}}}},{"3466":{"nb_commentaires":"0","id_commentaire":"3466","observation":"167375","proposition":"0","id_parent":"0","auteur.id":"7163","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 01:35:25","nom_sel":"Carduus pycnocephalus L.","nom_sel_nn":"75026","proposition_initiale":"0"}}]},"\"40099\"":{"id_observation":"40099","determination.ns":"Neotinea ustulata subsp. ustulata","determination.nt":"13044","determination.nn":"78364","determination.famille":"Orchidaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:30153","zone_geo":"Malons-et-Elze","station":"MDA","milieu":"Prairie humide","date_observation":"2010-05-23 00:00:00","mots_cles_texte":"Orchid\u00e9es","date_transmission":"2011-01-01 18:20:46","auteur.id":"11326","auteur.prenom":"Paul","auteur.nom":"Fabre","observateur":"paul.fabre19@gmail.com","commentaire":"Plante discr\u00e8te, d\u00e9licate et tr\u00e8s esth\u00e9tique","images":[{"id_image":"16423","date":"2010-05-23 13:40:33","hauteur":"2816","largeur":"2112","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000016423XL.jpg"},{"id_image":"16584","date":"2010-05-23 14:01:13","hauteur":"2816","largeur":"2112","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000016584XL.jpg"}],"commentaires":[{"356":{"nb_commentaires":"0","id_commentaire":"356","observation":"40099","proposition":"0","id_parent":"0","auteur.id":"11326","auteur.nom":"Fabre","auteur.prenom":"Paul","auteur.courriel":"paul.fabre19@gmail.com","date":"2011-01-01 18:20:46","nom_sel":"Neotinea ustulata subsp. ustulata","nom_sel_nn":"78364","nom_ret_nn":"78364","proposition_initiale":"1","votes":{"543":{"vote.id":"543","proposition.id":"356","auteur.id":"bd60d1e88277ffd39837715ac37fa11a","vote":"1","date":"2012-11-07 20:34:18"},"712":{"vote.id":"712","proposition.id":"356","auteur.id":"f3bb43f8949a9f87052a39850d570fb5","vote":"1","date":"2012-11-22 13:44:09"},"746":{"vote.id":"746","proposition.id":"356","auteur.id":"075162d89e6afe9fec2093b6a03cb87f","vote":"1","date":"2012-11-22 20:47:54"},"1139":{"vote.id":"1139","proposition.id":"356","auteur.id":"d28354f594fac20684eaee3924fc001f","vote":"1","date":"2012-11-27 17:06:32"},"1150":{"vote.id":"1150","proposition.id":"356","auteur.id":"4323cb26721a24199955441fd4ee6dc4","vote":"1","date":"2012-11-29 09:45:49"},"1506":{"vote.id":"1506","proposition.id":"356","auteur.id":"712aefda23de9dcff28d902d66d20959","vote":"1","date":"2013-02-12 18:11:02"},"1558":{"vote.id":"1558","proposition.id":"356","auteur.id":"20482","vote":"1","date":"2013-02-14 15:57:14"},"1630":{"vote.id":"1630","proposition.id":"356","auteur.id":"7887","vote":"1","date":"2013-02-16 18:08:45"},"2217":{"vote.id":"2217","proposition.id":"356","auteur.id":"d5d4b8ed2629ef534b5087c580520121","vote":"1","date":"2013-03-23 16:38:57"},"4710":{"vote.id":"4710","proposition.id":"356","auteur.id":"121","vote":"1","date":"2013-05-01 16:37:50"}}}}]},"\"19081\"":{"id_observation":"19081","determination.ns":"Spiranthes spiralis (L.) Chevall.","determination.nt":"6567","determination.nn":"65818","determination.famille":"Orchidaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34154","zone_geo":"Mauguio","lieudit":"Bois de la Mourre","date_observation":"2008-03-03 00:00:00","date_transmission":"2011-01-09 12:29:27","auteur.id":"12799","auteur.prenom":"Julien","auteur.nom":"BARATAUD","observateur":"julien.barataud@laposte.net","images":[{"id_image":"9815","date":"2008-03-03 18:51:18","hauteur":"1200","largeur":"1600","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000009815XL.jpg"},{"id_image":"9816","date":"2008-03-03 18:51:18","hauteur":"1200","largeur":"1600","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000009816XL.jpg"},{"id_image":"9817","date":"2008-03-03 18:51:09","hauteur":"1600","largeur":"1200","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000009817XL.jpg"}],"commentaires":[{"1567":{"nb_commentaires":"0","id_commentaire":"1567","observation":"19081","proposition":"0","id_parent":"0","auteur.id":"12799","auteur.nom":"BARATAUD","auteur.prenom":"Julien","auteur.courriel":"julien.barataud@laposte.net","date":"0000-00-00 00:00:00","nom_sel":"Spiranthes spiralis (L.) Chevall.","nom_sel_nn":"65818","nom_ret_nn":"65818","proposition_initiale":"0","votes":{"2049":{"vote.id":"2049","proposition.id":"1567","auteur.id":"18748","vote":"0","date":"2013-03-14 15:56:12"}}}}]},"\"313463\"":{"id_observation":"313463","determination.ns":"Urtica urens L.","determination.nt":"5650","determination.nn":"70431","determination.famille":"Urticaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:83126","zone_geo":"La Seyne-sur-Mer","milieu":"friche","date_observation":"2011-04-10 00:00:00","date_transmission":"2011-04-11 09:32:28","auteur.id":"76","auteur.prenom":"Frederic","auteur.nom":"BOUFFARD","observateur":"facama@orange.fr","images":[{"id_image":"27152","date":"2011-04-09 23:17:59","hauteur":"3240","largeur":"4320","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000027152XL.jpg"}],"commentaires":[{"1523":{"nb_commentaires":"0","id_commentaire":"1523","observation":"313463","proposition":"0","id_parent":"0","auteur.id":"76","auteur.nom":"bouffard","auteur.prenom":"frederic","auteur.courriel":"facama@orange.fr","date":"0000-00-00 00:00:00","nom_sel":"Urtica urens L.","nom_sel_nn":"70431","nom_ret_nn":"70431","proposition_initiale":"0","votes":{"2001":{"vote.id":"2001","proposition.id":"1523","auteur.id":"1356","vote":"0","date":"2013-03-10 10:15:15"},"2218":{"vote.id":"2218","proposition.id":"1523","auteur.id":"d5d4b8ed2629ef534b5087c580520121","vote":"1","date":"2013-03-23 16:39:03"}}}}]},"\"350814\"":{"id_observation":"350814","determination.ns":"Gagea granatelli (Parl.) Parl.","determination.nt":"6315","determination.nn":"28634","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34129","zone_geo":"Lattes","lieudit":"Mas Mannier","date_observation":"2005-03-01 00:00:00","date_transmission":"2011-08-04 11:20:23","auteur.id":"16995","auteur.prenom":"Christophe","auteur.nom":"BERNIER","observateur":"christophe.bernier@tela-botanica.org","images":[{"id_image":"39928","date":"2005-03-08 17:47:39","hauteur":"1536","largeur":"2048","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039928XL.jpg"},{"id_image":"39927","date":"2005-03-08 17:41:21","hauteur":"1536","largeur":"2048","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039927XL.jpg"}],"commentaires":[{"1142":{"nb_commentaires":"0","id_commentaire":"1142","observation":"350814","proposition":"0","id_parent":"0","auteur.id":"44","texte":"la gag\u00e9e des garrigues calcaires autour de Montpellier est G. lacaitae. G. granatelli pousse plus \u00e0 l'ouest, dans le Minervois","auteur.nom":"GIROD","auteur.prenom":"Christophe","auteur.courriel":"chrisgir2@yahoo.fr","date":"2013-02-04 20:38:15","nom_sel":"Gagea lacaitae A.Terracc.","proposition_initiale":"0"}},{"1150":{"nb_commentaires":"0","id_commentaire":"1150","observation":"350814","proposition":"0","id_parent":"0","auteur.id":"16995","auteur.nom":"BERNIER","auteur.prenom":"Christophe","auteur.courriel":"christophe.bernier@tela-botanica.org","date":"2011-08-04 11:20:23","nom_sel":"Gagea granatelli (Parl.) Parl.","nom_sel_nn":"28634","nom_ret_nn":"28634","proposition_initiale":"1","votes":{"1434":{"vote.id":"1434","proposition.id":"1150","auteur.id":"44","vote":"0","date":"2013-02-04 20:40:41"},"1564":{"vote.id":"1564","proposition.id":"1150","auteur.id":"389470bbca344f8cdc4905d2c791395b","vote":"0","date":"2013-02-14 16:11:42"}}}}]},"\"350815\"":{"id_observation":"350815","determination.ns":"Gagea granatelli (Parl.) Parl.","determination.nt":"6315","determination.nn":"28634","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34129","zone_geo":"Lattes","lieudit":"Mas Mannier","date_observation":"2005-03-08 00:00:00","date_transmission":"2011-08-04 11:20:23","auteur.id":"16995","auteur.prenom":"Christophe","auteur.nom":"BERNIER","observateur":"christophe.bernier@tela-botanica.org","images":[{"id_image":"39930","date":"0000-00-00 00:00:00","hauteur":"1173","largeur":"1969","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039930XL.jpg"},{"id_image":"39929","date":"0000-00-00 00:00:00","hauteur":"1224","largeur":"1944","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039929XL.jpg"}],"commentaires":[{"1151":{"nb_commentaires":"0","id_commentaire":"1151","observation":"350815","proposition":"0","id_parent":"0","auteur.id":"16995","auteur.nom":"BERNIER","auteur.prenom":"Christophe","auteur.courriel":"christophe.bernier@tela-botanica.org","date":"2011-08-04 11:20:23","nom_sel":"Gagea granatelli (Parl.) Parl.","nom_sel_nn":"28634","nom_ret_nn":"28634","proposition_initiale":"1","votes":{"1435":{"vote.id":"1435","proposition.id":"1151","auteur.id":"44","vote":"0","date":"2013-02-04 20:40:47"}}}},{"1152":{"nb_commentaires":"0","id_commentaire":"1152","observation":"350815","proposition":"0","id_parent":"0","auteur.id":"44","texte":"la gag\u00e9e des garrigues calcaires autour de Montpellier est G. lacaitae. G. granatelli pousse plus \u00e0 l'ouest, dans le Minervois","auteur.nom":"GIROD","auteur.prenom":"Christophe","auteur.courriel":"chrisgir2@yahoo.fr","date":"2013-02-04 20:40:56","nom_sel":"Gagea lacaitae A.Terracc.","proposition_initiale":"0"}}]}}}
/branches/v1.1-helium/services/tests/0.1/observations/masque=G-date=2009.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque=Grand&masque.date=2009","total":8,"depart":"0","limite":"12"},"resultats":{"\"44174\"":{"id_observation":"44174","date_observation":"2009-06-08 00:00:00","date_transmission":"2010-12-21 21:16:09","determination.famille":"Apiaceae","determination.ns":"Orlaya grandiflora (L.) Hoffm.","determination.nn":"46423","determination.nt":"235","determination.referentiel":"bdtfx:v1.01","observateur":"paul.fabre19@gmail.com","id_zone_geo":"INSEE-C:07334","zone_geo":"Les Vans","lieudit":null,"station":"Les Gras","milieu":"Garrigue de thym","auteur.nom":"FABRE","auteur.prenom":"Paul","auteur.id":"11326","mots_cles_texte":null,"commentaire":"sol calcaire","images":[{"id_image":"15653","date":"2009-06-08 12:47:28","hauteur":"2112","largeur":"2816","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000015653XL.jpg"},{"id_image":"15654","date":"2009-06-08 12:47:39","hauteur":"2816","largeur":"2112","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000015654XL.jpg"}]},"\"619435\"":{"id_observation":"619435","date_observation":"2009-06-14 00:00:00","date_transmission":"2011-11-23 08:03:58","determination.famille":"Lamiaceae","determination.ns":"Clinopodium grandiflorum (L.) Kuntze","determination.nn":"18255","determination.nt":"3527","determination.referentiel":"bdtfx:v1.01","observateur":"houdrejj@free.fr","id_zone_geo":"INSEE-C:15073","zone_geo":"Fridefont","lieudit":null,"station":null,"milieu":null,"auteur.nom":"HOUDR\u00c9","auteur.prenom":"Jean-Jacques","auteur.id":"1503","mots_cles_texte":"null","commentaire":null,"images":[{"id_image":"51994","date":"2009-06-14 07:54:10","hauteur":"1602","largeur":"2394","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000051994XL.jpg"}]},"\"619700\"":{"id_observation":"619700","date_observation":"2009-07-12 00:00:00","date_transmission":"2011-11-25 22:37:48","determination.famille":"Plantaginaceae","determination.ns":"Digitalis grandiflora Mill.","determination.nn":"22409","determination.nt":"5321","determination.referentiel":"bdtfx:v1.01","observateur":"houdrejj@free.fr","id_zone_geo":"INSEE-C:73024","zone_geo":"Les Avanchers-Valmorel","lieudit":null,"station":null,"milieu":null,"auteur.nom":"HOUDR\u00c9","auteur.prenom":"Jean-Jacques","auteur.id":"1503","mots_cles_texte":null,"commentaire":null,"images":[{"id_image":"52237","date":"2009-07-12 08:37:18","hauteur":"2008","largeur":"3000","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000052237XL.jpg"},{"id_image":"52238","date":"2009-07-12 08:37:37","hauteur":"3000","largeur":"2008","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000052238XL.jpg"}],"commentaires":[{"2863":{"nb_commentaires":"0","id_commentaire":"2863","observation":"619700","proposition":"0","id_parent":"0","auteur.id":"1503","auteur.nom":"HOUDR\u00c9","auteur.prenom":"Jean-Jacques","auteur.courriel":"houdrejj@free.fr","date":"0000-00-00 00:00:00","nom_sel":"Digitalis grandiflora Mill.","nom_sel_nn":"22409","nom_ret_nn":"22409","proposition_initiale":"1","votes":{"3741":{"vote.id":"3741","proposition.id":"2863","auteur.id":"20207","vote":"1","date":"2013-04-11 22:30:20"}}}}]},"\"722604\"":{"id_observation":"722604","date_observation":"2009-06-10 00:00:00","date_transmission":"2012-02-14 15:47:50","determination.famille":"Apiaceae","determination.ns":"Orlaya grandiflora (L.) Hoffm.","determination.nn":"46423","determination.nt":"235","determination.referentiel":"bdtfx:v1.01","observateur":"mathieumenand@yahoo.fr","id_zone_geo":"INSEE-C:84143","zone_geo":"Venasque","lieudit":null,"station":null,"milieu":null,"auteur.nom":"MENAND","auteur.prenom":"Mathieu","auteur.id":"4967","mots_cles_texte":null,"commentaire":null,"images":[{"id_image":"18638","date":"2009-06-10 17:12:11","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000018638XL.jpg"},{"id_image":"18832","date":"2009-06-10 17:14:26","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000018832XL.jpg"}]},"\"724855\"":{"id_observation":"724855","date_observation":"2009-07-21 00:00:00","date_transmission":"2012-02-14 19:09:59","determination.famille":"Lentibulariaceae","determination.ns":"Pinguicula grandiflora Lam.","determination.nn":"49553","determination.nt":"5654","determination.referentiel":"bdtfx:v1.01","observateur":"avreliane@botaniste-en-herbe.net","id_zone_geo":"INSEE-C:09100","zone_geo":"Couflens","lieudit":null,"station":null,"milieu":null,"auteur.nom":"MAHYEUX","auteur.prenom":"Catherine","auteur.id":"8082","mots_cles_texte":null,"commentaire":null,"images":[{"id_image":"60639","date":"2009-07-21 16:41:58","hauteur":"488","largeur":"650","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000060639XL.jpg"},{"id_image":"60640","date":"2009-07-21 16:27:45","hauteur":"488","largeur":"650","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000060640XL.jpg"}]},"\"1021817\"":{"id_observation":"1021817","date_observation":"2009-06-27 00:00:00","date_transmission":"2013-07-16 12:01:26","determination.famille":"LINACEAE","determination.ns":"Linum alpinum Jacq.","determination.nn":"39369","determination.nt":"3751","determination.referentiel":"bdtfx:v1.01","observateur":"claude.figureau.plantnet@gmail.com","id_zone_geo":"INSEE-C:","zone_geo":"Grand Montrond lelex","lieudit":"","station":"","milieu":"","auteur.nom":"FIGUREAU","auteur.prenom":"Claude","auteur.id":"22878","mots_cles_texte":"Projets coop\u00e9ratifs","commentaire":"","images":[{"id_image":"165267","date":"2009-06-27 12:06:27","hauteur":"450","largeur":"600","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000165267XL.jpg"},{"id_image":"165266","date":"2009-06-27 12:06:51","hauteur":"450","largeur":"600","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000165266XL.jpg"},{"id_image":"165264","date":"2009-06-27 12:06:41","hauteur":"450","largeur":"600","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000165264XL.jpg"}]},"\"1032660\"":{"id_observation":"1032660","date_observation":"2009-04-04 00:00:00","date_transmission":"2013-08-02 18:55:42","determination.famille":"Fabaceae","determination.ns":"Agati grandiflora (L.) Desv.","determination.nn":"284","determination.nt":"2809","determination.referentiel":"bdtxa:1.00","observateur":"valbrasse@orange.fr","id_zone_geo":"INSEE-C:63099","zone_geo":"Ch\u00e2teaugay","lieudit":"","station":"","milieu":"","auteur.nom":"BRASSE","auteur.prenom":"Val\u00e9rie","auteur.id":"11681","mots_cles_texte":"WidgetSaisie,aDeterminer,Projets coop\u00e9ratifs","commentaire":"vue en Guadeloupe","images":[{"id_image":"179865","date":"2009-03-18 16:14:59","hauteur":"2816","largeur":"2112","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000179865XL.jpg"}]},"\"1032667\"":{"id_observation":"1032667","date_observation":"2009-04-04 00:00:00","date_transmission":"2013-08-02 19:05:53","determination.famille":"Aristolochiaceae","determination.ns":"Aristolochia grandiflora Sw.","determination.nn":"876","determination.nt":"273","determination.referentiel":"bdtxa:1.00","observateur":"valbrasse@orange.fr","id_zone_geo":"INSEE-C:63099","zone_geo":"Ch\u00e2teaugay","lieudit":"","station":"","milieu":"","auteur.nom":"BRASSE","auteur.prenom":"Val\u00e9rie","auteur.id":"11681","mots_cles_texte":"WidgetSaisie,aDeterminer,Projets coop\u00e9ratifs","commentaire":"vue en Guadeloupe (fleur)","images":[{"id_image":"179872","date":"2009-03-13 20:30:52","hauteur":"2816","largeur":"2112","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000179872XL.jpg"}]}}}
/branches/v1.1-helium/services/tests/0.1/observations/ObservationsTest.php
New file
0,0 → 1,162
<?php
require_once __DIR__ . '/../ServiceDelPhpUnit.php';
error_reporting(E_ALL);
 
class ObservationsTest extends ServiceDelPhpUnit {
//static function creerUrl($service, $ressources, $parametres = NULL) {
public function testNonExistant() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([0], []),
$url);
// doit retourner une 404
$this->assertEmpty($retour, "Le json doit retourner un tableau vide. Voir : $url");
}
 
public function testExistant() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([1043942], []),
$url);
 
$this->hasKeysAndNotEmpty($retour, ['auteur.id', 'auteur.nom', 'date_transmission', 'observateur', 'id_observation'], $url);
$this->hasKeys($retour, ['auteur.nom'], $url);
$this->assertArrayHasKey('auteur.id', $retour, "attribut auteur.id manquant. Voir : $url");
}
 
 
public function testExistantEtContenu() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([1043942], []),
$url);
 
// from
// "http://www.tela-botanica.org/eflore/del/services/0.1/observations/1048532
$expected = json_decode(file_get_contents("obs-1048532-complete.data.json"), true);
 
self::ignoreNullValuesAndSort($expected);
self::ignoreNullValuesAndSort($retour);
 
// echo implode(',', array_keys($retour['resultats'])) . "\n" . implode(',', array_keys($expected['resultats']));die;
$this->clefsIdentiques($expected, $retour, $url);
$this->assertEquals($expected['resultats'], $retour['resultats'], "Différences dans le tableau pour l'obs 1048532, $url");
}
 
public function testSansImage() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([14203], []),
$url);
// doit retourner une 404
$this->assertEmpty($retour, "Observation sans image devrait ne rien retourner. Voir : $url");
}
 
public function testRechercheNoObs() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([], ['masque.date'=>-1e11]), // 10^11 secondes avant 1970
$url);
// doit retourner une 404
$this->assertEquals(0, $retour['entete']['total'], "Incongruité sur ['entete'][0]. Voir : $url");
$this->assertEmpty($retour['resultats'], "Absence d'observation devrait retourner un result-set vide. Voir : $url");
}
 
/* guidelines pour des tests pérennes:
* - Utiliser ordre=asc pour prendre les enregistrement les plus anciens, moins susceptibles de changer
* - Restreindre par date (évite les date_transmission = NULL, qui, après des GROUP BY affectent le result-set
* - Utiliser moins de "$limite" résultats à cause de la sélection (arbitraire[GROUP-BY])) des obs ayant la même date */
public function testMasque() {
$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([], ['navigation.depart'=>0,'navigation.limite'=>12,'ordre'=>'asc','masque'=>'Grand','masque.date'=>2009]),
$url);
// from
// "http://www.tela-botanica.org/eflore/del/services/0.1/observations?navigation.depart=0&navigation.limite=12&masque=Grand&masque.date=2009&ordre=asc"
$expected = json_decode(file_get_contents("masque=G-date=2009.data.json"), true);
 
self::ignoreNullValuesAndSort($expected);
self::ignoreNullValuesAndSort($retour);
 
// echo implode(',', array_keys($retour['resultats'])) . "\n" . implode(',', array_keys($expected['resultats']));die;
$this->clefsIdentiques($expected, $retour, $url);
$this->assertEquals($expected['resultats'], $retour['resultats'], "Différences dans le tableau, $url");
}
 
public function testType() {
@$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([], ['ordre'=>'asc','masque.type'=>'endiscussion']),
$url);
if(Config::get('nb_commentaires_discussion') != 1) {
printf("can't do test: Config::get('nb_commentaires_discussion') == %d <> 1\n", Config::get('nb_commentaires_discussion'));
return;
}
 
// from
// "http://www.tela-botanica.org/eflore/del/services/0.1/observations?masque.type=endiscussion&ordre=asc"
$expected = json_decode(file_get_contents("masque.type=endiscussion.data.json"), true);
 
self::ignoreNullValuesAndSort($expected);
self::ignoreNullValuesAndSort($retour);
 
// echo implode(',', array_keys($retour['resultats'])) . "\n" . implode(',', array_keys($expected['resultats']));die;
$this->clefsIdentiques($expected, $retour, $url);
$this->assertEquals($expected['resultats'], $retour['resultats'], "Différences dans le tableau, $url");
}
 
 
public function testMasqueEtType() {
@$url = $this->creerUrl('observations');
$i = new Observations();
$retour = $this->consulterDirectJson($i->consulter([], ['navigation.depart'=>0,'navigation.limite'=>12,'ordre'=>'asc','masque'=>'G','masque.type'=>'endiscussion']),
$url);
 
$expected = json_decode(file_get_contents("masque=G-masque.type=endiscussion.data.json"), true);
 
self::ignoreNullValuesAndSort($expected);
self::ignoreNullValuesAndSort($retour);
 
// echo implode(',', array_keys($retour['resultats'])) . "\n" . implode(',', array_keys($expected['resultats']));die;
$this->clefsIdentiques($expected, $retour, $url);
$this->assertEquals($expected['resultats'], $retour['resultats'], "Différences dans le tableau, $url");
}
 
 
 
public function hasKeys($arr, $keys, $url) {
foreach($keys as $k) {
$this->assertArrayHasKey($k, $arr, "attribut {$k} manquant. Voir : $url");
}
}
 
public function hasKeysAndNotEmpty($arr, $keys, $url) {
foreach($keys as $k) {
$this->assertArrayHasKey($k, $arr, "attribut {$k} manquant. Voir : $url");
$this->assertNotEmpty($arr[$k], "attribut {$k} vide. Voir : $url");
}
}
 
static function ignoreNullValuesAndSort(&$arr) {
$arr['resultats'] = array_map('array_filter', $arr['resultats']);
ksort($arr['resultats']);
}
 
public function clefsIdentiques($e, $r, $url) {
$e = array_map(function($a) { return intval(trim($a, '"')); }, array_keys($e['resultats']));
sort($e, SORT_NUMERIC);
$r = array_map(function($a) { return intval(trim($a, '"')); }, array_keys($r['resultats']));
sort($r, SORT_NUMERIC);
$this->assertEquals($e, $r, "Différences dans les clefs du tableau, $url");
}
 
// obs-1048891-masque.ns=Fleur+violette-masque.comune=Arette.data.json
// test la présence des commentaires en général
 
// obs-1047949-masque.ns=Echium+wildpretii+Pearson-masque.commune=Vilaflor.data.json
// test la présence des commentaires parents: "1047949"[commentaires][7730][nb_commentaires] == 1 ?
 
// obs-1043671-masque.ns=plante+rose-masque.departement=38451
// idem
}
/branches/v1.1-helium/services/tests/0.1/observations/obs-1047949-masque.ns=Echium+wildpretii+Pearson-masque.commune=Vilaflor.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque.ns=Echium wildpretii Pearson&masque.commune=Vilaflor","total":1,"depart":0,"limite":10},"resultats":{"\"1047949\"":{"id_observation":"1047949","date_observation":"2013-05-23 00:00:00","date_transmission":"2013-09-19 00:20:22","determination.famille":null,"determination.ns":"Echium wildpretii Pearson ex Hook.f.","determination.nn":null,"determination.nt":null,"observateur":"pdesnos45@free.fr","id_zone_geo":null,"zone_geo":"Vilaflor - Tenerife - Espagne","lieudit":"Caldeira du Teide","station":null,"milieu":"Sable","auteur.nom":"DESNOS","auteur.prenom":"Pat","auteur.id":"22894","mots_cles_texte":"","commentaire":null,"images":[{"id_image":"187515","date":"2013-05-23 12:17:54","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187515XL.jpg","hauteur":"964"},{"id_image":"187516","date":"2013-05-23 12:16:36","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187516XL.jpg","hauteur":"964"},{"id_image":"187517","date":"2013-05-23 12:17:46","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187517XL.jpg","hauteur":"963"},{"id_image":"187518","date":"2013-05-23 12:18:13","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187518XL.jpg","hauteur":"964"},{"id_image":"187519","date":"2013-05-23 12:18:19","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187519XL.jpg","hauteur":"962"},{"id_image":"187520","date":"2013-05-23 12:20:34","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187520XL.jpg","hauteur":"963"}],"commentaires":{"7730":{"nb_commentaires":"1","id_commentaire":"7730","observation":"1047949","proposition":"0","id_parent":"0","auteur.id":"22894","auteur.nom":"DESNOS","auteur.prenom":"Pat","auteur.courriel":"pdesnos45@free.fr","date":"2013-09-20 11:14:33","nom_sel":"Echium wildpretii Pearson ex Hook.f.","proposition_initiale":"1","votes":{"9451":{"vote.id":"9451","proposition.id":"7730","auteur.id":"3c6ef2d001a39ff75df2327b86d3666b","vote":"1","date":"2013-09-20 11:14:33"}}}}}}}
/branches/v1.1-helium/services/tests/0.1/observations/obs-1043671-masque.ns=plante+rose-masque.departement=38451.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque.ns=plante rose&masque.departement=38451","total":1,"depart":0,"limite":10},"resultats":{"\"1043671\"":{"id_observation":"1043671","date_observation":"2013-09-06 00:00:00","date_transmission":"2013-09-09 21:14:23","determination.famille":"","determination.ns":"plante rose","determination.nn":"0","determination.nt":"0","observateur":"mhusson.bonetti@hotmail.fr","id_zone_geo":"INSEE-C:38451","zone_geo":"Saint-Romain-de-Jalionas","lieudit":"","station":"","milieu":"","auteur.nom":"HUSSON","auteur.prenom":"Margaux","auteur.id":"20497","mots_cles_texte":"Projets coop\u00e9ratifs,WidgetSaisie","commentaire":"","images":[{"id_image":"185842","date":"2013-09-06 19:26:55","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000185842XL.jpg","hauteur":"3000"}],"commentaires":{"7502":{"nb_commentaires":"0","id_commentaire":"7502","observation":"1043671","proposition":"0","id_parent":"0","auteur.id":"20497","auteur.nom":"HUSSON","auteur.prenom":"Margaux","auteur.courriel":"mhusson.bonetti@hotmail.fr","date":"2013-09-10 10:43:15","nom_sel":"plante rose","nom_sel_nn":"0","nom_ret_nn":"0","proposition_initiale":"1"},"7506":{"nb_commentaires":"3","id_commentaire":"7506","observation":"1043671","proposition":"0","id_parent":"0","auteur.id":"11851","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.courriel":"herve.goeau@inria.fr","date":"2013-09-10 12:04:58","nom_sel":"Silene flos-cuculi (L.) Clairv.","nom_sel_nn":"75377","proposition_initiale":"0","votes":{"9222":{"vote.id":"9222","proposition.id":"7506","auteur.id":"11851","vote":"0","date":"2013-09-12 16:16:03"},"9260":{"vote.id":"9260","proposition.id":"7506","auteur.id":"13366","vote":"0","date":"2013-09-11 13:00:07"}}},"7560":{"nb_commentaires":"0","id_commentaire":"7560","observation":"1043671","proposition":"0","id_parent":"0","auteur.id":"11851","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.courriel":"herve.goeau@inria.fr","date":"2013-09-12 16:15:59","nom_sel":"Centaurea jacea L.","nom_sel_nn":"15248","proposition_initiale":"0","votes":{"9279":{"vote.id":"9279","proposition.id":"7560","auteur.id":"11851","vote":"1","date":"2013-09-12 16:18:07"}}}}}}}
/branches/v1.1-helium/services/tests/0.1/observations/obs-1048891-masque.ns=Fleur+violette-masque.comune=Arette.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque.ns=Fleur violette&masque.commune=Arette","total":1,"depart":0,"limite":10},"resultats":{"\"1048891\"":{"id_observation":"1048891","date_observation":"0000-00-00 00:00:00","date_transmission":"2013-09-23 18:17:55","determination.famille":"","determination.ns":"Fleur violette","determination.nn":"0","determination.nt":"0","observateur":null,"id_zone_geo":"INSEE-C:64040","zone_geo":"Arette","lieudit":"","station":"","milieu":"","auteur.nom":"","auteur.prenom":"","auteur.id":"0","mots_cles_texte":"aDeterminer,WidgetSaisie,Projets coop\u00e9ratifs","commentaire":"Bonjour, je n'ai pas beaucoup d'information \u00e0 vous donner sur cette fleur car c'est un ami qui m'a donn\u00e9 cette photo \u00e0 d\u00e9terminer et je ne sais pas ce que c'est. Au sol j'ai l'impression que c'est des feuilles de Dryas octopetala. C'est tout ce que je peux vous dire.\n\nMerci","images":[{"id_image":"188102","date":"2012-05-31 08:36:26","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000188102XL.jpg","hauteur":"1920"}],"commentaires":{"7785":{"nb_commentaires":"0","id_commentaire":"7785","observation":"1048891","proposition":"0","id_parent":"0","auteur.id":"0","auteur.nom":"","auteur.prenom":"","auteur.courriel":"","date":"2013-09-24 12:36:53","nom_sel":"Fleur violette","nom_sel_nn":"0","nom_ret_nn":"0","proposition_initiale":"1"},"7786":{"nb_commentaires":"0","id_commentaire":"7786","observation":"1048891","proposition":"0","id_parent":"0","auteur.id":"11851","texte":"je tente ma chance avec cette esp\u00e8ce sans conviction","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.courriel":"herve.goeau@inria.fr","date":"2013-09-24 12:36:53","nom_sel":"Jasione crispa (Pourr.) Samp.","nom_sel_nn":"36248","proposition_initiale":"0"},"7804":{"nb_commentaires":"0","id_commentaire":"7804","observation":"1048891","proposition":"0","id_parent":"0","auteur.id":"10104","texte":"Je pense \u00e0 une V\u00e9ronique.\n4 p\u00e9tales, 2 \u00e9tamines ... mais je ne saurais dire laquelle...","auteur.nom":"BECK","auteur.prenom":"Florent","auteur.courriel":"beckflorent@gmail.com","date":"2013-09-24 22:58:12","nom_sel":"Veronica sp.","proposition_initiale":"0"},"7864":{"nb_commentaires":"0","id_commentaire":"7864","observation":"1048891","proposition":"0","id_parent":"0","auteur.id":"23407","texte":"Suite \u00e0 vos commentaire j'ai vu sur internet cette esp\u00e8ce mais qui m'a l'air bien plus velue","auteur.nom":"LABEYRIE","auteur.prenom":"Vincent","auteur.courriel":"vincent.labeyrie@hotmail.fr","date":"2013-09-26 15:21:43","nom_sel":"Jasione crispa subsp. amethystina (Lag. & Rodr.) Tutin","nom_sel_nn":"83547","proposition_initiale":"0"}}}}}
/branches/v1.1-helium/services/tests/0.1/observations/masque=G-masque.type=endiscussion.data.json
New file
0,0 → 1,0
{"entete":{"masque":"masque=G&masque.type%5Bendiscussion%5D=0","total":349,"depart":0,"limite":12,"href.precedent":null,"href.suivant":"http:\/\/eflore?navigation.depart=12&navigation.limite=12&tri=date_transmission&ordre=asc&masque=G&masque.type%5Bendiscussion%5D=0"},"resultats":{"\"16117\"":{"id_observation":"16117","determination.ns":"Ophrys scolopax Cav.","determination.nt":"30471","determination.nn":"75479","determination.famille":"Orchidaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34131","zone_geo":"Lauret","date_observation":"2009-05-20 00:00:00","mots_cles_texte":"Mes observations,aurelienperonnet","date_transmission":"2009-09-03 22:54:35","auteur.id":"11623","auteur.prenom":"Gr\u00e9goire","auteur.nom":"DUCH\u00c9","observateur":"gregoire@tela-botanica.org","images":[{"id_image":"6969","date":"2009-05-20 18:09:59","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006969XL.jpg"},{"id_image":"6968","date":"2009-05-20 18:09:28","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006968XL.jpg"},{"id_image":"6970","date":"2009-05-20 18:11:22","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006970XL.jpg"},{"id_image":"6971","date":"2009-05-20 18:13:29","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006971XL.jpg"},{"id_image":"6972","date":"2009-05-20 18:14:22","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006972XL.jpg"},{"id_image":"6973","date":"2009-05-20 18:15:15","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006973XL.jpg"},{"id_image":"6974","date":"2009-05-20 18:15:39","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006974XL.jpg"},{"id_image":"6975","date":"2009-05-20 18:16:15","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006975XL.jpg"},{"id_image":"6976","date":"2009-05-20 18:16:50","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006976XL.jpg"},{"id_image":"6977","date":"2009-05-20 18:16:58","hauteur":"4288","largeur":"2848","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006977XL.jpg"},{"id_image":"6978","date":"2009-05-20 18:25:52","hauteur":"2304","largeur":"1759","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006978XL.jpg"}],"commentaires":[{"3911":{"nb_commentaires":"0","id_commentaire":"3911","observation":"16117","proposition":"0","id_parent":"0","auteur.id":"11623","auteur.nom":"DUCH\u00c9","auteur.prenom":"Gr\u00e9goire","auteur.courriel":"gregoire@tela-botanica.org","date":"2013-05-13 11:09:03","nom_sel":"Ophrys scolopax Cav.","nom_sel_nn":"75479","nom_ret_nn":"75479","proposition_initiale":"1","votes":{"5151":{"vote.id":"5151","proposition.id":"3911","auteur.id":"11623","vote":"0","date":"2013-05-13 11:09:07"}}}},{"3912":{"nb_commentaires":"0","id_commentaire":"3912","observation":"16117","proposition":"0","id_parent":"0","auteur.id":"11623","texte":"je crois","auteur.nom":"DUCH\u00c9","auteur.prenom":"Gr\u00e9goire","auteur.courriel":"gregoire@tela-botanica.org","date":"2013-05-13 11:09:03","nom_sel":"Ophrys apifera Huds.","nom_sel_nn":"45064","proposition_initiale":"0","votes":{"5150":{"vote.id":"5150","proposition.id":"3912","auteur.id":"11623","vote":"1","date":"2013-05-13 11:09:06"}}}}]},"\"15282\"":{"id_observation":"15282","determination.ns":"Mespilus germanica L.","determination.nt":"4763","determination.nn":"42397","determination.famille":"Rosaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:30033","zone_geo":"Beauvoisin","lieudit":"La Passeronne","station":"43\u00b043'02.2\"N 04\u00b019'02.2\"E","date_observation":"2009-08-06 00:00:00","date_transmission":"2010-11-04 15:46:31","auteur.id":"7303","auteur.prenom":"Ruddy","auteur.nom":"BENEZET","observateur":"ruddy.benezet@gmail.com","images":[{"id_image":"6410","date":"0000-00-00 00:00:00","hauteur":"1494","largeur":"1528","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006410XL.jpg"},{"id_image":"6409","date":"0000-00-00 00:00:00","hauteur":"1918","largeur":"1719","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006409XL.jpg"},{"id_image":"6408","date":"0000-00-00 00:00:00","hauteur":"2162","largeur":"1371","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006408XL.jpg"},{"id_image":"6407","date":"0000-00-00 00:00:00","hauteur":"3510","largeur":"2226","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006407XL.jpg"},{"id_image":"6406","date":"0000-00-00 00:00:00","hauteur":"3092","largeur":"1975","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006406XL.jpg"},{"id_image":"6405","date":"0000-00-00 00:00:00","hauteur":"3057","largeur":"1989","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006405XL.jpg"},{"id_image":"6404","date":"0000-00-00 00:00:00","hauteur":"1755","largeur":"1275","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006404XL.jpg"},{"id_image":"6881","date":"0000-00-00 00:00:00","hauteur":"1755","largeur":"1275","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006881XL.jpg"},{"id_image":"6882","date":"0000-00-00 00:00:00","hauteur":"2739","largeur":"1509","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006882XL.jpg"},{"id_image":"6883","date":"0000-00-00 00:00:00","hauteur":"2671","largeur":"1995","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006883XL.jpg"},{"id_image":"6884","date":"0000-00-00 00:00:00","hauteur":"3201","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006884XL.jpg"},{"id_image":"6885","date":"0000-00-00 00:00:00","hauteur":"2559","largeur":"2374","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006885XL.jpg"},{"id_image":"6886","date":"0000-00-00 00:00:00","hauteur":"2466","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006886XL.jpg"},{"id_image":"6887","date":"0000-00-00 00:00:00","hauteur":"2829","largeur":"1539","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006887XL.jpg"},{"id_image":"6888","date":"0000-00-00 00:00:00","hauteur":"3254","largeur":"1495","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006888XL.jpg"},{"id_image":"6889","date":"0000-00-00 00:00:00","hauteur":"2933","largeur":"1969","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006889XL.jpg"},{"id_image":"6890","date":"0000-00-00 00:00:00","hauteur":"2994","largeur":"2376","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000006890XL.jpg"}],"commentaires":[{"3937":{"nb_commentaires":"0","id_commentaire":"3937","observation":"15282","proposition":"0","id_parent":"0","auteur.id":"7303","auteur.nom":"BENEZET","auteur.prenom":"Ruddy","auteur.courriel":"ruddy.benezet@gmail.com","date":"2013-05-13 22:49:10","nom_sel":"Mespilus germanica L.","nom_sel_nn":"42397","nom_ret_nn":"42397","proposition_initiale":"1"}},{"3938":{"nb_commentaires":"0","id_commentaire":"3938","observation":"15282","proposition":"0","id_parent":"0","auteur.id":"121","auteur.nom":"BARTH\u00c9L\u00c9MY","auteur.prenom":"Daniel","auteur.courriel":"daniel.barthelemy@cirad.fr","date":"2013-05-13 22:49:10","nom_sel":"Eriobotrya japonica (Thunb.) Lindl.","nom_sel_nn":"24979","proposition_initiale":"0"}}]},"\"167375\"":{"id_observation":"167375","determination.ns":"Carduus tenuiflorus Curtis","determination.nt":"545","determination.nn":"13358","determination.famille":"Asteraceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:84007","zone_geo":"Avignon","lieudit":"Avenue de Tarascon","station":"Culture abandonn\u00e9e","milieu":"Prairie","date_observation":"2009-05-03 00:00:00","mots_cles_texte":"Vigie-Flore,Maille 4202-G","date_transmission":"2010-12-28 23:23:13","auteur.id":"5","auteur.prenom":"Daniel","auteur.nom":"MATHIEU","observateur":"dmathieu@tela-botanica.org","images":[{"id_image":"15974","date":"2009-05-03 10:49:16","hauteur":"800","largeur":"535","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000015974XL.jpg"}],"commentaires":[{"3465":{"nb_commentaires":"0","id_commentaire":"3465","observation":"167375","proposition":"0","id_parent":"0","auteur.id":"5","auteur.nom":"MATHIEU","auteur.prenom":"Daniel","auteur.courriel":"dmathieu@tela-botanica.org","date":"2013-04-28 01:35:25","nom_sel":"Carduus tenuiflorus Curtis","nom_sel_nn":"13358","nom_ret_nn":"13358","proposition_initiale":"1","votes":{"4479":{"vote.id":"4479","proposition.id":"3465","auteur.id":"7163","vote":"0","date":"2013-04-28 01:35:28"}}}},{"3466":{"nb_commentaires":"0","id_commentaire":"3466","observation":"167375","proposition":"0","id_parent":"0","auteur.id":"7163","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 01:35:25","nom_sel":"Carduus pycnocephalus L.","nom_sel_nn":"75026","proposition_initiale":"0"}}]},"\"350814\"":{"id_observation":"350814","determination.ns":"Gagea granatelli (Parl.) Parl.","determination.nt":"6315","determination.nn":"28634","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34129","zone_geo":"Lattes","lieudit":"Mas Mannier","date_observation":"2005-03-01 00:00:00","date_transmission":"2011-08-04 11:20:23","auteur.id":"16995","auteur.prenom":"Christophe","auteur.nom":"BERNIER","observateur":"christophe.bernier@tela-botanica.org","images":[{"id_image":"39928","date":"2005-03-08 17:47:39","hauteur":"1536","largeur":"2048","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039928XL.jpg"},{"id_image":"39927","date":"2005-03-08 17:41:21","hauteur":"1536","largeur":"2048","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039927XL.jpg"}],"commentaires":[{"1142":{"nb_commentaires":"0","id_commentaire":"1142","observation":"350814","proposition":"0","id_parent":"0","auteur.id":"44","texte":"la gag\u00e9e des garrigues calcaires autour de Montpellier est G. lacaitae. G. granatelli pousse plus \u00e0 l'ouest, dans le Minervois","auteur.nom":"GIROD","auteur.prenom":"Christophe","auteur.courriel":"chrisgir2@yahoo.fr","date":"2013-02-04 20:38:15","nom_sel":"Gagea lacaitae A.Terracc.","proposition_initiale":"0"}},{"1150":{"nb_commentaires":"0","id_commentaire":"1150","observation":"350814","proposition":"0","id_parent":"0","auteur.id":"16995","auteur.nom":"BERNIER","auteur.prenom":"Christophe","auteur.courriel":"christophe.bernier@tela-botanica.org","date":"2011-08-04 11:20:23","nom_sel":"Gagea granatelli (Parl.) Parl.","nom_sel_nn":"28634","nom_ret_nn":"28634","proposition_initiale":"1","votes":{"1434":{"vote.id":"1434","proposition.id":"1150","auteur.id":"44","vote":"0","date":"2013-02-04 20:40:41"},"1564":{"vote.id":"1564","proposition.id":"1150","auteur.id":"389470bbca344f8cdc4905d2c791395b","vote":"0","date":"2013-02-14 16:11:42"}}}}]},"\"350815\"":{"id_observation":"350815","determination.ns":"Gagea granatelli (Parl.) Parl.","determination.nt":"6315","determination.nn":"28634","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34129","zone_geo":"Lattes","lieudit":"Mas Mannier","date_observation":"2005-03-08 00:00:00","date_transmission":"2011-08-04 11:20:23","auteur.id":"16995","auteur.prenom":"Christophe","auteur.nom":"BERNIER","observateur":"christophe.bernier@tela-botanica.org","images":[{"id_image":"39930","date":"0000-00-00 00:00:00","hauteur":"1173","largeur":"1969","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039930XL.jpg"},{"id_image":"39929","date":"0000-00-00 00:00:00","hauteur":"1224","largeur":"1944","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000039929XL.jpg"}],"commentaires":[{"1151":{"nb_commentaires":"0","id_commentaire":"1151","observation":"350815","proposition":"0","id_parent":"0","auteur.id":"16995","auteur.nom":"BERNIER","auteur.prenom":"Christophe","auteur.courriel":"christophe.bernier@tela-botanica.org","date":"2011-08-04 11:20:23","nom_sel":"Gagea granatelli (Parl.) Parl.","nom_sel_nn":"28634","nom_ret_nn":"28634","proposition_initiale":"1","votes":{"1435":{"vote.id":"1435","proposition.id":"1151","auteur.id":"44","vote":"0","date":"2013-02-04 20:40:47"}}}},{"1152":{"nb_commentaires":"0","id_commentaire":"1152","observation":"350815","proposition":"0","id_parent":"0","auteur.id":"44","texte":"la gag\u00e9e des garrigues calcaires autour de Montpellier est G. lacaitae. G. granatelli pousse plus \u00e0 l'ouest, dans le Minervois","auteur.nom":"GIROD","auteur.prenom":"Christophe","auteur.courriel":"chrisgir2@yahoo.fr","date":"2013-02-04 20:40:56","nom_sel":"Gagea lacaitae A.Terracc.","proposition_initiale":"0"}}]},"\"374880\"":{"id_observation":"374880","determination.ns":"Carduus tenuiflorus Curtis","determination.nt":"545","determination.nn":"13358","determination.famille":"Asteraceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:49117","zone_geo":"La Dagueni\u00e8re","milieu":"bord de rivi\u00e8re","date_observation":"2004-05-29 00:00:00","date_transmission":"2011-10-08 19:00:59","auteur.id":"2143","auteur.prenom":"Augustin","auteur.nom":"ROCHE","observateur":"augustin.roche@gmail.com","images":[{"id_image":"26280","date":"2004-05-29 16:06:11","hauteur":"1600","largeur":"1200","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000026280XL.jpg"},{"id_image":"26368","date":"2004-05-29 16:06:20","hauteur":"1600","largeur":"1200","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000026368XL.jpg"},{"id_image":"26279","date":"2004-05-29 16:06:27","hauteur":"1600","largeur":"1200","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000026279XL.jpg"}],"commentaires":[{"3435":{"nb_commentaires":"0","id_commentaire":"3435","observation":"374880","proposition":"0","id_parent":"0","auteur.id":"2143","auteur.nom":"ROCHE","auteur.prenom":"Augustin","auteur.courriel":"augustin.roche@gmail.com","date":"2013-04-28 01:14:57","nom_sel":"Carduus tenuiflorus Curtis","nom_sel_nn":"13358","nom_ret_nn":"13358","proposition_initiale":"1","votes":{"4451":{"vote.id":"4451","proposition.id":"3435","auteur.id":"7163","vote":"0","date":"2013-04-28 01:14:57"}}}},{"3436":{"nb_commentaires":"0","id_commentaire":"3436","observation":"374880","proposition":"0","id_parent":"0","auteur.id":"7163","texte":"Les fleurons d\u00e9passent nettement les bract\u00e9es, ces derni\u00e8res \u00e9tant tr\u00e8s aran\u00e9euses sur le dos : il s'agit de C. pycnocephalus. Les confusions sont fr\u00e9quentes entre C. pycnocephalus et tenuiflorus, pour cause de flores indiquant des caract\u00e8res peu fiables (tiges ail\u00e9es ou non, capitules regroup\u00e9s en t\u00eates plus ou moins fournies, couleurs de fleurs). La flore des champs cultiv\u00e9s (Jauzein 1995) est plus fiable.","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 01:19:29","nom_sel":"Carduus pycnocephalus L.","nom_sel_nn":"75026","proposition_initiale":"0","votes":{"4467":{"vote.id":"4467","proposition.id":"3436","auteur.id":"7163","vote":"1","date":"2013-04-28 01:28:42"}}}}]},"\"386183\"":{"id_observation":"386183","determination.ns":"Fritillaria meleagris L.","determination.nt":"6305","determination.nn":"28311","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:37242","zone_geo":"Savigny-en-V\u00e9ron","milieu":"prairie humide","date_observation":"2011-04-07 00:00:00","date_transmission":"2011-11-04 15:07:41","auteur.id":"17106","auteur.prenom":"H\u00e9l\u00e8ne","auteur.nom":"LABALTE","observateur":"labalte.h@wanadoo.fr","images":[{"id_image":"50739","date":"2011-04-07 16:34:51","hauteur":"3264","largeur":"2448","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000050739XL.jpg"}],"commentaires":[{"2634":{"nb_commentaires":"0","id_commentaire":"2634","observation":"386183","proposition":"0","id_parent":"0","auteur.id":"17106","auteur.nom":"LABALTE","auteur.prenom":"H\u00e9l\u00e8ne","auteur.courriel":"labalte.h@wanadoo.fr","date":"2011-11-04 15:07:41","nom_sel":"Fritillaria","nom_sel_nn":"100649","nom_ret_nn":"100649","proposition_initiale":"1","votes":{"3499":{"vote.id":"3499","proposition.id":"2634","auteur.id":"20207","vote":"1","date":"2013-04-10 08:55:13"}}}},{"2635":{"nb_commentaires":"0","id_commentaire":"2635","observation":"386183","proposition":"0","id_parent":"0","auteur.id":"20207","auteur.nom":"SUTTER","auteur.prenom":"Claire","auteur.courriel":"jardin-de-claire@orange.fr","date":"2013-04-10 08:55:07","nom_sel":"Fritillaria meleagris L.","proposition_initiale":"0","votes":{"4708":{"vote.id":"4708","proposition.id":"2635","auteur.id":"121","vote":"1","date":"2013-05-01 16:37:25"},"5434":{"vote.id":"5434","proposition.id":"2635","auteur.id":"2819","vote":"1","date":"2013-05-22 14:47:55"}}}}]},"\"629145\"":{"id_observation":"629145","determination.ns":"Gagea granatelli (Parl.) Parl.","determination.nt":"6315","determination.nn":"28634","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:34270","zone_geo":"Saint-Jean-de-V\u00e9das","milieu":"gazon \u00e0 brachypodes en pente douce, sol l\u00e9g\u00e8rement humide","date_observation":"2012-01-17 00:00:00","mots_cles_texte":"module veiller","date_transmission":"2012-01-18 15:45:47","auteur.id":"18043","auteur.prenom":"L\u00e9o","auteur.nom":"PICHON","observateur":"pichon.leo@gmail.com","commentaire":"10 \u00e0 15 individus observ\u00e9s sur une zone tr\u00e8s localis\u00e9e d'environ 1m\u00e8tre carr\u00e9, assez dense en brachypodes","images":[{"id_image":"55446","date":"2012-01-17 16:03:01","hauteur":"1632","largeur":"1224","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000055446XL.jpg"}],"commentaires":[{"1139":{"nb_commentaires":"0","id_commentaire":"1139","observation":"629145","proposition":"0","id_parent":"0","auteur.id":"44","texte":"la gag\u00e9e des garrigues calcaires autour de Montpellier est G. lacaitae. G. granatelli pousse plus \u00e0 l'ouest, dans le Minervois","auteur.nom":"GIROD","auteur.prenom":"Christophe","auteur.courriel":"chrisgir2@yahoo.fr","date":"2013-02-04 20:37:34","nom_sel":"Gagea lacaitae A.Terracc.","proposition_initiale":"0"}},{"1141":{"nb_commentaires":"0","id_commentaire":"1141","observation":"629145","proposition":"0","id_parent":"0","auteur.id":"18043","auteur.nom":"PICHON","auteur.prenom":"L\u00e9o","auteur.courriel":"pichon.leo@gmail.com","date":"2012-01-18 15:45:47","nom_sel":"Gagea granatelli (Parl.) Parl.","nom_sel_nn":"28634","nom_ret_nn":"28634","proposition_initiale":"1","votes":{"1427":{"vote.id":"1427","proposition.id":"1141","auteur.id":"44","vote":"0","date":"2013-02-04 20:37:38"}}}}]},"\"636442\"":{"id_observation":"636442","determination.ns":"Clematis vitalba L.","determination.nt":"4436","determination.nn":"18235","determination.famille":"Ranunculaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:05061","zone_geo":"Gap","date_observation":"1863-08-16 00:00:00","mots_cles_texte":"Herbier:Inconnu,Liasse:HG8,Herbarium:MPU,Collecteur:inconnu,France","date_transmission":"2012-01-26 09:35:21","auteur.id":"18133","auteur.prenom":"Jean-Pascal","auteur.nom":"MILCENT","observateur":"mpu-jp.milcent@tela-botanica.org","commentaire":"num_part:3;","images":[{"id_image":"55822","date":"2012-01-24 15:05:52","hauteur":"3279","largeur":"2034","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000055822XL.jpg"},{"id_image":"55823","date":"2012-01-24 15:05:52","hauteur":"450","largeur":"921","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000055823XL.jpg"}],"commentaires":[{"2986":{"nb_commentaires":"0","id_commentaire":"2986","observation":"636442","proposition":"0","id_parent":"0","auteur.id":"18133","auteur.nom":"MILCENT","auteur.prenom":"Jean-Pascal","auteur.courriel":"mpu-jp.milcent@tela-botanica.org","date":"2013-04-15 14:18:10","nom_sel":"Clematis vitalba L.","nom_sel_nn":"18235","nom_ret_nn":"18235","proposition_initiale":"1"}},{"2987":{"nb_commentaires":"0","id_commentaire":"2987","observation":"636442","proposition":"0","id_parent":"0","auteur.id":"10229","auteur.nom":"PERONNET","auteur.prenom":"Aur\u00e9lien","auteur.courriel":"aurelien@tela-botanica.org","date":"2013-04-15 14:18:10","nom_sel":"Clematis alpina (L.) Mill.","nom_sel_nn":"75058","proposition_initiale":"0","votes":{"3900":{"vote.id":"3900","proposition.id":"2987","auteur.id":"10229","vote":"1","date":"2013-04-15 14:18:14"}}}}]},"\"719147\"":{"id_observation":"719147","determination.ns":"Sagina apetala Ard.","determination.nt":"2161","determination.nn":"59056","determination.famille":"Caryophyllaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:85166","zone_geo":"Olonne-sur-Mer","date_observation":"2006-08-30 00:00:00","date_transmission":"2012-02-14 15:47:50","auteur.id":"4967","auteur.prenom":"Mathieu","auteur.nom":"MENAND","observateur":"mathieumenand@yahoo.fr","images":[{"id_image":"20637","date":"2008-04-19 17:06:01","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000020637XL.jpg"}],"commentaires":[{"3416":{"nb_commentaires":"0","id_commentaire":"3416","observation":"719147","proposition":"0","id_parent":"0","auteur.id":"4967","auteur.nom":"MENAND","auteur.prenom":"Mathieu","auteur.courriel":"mathieumenand@yahoo.fr","date":"2013-04-28 00:52:47","nom_sel":"Sagina apetala Ard.","nom_sel_nn":"59056","nom_ret_nn":"59056","proposition_initiale":"1"}},{"3417":{"nb_commentaires":"0","id_commentaire":"3417","observation":"719147","proposition":"0","id_parent":"0","auteur.id":"7163","texte":"C'est la sous-esp\u00e8ce erecta \u00e0 s\u00e9pales courts et \u00e9tal\u00e9s","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 00:52:47","nom_sel":"Sagina apetala subsp. erecta F.Herm.","nom_sel_nn":"59061","proposition_initiale":"0","votes":{"4434":{"vote.id":"4434","proposition.id":"3417","auteur.id":"7163","vote":"1","date":"2013-04-28 00:55:36"}}}}]},"\"720368\"":{"id_observation":"720368","determination.ns":"Sagina apetala Ard.","determination.nt":"2161","determination.nn":"59056","determination.famille":"Caryophyllaceae","determination.referentiel":"bdtfx:v1.01","id_zone_geo":"INSEE-C:31022","zone_geo":"Aucamville","date_observation":"2008-04-10 00:00:00","date_transmission":"2012-02-14 15:47:50","auteur.id":"4967","auteur.prenom":"Mathieu","auteur.nom":"MENAND","observateur":"mathieumenand@yahoo.fr","images":[{"id_image":"20627","date":"2008-04-10 12:01:54","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000020627XL.jpg"}],"commentaires":[{"3420":{"nb_commentaires":"0","id_commentaire":"3420","observation":"720368","proposition":"0","id_parent":"0","auteur.id":"4967","auteur.nom":"MENAND","auteur.prenom":"Mathieu","auteur.courriel":"mathieumenand@yahoo.fr","date":"2013-04-28 01:01:07","nom_sel":"Sagina apetala Ard.","nom_sel_nn":"59056","nom_ret_nn":"59056","proposition_initiale":"1"}},{"3421":{"nb_commentaires":"1","id_commentaire":"3421","observation":"720368","proposition":"0","id_parent":"0","auteur.id":"7163","texte":"Il semble qu'il s'agisse de cette esp\u00e8ce : les s\u00e9pales sont grands, les feuilles larges et la plante est apparemment vivace. Mais la photo est difficile \u00e0 interpr\u00e9ter, donc je reste avec un l\u00e9ger doute.","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 01:01:07","nom_sel":"Sagina procumbens L.","nom_sel_nn":"59112","proposition_initiale":"0","votes":{"4442":{"vote.id":"4442","proposition.id":"3421","auteur.id":"7163","vote":"0","date":"2013-04-28 01:06:35"}}}},{"3428":{"nb_commentaires":"0","id_commentaire":"3428","observation":"720368","proposition":"0","id_parent":"0","auteur.id":"7163","texte":"J'ai d'abord pris cette plante pour S. procumbens : voir le commentaires sous ce nom.","auteur.nom":"MERCIER","auteur.prenom":"David","auteur.courriel":"davidpmercier@yahoo.fr","date":"2013-04-28 01:09:34","nom_sel":"Sagina apetala subsp. erecta F.Herm.","nom_sel_nn":"59061","proposition_initiale":"0","votes":{"4443":{"vote.id":"4443","proposition.id":"3428","auteur.id":"7163","vote":"1","date":"2013-04-28 01:09:37"}}}}]},"\"720471\"":{"id_observation":"720471","determination.ns":"Gagea bohemica (Zauschn.) Schult. & Schult.f.","determination.nt":"10135","determination.nn":"28606","determination.famille":"Liliaceae","determination.referentiel":"bdtfx:v1.01","zone_geo":"Cr\u00e8te","date_observation":"2008-04-23 00:00:00","date_transmission":"2012-02-14 15:47:50","auteur.id":"4967","auteur.prenom":"Mathieu","auteur.nom":"MENAND","observateur":"mathieumenand@yahoo.fr","images":[{"id_image":"22952","date":"2008-04-30 14:42:08","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000022952XL.jpg"},{"id_image":"22965","date":"2008-04-30 14:42:35","hauteur":"600","largeur":"800","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000022965XL.jpg"}],"commentaires":[{"1134":{"nb_commentaires":"0","id_commentaire":"1134","observation":"720471","proposition":"0","id_parent":"0","auteur.id":"4967","auteur.nom":"MENAND","auteur.prenom":"Mathieu","auteur.courriel":"mathieumenand@yahoo.fr","date":"2012-02-14 15:47:50","nom_sel":"Gagea bohemica (Zauschn.) Schult. & Schult.f.","nom_sel_nn":"28606","nom_ret_nn":"28606","proposition_initiale":"1","votes":{"1422":{"vote.id":"1422","proposition.id":"1134","auteur.id":"44","vote":"0","date":"2013-02-04 20:36:18"}}}}]}}}
/branches/v1.1-helium/services/tests/0.1/observations/obs-1048532-complete.data.json
New file
0,0 → 1,0
{"id_observation":"1048532","date_observation":"2013-09-19 00:00:00","date_transmission":"2013-09-19 20:48:46","determination.famille":"Berberidaceae","determination.ns":"Mahonia aquifolium (Pursh) Nutt.","determination.nn":"40676","determination.nt":"1317","determination.referentiel":"bdtfx","observateur":"herve.goeau@inria.fr","id_zone_geo":"INSEE-C:","zone_geo":"Sainte-Genevi\u00e8ve-des-Bois","lieudit":"","station":"","milieu":"","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.id":"11851","mots_cles_texte":"plantnet,plantnet-mobile,Projets coop\u00e9ratifs","commentaire":"","images":[{"id_image":"187550","date":"0000-00-00 00:00:00","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187550XL.jpg","hauteur":"563","protocoles_votes":{"3":{"protocole.id":"3","protocole.intitule":"Capitalisation d'images","protocole.descriptif":"photographier en ext\u00e9rieur les organes (feuille, fruit, tronc, etc.) de plantes et transmettre les photos via le Carnet en ligne.","protocole.tag":"Plantnet","votes":{"120014":{"vote.id":"120014","image.id":"187550","auteur.id":"11851","vote":"3","date":"2013-09-19 23:19:10"}}}}},{"id_image":"187551","date":"0000-00-00 00:00:00","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187551XL.jpg","hauteur":"563","protocoles_votes":{"3":{"protocole.id":"3","protocole.intitule":"Capitalisation d'images","protocole.descriptif":"photographier en ext\u00e9rieur les organes (feuille, fruit, tronc, etc.) de plantes et transmettre les photos via le Carnet en ligne.","protocole.tag":"Plantnet","votes":{"120013":{"vote.id":"120013","image.id":"187551","auteur.id":"11851","vote":"3","date":"2013-09-19 23:19:08"}}}}},{"id_image":"187552","date":"0000-00-00 00:00:00","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187552XL.jpg","hauteur":"563","protocoles_votes":{"3":{"protocole.id":"3","protocole.intitule":"Capitalisation d'images","protocole.descriptif":"photographier en ext\u00e9rieur les organes (feuille, fruit, tronc, etc.) de plantes et transmettre les photos via le Carnet en ligne.","protocole.tag":"Plantnet","votes":{"120017":{"vote.id":"120017","image.id":"187552","auteur.id":"11851","vote":"3","date":"2013-09-19 23:19:27"}}}}},{"id_image":"187553","date":"0000-00-00 00:00:00","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187553XL.jpg","hauteur":"563","protocoles_votes":{"3":{"protocole.id":"3","protocole.intitule":"Capitalisation d'images","protocole.descriptif":"photographier en ext\u00e9rieur les organes (feuille, fruit, tronc, etc.) de plantes et transmettre les photos via le Carnet en ligne.","protocole.tag":"Plantnet","votes":{"120016":{"vote.id":"120016","image.id":"187553","auteur.id":"11851","vote":"3","date":"2013-09-19 23:19:16"}}}}},{"id_image":"187554","date":"0000-00-00 00:00:00","binaire.href":"http:\/\/www.tela-botanica.org\/appli:cel-img:000187554XL.jpg","hauteur":"563","protocoles_votes":{"3":{"protocole.id":"3","protocole.intitule":"Capitalisation d'images","protocole.descriptif":"photographier en ext\u00e9rieur les organes (feuille, fruit, tronc, etc.) de plantes et transmettre les photos via le Carnet en ligne.","protocole.tag":"Plantnet","votes":{"120015":{"vote.id":"120015","image.id":"187554","auteur.id":"11851","vote":"3","date":"2013-09-19 23:19:13"}}}}}],"commentaires":{"7725":{"id_commentaire":"7725","observation":"1048532","proposition":"0","id_parent":"0","auteur.id":"11851","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.courriel":"herve.goeau@inria.fr","date":"2013-09-19 23:25:19","nom_sel":"","nom_sel_nn":"0","nom_ret_nn":"0","proposition_initiale":"1"},"7726":{"id_commentaire":"7726","observation":"1048532","proposition":"0","id_parent":"0","auteur.id":"11851","auteur.nom":"GO\u00cbAU","auteur.prenom":"Herv\u00e9","auteur.courriel":"herve.goeau@inria.fr","date":"2013-09-19 23:25:19","nom_sel":"Mahonia aquifolium (Pursh) Nutt.","nom_sel_nn":"40676","proposition_initiale":"0","votes":{"9447":{"vote.id":"9447","proposition.id":"7726","auteur.id":"11851","vote":"1","date":"2013-09-19 23:25:21"}}}}}
/branches/v1.1-helium/services/tests/0.1/ServiceDelPhpUnit.php
New file
0,0 → 1,166
<?php
/**
* Classe contenant des méthodes :
* - d'intialisation des tests,
* - refactorisant le code des tests,
* - facilitant les tests.
*
* @category php 5.4
* @package Tests/Services
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2011, 2013 Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
*/
 
abstract class ServiceDelPhpUnit extends PHPUnit_Framework_TestCase {
 
/** Définir la valeur de cet attribut dans le constructeur de la classe de test.*/
protected $projet = '';
/** Définir la valeur de cet attribut dans le constructeur de la classe de test.*/
protected $service = '';
 
//+------------------------------------------------------------------------------------------------------+
// Intialisation
 
public static function setUpBeforeClass() {
error_reporting(E_ALL);
 
self::chargerFramework();
 
// Enregistrement en première position des autoload de la méthode gérant les classes des services
spl_autoload_register(array(get_class(), 'chargerClasseAuto'));
}
 
public static function chargerClasseAuto($classe) {
if (class_exists($classe)) {
return null;
}
$cheminBase = realpath(__DIR__.'/../../modules/0.1').'/';
$cheminsTests = __DIR__.'/';
$chemins = array($cheminBase, $cheminsTests);
foreach ($chemins as $chemin) {
$chemin = $chemin.$classe.'.php';
if (file_exists($chemin)) {
require_once $chemin;
}
}
}
 
private static function chargerFramework() {
$cheminRacine = realpath(dirname(__FILE__).'/../..').'/';
$framework = $cheminRacine.'framework.php';
if (!file_exists($framework)) {
$e = "Veuillez paramétrer l'emplacement et la version du Framework dans le fichier $framework";
trigger_error($e, E_USER_ERROR);
} else {
// Inclusion du Framework
require_once $framework;
 
// Ajout d'information concernant cette application
Framework::setCheminAppli($cheminRacine);// Obligatoire
}
}
 
//+------------------------------------------------------------------------------------------------------+
// Refactorisation
protected function consulterJson($ressources, $parametres) {
$retourJson = $this->consulterBrut($ressources, $parametres);
$retour = json_decode($retourJson, true);
$url = $this->creerUrl($ressources, $parametres);
$this->assertEquals(JSON_ERROR_NONE, json_last_error(), "Le json contient des erreurs qui bloquent le décodage. Voir : $url\n".print_r($retourJson, true));
return $retour;
}
 
public function consulterDirectJson($json, $url) {
$retour = json_decode($json, true);
$this->assertEquals(JSON_ERROR_NONE,
json_last_error(),
"Le json contient des erreurs qui bloquent le décodage. Voir : $url\n" .
print_r($json, true));
return $retour;
}
 
protected function consulterBrut($ressources, $parametres) {
array_unshift($ressources, $this->service);
array_unshift($ressources, $this->projet);
$projets = new Determinations();
$retourJson = $projets->consulter($ressources, $parametres);
return $retourJson;
}
 
 
protected function creerUrl($ressources, $parametres = []) {
$version = '';
$ressourcesUrl = array();
foreach ($ressources as $ressource) {
$ressourcesUrl[] = $ressource;
}
$ressourcesUrl = count($ressourcesUrl) > 0 ? '/'.implode('/', $ressourcesUrl) : '';
 
$parametresUrl = '';
if (count($parametres) > 0) {
foreach ($parametres as $cle => $valeur) {
$parametresUrl[] = $cle.'='.rawurlencode($valeur);
}
$parametresUrl = '?'.implode('&', $parametresUrl);
}
 
$url = Config::get('url_service').'/'.$version.$this->service.$ressourcesUrl.$parametresUrl;
return $url;
}
 
//+------------------------------------------------------------------------------------------------------+
// Méthodes facilitant les tests
 
/**
* Récupère un bouchon de classe abstraite.
* Comment l'utiliser :
* $classeAstraite = $this->getClasseAbstraite('MaClasse', array('param1', 'param2'));
* $foo = $classeAstraite->methodeConcretePublique();
*
* @param String $classeNom Le nom de la classe
* @param Array $parametres Les paramètres à passer au constructeur.
* @return Object Le bouchon de la classe abstraite
*/
public function getClasseAbstraite($classeNom, Array $parametres) {
$efloreScript = $this->getMockForAbstractClass($classeNom, $parametres);
return $efloreScript;
}
 
/**
* Récupère une méthode privée d'une classe pour tester/documenter.
* Comment l'utiliser :
* MyClass->foo():
* $cls = new MyClass();
* $foo = self::getPrivateMethode($cls, 'foo');
* $foo->invoke($cls, $...);
*
* @param object $objet Une instance de votre classe
* @param string $methode Le nom de la méthode private
* @return ReflectionMethod La méthode demandée
*/
public static function getMethodePrivee($objet, $nomMethode) {
$classe = new ReflectionClass($objet);
$methode = $classe->getMethod($nomMethode);
$methode->setAccessible(true);
return $methode;
}
 
/**
* Récupère une méthode protégée d'une classe pour tester/documenter.
* Comment l'utiliser :
* MyClass->foo():
* $cls = new MyClass();
* $foo = self::getProtectedMethode($cls, 'foo');
* $foo->invoke($cls, $...);
* @param object $objet Une instance de votre classe
* @param string $methode Le nom de la méthode protected
* @return ReflectionMethod La méthode demandée
*/
public static function getMethodeProtegee($objet, $nomMethode) {
return self::getMethodePrivee($objet, $nomMethode);
}
}
?>
/branches/v1.1-helium/services/tests/0.1/images/ImagesTest.php
New file
0,0 → 1,45
<?php
/*
* @author Grégoire DUCHE <gregoire@tela-botanica.org>
* @author Raphaël Droz <raphael@tela-botanica.org>
* @copyright Copyright (c) 2012, 2013 Tela Botanica (accueil@tela-botanica.org)
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
*
* Classe de test pour /del/services/0.1/images
*/
require_once dirname(__FILE__).'/../ServiceDelPhpUnit.php';
 
class ImagesTest extends ServiceDelPhpUnit {
 
const nbResultatsGregoire = 409;
public function testCoherenceResultat() {
$url = $this->creerUrl('images');
$i = new Images();
$retour = $this->consulterDirectJson($i->consulter([], []),
$url);
 
$this->assertArrayHasKey('entete', $retour, "Le json ne contient pas d'attribut : entete. Voir : $url");
$this->assertArrayHasKey('resultats', $retour, "Le json ne contient pas d'attribut : resultat. Voir : $url");
}
 
public function testNbObsEnFonctionDeLauteur() {
$parametres = ['masque.auteur' => 'gregoire'];
$url = $this->creerUrl('images', $parametres);
$i = new Images();
$retour = $this->consulterDirectJson($i->consulter([], $parametres),
$url);
$this->assertEquals(self::nbResultatsGregoire,
$retour['entete']['total'],
'Le total des observations de Grégoire n\'est pas égal à '.self::nbResultatsGregoire);
}
 
 
// del:services:0.1/images?navigation.depart=0&navigation.limite=12&tri=date_observation&ordre=asc
// doit avoir 12 éléments
}
?>
/branches/v1.1-helium/services/.
New file
Property changes:
Added: svn:ignore
+framework.php