Subversion Repositories eFlore/Applications.del

Compare Revisions

No changes between revisions

Ignore whitespace Rev 2170 → Rev 2171

/branches/v1.11-magnesium/services/bibliotheque/ParametresFiltrage.php
New file
0,0 → 1,485
<?php
// declare(encoding='UTF-8');
/**
* Classe contenant des méthodes de filtrage/formatage des paramètres de recherche passés dans l'URL.
*
* Cette classe filtre et formate les parametres passées dans l'URL et construit un tableau associatif contenant
* le résultat des filtrages/formatages et les infos nécessaires à la construction d'une requête SQL.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ParametresFiltrage {
 
const APPLI_IMG = 'IMG';
const APPLI_OBS = 'OBS';
 
const LISTE_OBS_MAX_RESULT_LIMIT = 1000;
const LISTE_OBS_MAX_ID_OBS = 10e7;
const LISTE_OBS_MAX_BDTFX_NT = 1000000; // SELECT MAX(num_taxonomique) FROM bdtfx_v2_00; // 44378 + 1000
const LISTE_OBS_MAX_BDTFX_NN = 1000000; // SELECT MAX(num_nom) FROM bdtfx_v2_00;// 120816 + 10000
 
private $conteneur;
private $contexte;
private $parametres = array();
private $parametresFiltres = array();
private $appli;
 
public function __construct($conteneur) {
$this->conteneur = $conteneur;
$this->contexte = $this->conteneur->getContexte();
$this->parametres = $this->contexte->getQS();
}
 
private function etreAppliImg() {
return $this->appli === 'IMG' ? true : false;
}
 
private function etreAppliObs() {
return $this->appli === 'OBS' ? true : false;
}
 
public function filtrerUrlParamsAppliImg() {
$this->appli = self::APPLI_IMG;
$this->maintenirCompatibilitesParametres();
 
$parametresAutorises = $this->conteneur->getParametreTableau('images.masques_possibles');
$this->eliminerParametresInconnus($parametresAutorises);
 
$this->repartirMasqueGeneral();
 
$paramsParDefaut = $this->conteneur->getParametreTableau('images.parametres_valeurs_defaut');
$this->definirParametresDefauts($paramsParDefaut);
 
$this->filtrerUrlParamsGeneraux();
 
$trisPossibles = $this->conteneur->getParametreTableau('appli_img.tris_possibles');
$this->detruireParametreInvalide('tri', $trisPossibles);
$formatsImgPossibles = $this->conteneur->getParametreTableau('appli_img.img_formats_possibles');
$this->detruireParametreInvalide('format', $formatsImgPossibles);
$this->filtrerProtocole();
 
$this->supprimerParametresFiltresInvalides();
return $this->parametresFiltres;
}
 
public function filtrerUrlParamsAppliObs() {
$this->appli = self::APPLI_OBS;
$this->maintenirCompatibilitesParametres();
 
$parametresAutorises = $this->conteneur->getParametreTableau(('observations.masques_possibles'));
$this->eliminerParametresInconnus($parametresAutorises);
 
$this->repartirMasqueGeneral();
 
$paramsParDefaut = $this->conteneur->getParametreTableau('observations.parametres_valeurs_defaut');
$this->definirParametresDefauts($paramsParDefaut);
 
$this->filtrerUrlParamsGeneraux();
 
$trisPossibles = $this->conteneur->getParametreTableau('appli_obs.tris_possibles');
$this->detruireParametreInvalide('tri', $trisPossibles);
 
$this->supprimerParametresFiltresInvalides();
return $this->parametresFiltres;
}
 
private function maintenirCompatibilitesParametres() {
$this->renommerParametres();
 
if ($this->etreAppliImg() && !isset($this->parametres['masque.tag_del']) && isset($this->parametres['masque.tag'])) {
$this->parametres['masque.tag_del'] = $this->parametres['masque.tag'];
unset($this->parametres['masque.tag']);
}
if ($this->etreAppliobs() && !isset($this->parametres['masque.tag_cel']) && isset($this->parametres['masque.tag'])) {
$this->parametres['masque.tag_cel'] = $this->parametres['masque.tag'];
unset($this->parametres['masque.tag']);
}
}
 
private function renommerParametres() {
$renomages = array('masque.tag_pictoflora' => 'masque.tag_del');
foreach ($renomages as $ancienNom => $nouveauNom) {
if (isset($this->parametres[$ancienNom])) {
$this->parametres[$nouveauNom] = $this->parametres[$ancienNom];
unset($this->parametres[$ancienNom]);
}
}
}
 
/**
* Suppression de toutes les clefs NON présentes dans le paramètre de config : images|observations.masques_possibles
* @param array $parametresAutorises tableau des paramètres pouvant être utilisé dans l'url.
*/
private function eliminerParametresInconnus(Array $parametresAutorises = null) {
if ($parametresAutorises) {
$this->parametres = array_intersect_key($this->parametres, array_flip($parametresAutorises));
}
}
 
/**
* Les paramètres par défaut sont écrasés par ceux passés dans l'url.
*
* @param array $paramsParDefaut tableau associatif des paramètres d'url par défaut
*/
private function definirParametresDefauts(Array $paramsParDefaut) {
$this->parametres = array_merge($paramsParDefaut, $this->parametres);
}
 
/**
* "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.
*/
private function repartirMasqueGeneral() {
if (isset($this->parametres['masque']) && !empty(trim($this->parametres['masque']))) {
$masqueGeneral = trim($this->parametres['masque']);
$masquesDetailCles = array('masque.auteur', 'masque.departement', 'masque.commune', 'masque.id_zone_geo',
'masque.ns', 'masque.famille', 'masque.date', 'masque.genre', 'masque.milieu');
 
// Suppression de la génération de SQL du masque général sur les champ spécifiques qui sont traités avec leur valeur propre.
foreach ($masquesDetailCles as $cle) {
if (isset($this->parametres[$cle]) === false) {
$this->parametres[$cle] = $masqueGeneral;
$this->parametresFiltres['_parametres_condition_or_'][] = $cle;
}
}
}
}
 
/**
* 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
*
* 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 '_'
*/
// TODO: ajouter un filtre sur le masque (général)
private function filtrerUrlParamsGeneraux() {
$this->detruireParametreInvalide('ordre', $this->conteneur->getParametreTableau('valeurs_ordre'));
$this->detruireParametreInvalide('masque.referentiel', $this->conteneur->getParametreTableau('valeurs_referentiel'));
 
$this->filtrerNavigationLimite();
$this->filtrerNavigationDepart();
$this->filtrerDepartement();
$this->filtrerDate();
$this->filtrerNn();
$this->filtrerNt();
 
$parametresATrimer = array('masque', 'masque.ns', 'masque.genre', 'masque.espece', 'masque.auteur', 'masque.milieu');
$this->supprimerCaracteresInvisibles($parametresATrimer);
 
$this->filtrerFamille();
$this->filtrerPays();
$this->filtrerIdZoneGeo();
$this->filtrerCommune();
$this->filtrerType();
 
$this->filtrerPnInscrits();
 
$this->filtrerTagCel();
$this->filtrerTagDel();
}
 
 
/**
* Supprime l'index du tableau des paramètres si sa valeur ne correspond pas
* au spectre passé par $values.
*/
private function detruireParametreInvalide($index, Array $valeursAutorisees) {
if (array_key_exists($index, $this->parametres)) {
if (!in_array($this->parametres[$index], $valeursAutorisees)) {
unset($this->parametres[$index]);
} else {
$this->parametresFiltres[$index] = $this->parametres[$index];
}
}
}
 
private function filtrerNavigationLimite() {
if (isset($this->parametres['navigation.limite'])) {
$options = array(
'options' => array(
'default' => null,
'min_range' => 1,
'max_range' => self::LISTE_OBS_MAX_RESULT_LIMIT));
$paramFiltre = filter_var($this->parametres['navigation.limite'], FILTER_VALIDATE_INT, $options);
$this->parametresFiltres['navigation.limite'] = $paramFiltre;
}
}
 
private function filtrerNavigationDepart() {
if (isset($this->parametres['navigation.depart'])) {
$options = array(
'options' => array(
'default' => null,
'min_range' => 0,
'max_range' => self::LISTE_OBS_MAX_ID_OBS));
$paramFiltre = filter_var($this->parametres['navigation.depart'], FILTER_VALIDATE_INT, $options);
$this->parametresFiltres['navigation.depart'] = $paramFiltre;
}
}
 
/**
* STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
* accept leading 0 ?
* TODO; filter patterns like 555.
*
* @return type
*/
private function filtrerDepartement() {
if (isset($this->parametres['masque.departement'])) {
$dept = $this->parametres['masque.departement'];
$paramFiltre = null;
if (preg_match('/^(\d{2}|\d{3}|2a|2b)$/i', $dept) != 0) {
$paramFiltre = is_numeric($dept) ? str_pad($dept, 5, '_') : $dept;
} else {
$dept_translit = iconv('UTF-8', 'ASCII//TRANSLIT', $dept);
$dpt_chaine = strtolower(str_replace(' ', '-', $dept_translit));
$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
$dpt_numero = $this->conteneur->getParametre($dpt_chaine);
if (!empty($dpt_numero)) {
$paramFiltre = str_pad($dpt_numero, 5, '_');
}
}
$this->parametresFiltres['masque.departement'] = $paramFiltre;
}
}
 
private function filtrerDate() {
if (isset($this->parametres['masque.date'])) {
$date = $this->parametres['masque.date'];
$paramFiltre = null;
if (preg_match('/^\d{4}$/', $date)) {
$paramFiltre = $date;
} else if (strpos($date, '/') !== false) {
// Format d'entrée DEL : jj/mm/yyyy
list($jour, $mois, $annee) = explode('/', $date);
$paramFiltre = "$annee-$mois-$jour";
} else if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
$paramFiltre = $date;
}
$this->parametresFiltres['masque.date'] = $paramFiltre;
}
}
 
private function filtrerNn() {
if (isset($this->parametres['masque.nn'])) {
$options = array(
'options' => array(
'default' => null,
'min_range' => 0,
'max_range' => self::LISTE_OBS_MAX_BDTFX_NN));
$paramFiltre = filter_var($this->parametres['masque.nn'], FILTER_VALIDATE_INT, $options);
$this->parametresFiltres['masque.nn'] = $paramFiltre;
}
}
 
private function filtrerNt() {
if (isset($this->parametres['masque.nt'])) {
$options = array(
'options' => array(
'default' => null,
'min_range' => 0,
'max_range' => self::LISTE_OBS_MAX_BDTFX_NT));
$paramFiltre = filter_var($this->parametres['masque.nt'], FILTER_VALIDATE_INT, $options);
$this->parametresFiltres['masque.nt'] = $paramFiltre;
}
}
 
private function supprimerCaracteresInvisibles(Array $liste_params) {
foreach ($liste_params as $param) {
if (isset($this->parametres[$param])) {
$this->parametresFiltres[$param] = trim($this->parametres[$param]);
}
}
}
 
private function filtrerFamille() {
if (isset($this->parametres['masque.famille'])) {
// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
$familleTranslit = iconv('UTF-8', 'ASCII//TRANSLIT',$this->parametres['masque.famille']);
$paramFiltre = preg_replace('/[^a-zA-Z %_]/', '', $familleTranslit);
$this->parametresFiltres['masque.famille'] = $paramFiltre;
}
}
 
// Idem pour id_zone_geo qui mappait à ce_zone_geo:
private function filtrerIdZoneGeo() {
if (isset($this->parametres['masque.id_zone_geo'])) {
if (preg_match('/^(INSEE-C:\d{5}|\d{2})$/', $this->parametres['masque.id_zone_geo'])) {
$paramFiltre = $this->parametres['masque.id_zone_geo'];
$this->parametresFiltres['masque.id_zone_geo'] = $paramFiltre;
}
}
}
// Idem pour id_zone_geo qui mappait à ce_zone_geo:
private function filtrerPays() {
if (isset($this->parametres['masque.pays'])) {
// une liste de pays séparés par des virgules est acceptable
if (preg_match('/^([a-zA-Z]{2},)*[a-zA-Z]{2}$/', $this->parametres['masque.pays'])) {
// Nettoyage d'une virgule terminale au cas ou
$this->parametres['masque.pays'] = rtrim($this->parametres['masque.pays'], ',');
$paramFiltre = $this->parametres['masque.pays'];
$this->parametresFiltres['masque.pays'] = $paramFiltre;
}
}
}
 
protected function filtrerPnInscrits() {
if (isset($this->parametres['masque.pninscritsseulement'])) {
if ($this->parametres['masque.pninscritsseulement'] == 1) {
$this->parametresFiltres['masque.pninscritsseulement'] = 1;
}
}
}
 
/** 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.
*/
private function filtrerCommune() {
if (isset($this->parametres['masque.commune'])) {
$paramFiltre = str_replace(array('-',' '), '_', $this->parametres['masque.commune']);
$this->parametresFiltres['masque.commune'] = $paramFiltre;
}
}
 
private function filtrerTagCel() {
if (isset($this->parametres['masque.tag_cel'])) {
$this->parametresFiltres['masque.tag_cel'] = $this->construireTableauTags($this->parametres['masque.tag_cel'], 'OR', ',');
} else if (isset($this->parametres['masque'])) {
$this->parametresFiltres['masque.tag_cel'] = $this->construireTableauTags($this->parametres['masque'], 'AND', ' ');
$this->parametresFiltres['_parametres_condition_or_'][] = 'masque.tag_cel';
}
}
 
private function filtrerTagDel() {
if (isset($this->parametres['masque.tag_del'])) {
$this->parametresFiltres['masque.tag_del'] = $this->construireTableauTags($this->parametres['masque.tag_del'], 'OR', ',');
} else if (isset($this->parametres['masque'])) {
$this->parametresFiltres['masque.tag_del'] = $this->construireTableauTags($this->parametres['masque'], 'AND', ' ');
$this->parametresFiltres['_parametres_condition_or_'][] = 'masque.tag_del';
}
}
 
 
/**
* 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 $operateur_par_defaut: "AND" ou "OR"
* @param $separateur_additionnel: séparateur de mots:
*/
public function construireTableauTags($str = null, $operateur_par_defaut, $separateur_additionnel = ',') {
if (!$str) return;
$op = $this->definirOperateurParDefaut($str, $operateur_par_defaut);
 
$mots = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
if ($separateur_additionnel) {
foreach ($mots as $index => $mot) {
$mot = trim($mot);
$mots_separes = preg_split("/$separateur_additionnel/", $mot, -1, PREG_SPLIT_NO_EMPTY);
$mots[$index] = array_shift($mots_separes);
$mots = array_merge($mots, $mots_separes);
}
}
$mots = array_filter($mots);
return array($op => $mots);
}
 
private function definirOperateurParDefaut($str, $operateur_par_defaut) {
$op = $operateur_par_defaut;
if (preg_match('/\b(ET|AND)\b/', $str)) {
$op = 'AND';
} else if(preg_match('/\b(OU|OR)\b/', $str)) {
$op = 'OR';
}
return $op;
}
 
// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees', 'monactivite']
private function filtrerType() {
if (isset($this->parametres['masque.type'])) {
$typesArray = explode(';', $this->parametres['masque.type']);
$typesFiltres = array_filter($typesArray);
$typesAutorises = $this->conteneur->getParametreTableau('valeurs_type');
$typesValides = array_intersect($typesFiltres, $typesAutorises);
$paramFiltre = array_flip($typesValides);
$this->parametresFiltres['masque.type'] = $paramFiltre;
}
}
 
private function filtrerProtocole() {
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
if (!isset($this->parametres['protocole']) || !is_numeric($this->parametres['protocole'])) {
$this->parametresFiltres['protocole'] = $this->conteneur->getParametre('appli_img.protocole_defaut');
} else {
$this->parametresFiltres['protocole'] = intval($this->parametres['protocole']);
}
}
 
private function supprimerParametresFiltresInvalides() {
// Suppression des NULL, FALSE et '', mais pas des 0, d'où l'utilisation de 'strlen'.
// La fonction 'strlen' permet de supprimer les NULL, FALSE et chaines vides mais gardent les valeurs 0 (zéro).
// Les valeurs spéciales contenant des tableaux (tag, _parametres_condition_or_) ne sont pas prise en compte
foreach ($this->parametresFiltres as $cle => $valeur) {
if (is_array($valeur) || strlen($valeur) !== 0) {
$this->parametresFiltres[$cle] = $valeur;
}
}
}
}
/branches/v1.11-magnesium/services/bibliotheque/Conteneur.php
New file
0,0 → 1,179
<?php
// declare(encoding='UTF-8');
/**
* Le conteneur encapsule l'instanciation des classes ainsi que la récupération des paramètres depuis l'url ou
* les fichiers de configuration
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
//TODO : initialiser tous les objets dans le conteneur
//TODO : créer un tableau de partage
class Conteneur {
 
protected $parametres;
protected $partages = array();
 
/**
* 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;
}
 
/**
* 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;
}
 
/**
* Obtenir un paramètre depuis le tableau de paramètres ou depuis le fichier de config
* et le transformer en tableau s'il est de la forme : "cle=valeur,cle=valeur,..."
* @param String $cle le nom du paramètre
* @return la valeur du paramètre
*/
public function getParametreTableau($cle) {
$tableau = array();
$parametre = $this->getParametre($cle);
if (empty($parametre) === false) {
$tableauPartiel = explode(',', $parametre);
foreach ($tableauPartiel as $champ) {
if (strpos($champ, '=') === false) {
$tableau[] = trim($champ);
} else {
list($cle, $val) = explode('=', $champ);
$tableau[trim($cle)] = trim($val);
}
}
}
return $tableau;
}
 
/**
* Enregistrer la valeur d'un paramètre
* */
public function setParametre($cle, $valeur) {
$this->parametres[$cle] = $valeur;
}
 
//--------------------------------------------------------------------------------------------------------
// TODO : Supprimer le chargement de configuration présent dans des fichiers séparés.
/**
* Charger la configuration depuis un fichier .ini.
*
* @param String $fichier le nom du fichier de configuration
* */
public function chargerConfiguration($fichier) {
$cheminConfigurations = Config::get('chemin_configurations');
if ($cheminConfigurations == null || $cheminConfigurations == '') {
$message = "Le parametre de configuration 'chemin_configurations' n'est pas défini.";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
 
$cheminConfigService = $cheminConfigurations.$fichier;
if (file_exists($cheminConfigService) === false) {
$nomClasse = get_class($this);
$message = "Classe $nomClasse : le fichier de configuration du service est introuvable : $cheminConfigService ";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
 
Config::charger($cheminConfigService);
}
 
public function getBdd() {
if (!isset($this->partages['Bdd'])){
$this->partages['Bdd'] = new Bdd();
}
return $this->partages['Bdd'];
}
 
public function getRestClient() {
if (!isset($this->partages['restClient'])){
$this->partages['restClient'] = new RestClient();
}
return $this->partages['restClient'];
}
 
public function getUrl($base) {
return new Url($base);
}
 
public function getControleAcces() {
if (!isset($this->partages['controleAcces'])) {
$this->partages['controleAcces'] = new ControleAcces($this);
}
return $this->partages['controleAcces'];
}
 
public function getNavigation() {
if (!isset($this->partages['navigation'])) {
$this->partages['navigation'] = new Navigation($this);
}
return $this->partages['navigation'];
}
 
public function getContexte() {
if (!isset($this->partages['contexte'])) {
$this->partages['contexte'] = new Contexte($this, $_SERVER, $_GET, $_POST, $_SESSION, $_COOKIE);
}
return $this->partages['contexte'];
}
 
public function getUtilisateur() {
if (!isset($this->partages['utilisateur'])) {
$this->partages['utilisateur'] = new GestionUtilisateur($this);
}
return $this->partages['utilisateur'];
}
 
public function getSyndicationOutils() {
if (!isset($this->partages['syndicationOutils'])) {
$this->partages['syndicationOutils'] = new SyndicationOutils($this);
}
return $this->partages['syndicationOutils'];
}
 
public function getParametresFiltrage() {
if (!isset($this->partages['parametresFiltrage'])) {
$this->partages['parametresFiltrage'] = new ParametresFiltrage($this);
}
return $this->partages['parametresFiltrage'];
}
 
public function getSql() {
if (!isset($this->partages['sql'])) {
$this->partages['sql'] = new Sql($this);
}
return $this->partages['sql'];
}
public function getSquelettePhp() {
if (!isset($this->partages['SquelettePhp'])) {
$this->partages['SquelettePhp'] = new SquelettePhp();
}
return $this->partages['SquelettePhp'];
}
public function getMessagerie() {
if (!isset($this->partages['Messagerie'])) {
$this->partages['Messagerie'] = new TelaBotanica\Del\Commun\Messagerie($this);
}
return $this->partages['Messagerie'];
}
}
/branches/v1.11-magnesium/services/bibliotheque/GestionUtilisateur.php
New file
0,0 → 1,444
<?php
// declare(encoding='UTF-8');
/**
* Contient les méthodes permettant d'identifier l'utilisateur de l'application DEL.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class GestionUtilisateur {
 
protected $conteneur;
protected $contexte;
protected $bdd;
/** contient le jeton SSO décodé, si une authentification a eu lieu avec succès */
protected $jetonDecode;
 
protected $utilisateur = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->contexte = $this->conteneur->getContexte();
$this->chargerUtilisateur();
}
 
/**
* Charge des données utilisateur : un vrai profil si l'utilisateur est identifié,
* un profil "anonyme" sinon
*/
private function chargerUtilisateur() {
$this->demarrerSession();
$infos = $this->getUtilisateurIdentifie();
$this->utilisateur = ($infos == null) ? $this->getUtilisateurAnonyme() : $infos;
}
 
private function demarrerSession() {
if (session_id() == '') {
// TODO : modifier ce test lors du passage en php 5.4
// TODO : expliquer pourquoi SVP :)
session_start();
}
}
 
// rétrocompat avec un vieux machin
public function getIdAnonymeTemporaire() {
return $this->utilisateur['session_id'];
}
 
/**
* Retourne l'utilisateur en cours, chargé dans le constructeur
* par getutilisateurIdentifié()
*/
public function getUtilisateur() {
return $this->utilisateur;
}
 
/**
* Recherche un jeton SSO dans l'entête HTTP "Authorization", vérifie ce
* jeton auprès de l'annuaire et en cas de succès charge les informations
* de l'utilisateur associé; si c'est la première fois que l'utilisateur
* utilise DeL, crée un profil local dans del_utilisateur_infos; si le
* profil a changé depuis la dernière connexion, le met à jour ainsi que
* les coordonnées dans les commentaires
*
* @return Array un profil utilisateur ou null
*/
public function getUtilisateurIdentifie() {
$utilisateur = null;
// lecture du jeton
$jeton = $this->lireJetonEntete();
if ($jeton != null) {
// validation par l'annuaire
$valide = $this->verifierJeton($jeton);
if ($valide === true) {
// décodage du courriel utilisateur depuis le jeton
$this->jetonDecode = $this->decoderJeton($jeton);
//var_dump($this->jetonDecode);
if ($this->jetonDecode != null && $this->jetonDecode["sub"] != "") {
// récupération de l'utilisateur
$courriel = $this->jetonDecode["sub"];
$utilisateur = $this->recupererUtilisateurEnBdd($courriel);
// Si l'utilisateur existe
if ($utilisateur != null) {
// profil changé ?
if ($this->profilAChange($utilisateur)) {
// mettre à jour les coordonnées dans le profil local
$this->mettreAJourProfilLocal();
// mettre à jour auteur commentaires
$this->mettreAJourCoordonneesDansCommentaires();
// relire infos
$utilisateur = $this->recupererUtilisateurEnBdd($courriel);
}
} else {
// première connexion à DeL
// initialiser infos
$this->initialiserInfosUtilisateur($this->jetonDecode['id']); // rétrocompat; le paramètre devrait être implicite
// relire infos
$utilisateur = $this->recupererUtilisateurEnBdd($courriel);
}
$utilisateur = $this->completerInfosUtilisateur($utilisateur);
}
}
}
 
return $utilisateur;
}
 
/**
* Retourne true si le profil local stocké dans del_utilisateur_infos
* n'est plus à jour par rapport aux informations du jeton SSO; si le
* jeton est vide, retourne false pour éviter de tout casser
*/
protected function profilAChange($infosDUI) {
$aChange = false;
if ($this->jetonDecode != null) {
$aChange = ($this->jetonDecode['nom'] != $infosDUI['nom'])
|| ($this->jetonDecode['intitule'] != $infosDUI['intitule'])
|| ($this->jetonDecode['prenom'] != $infosDUI['prenom']);
}
//var_dump($aChange);
return $aChange;
}
 
/**
* Met à jour del_utilisateur_infos en fonction des informations
* contenues par le jeton SSO; si ce dernier est vide, ne fait
* rien (boulette-proof)
*/
protected function mettreAJourProfilLocal() {
//echo "Mise à jour profil local !!";
if ($this->jetonDecode != null && $this->jetonDecode['id'] != '') {
$requete = 'UPDATE del_utilisateur_infos SET'
. ' nom = ' . $this->bdd->proteger($this->jetonDecode['nom']) . ', '
. ' intitule = ' . $this->bdd->proteger($this->jetonDecode['intitule']) . ', '
. ' prenom = ' . $this->bdd->proteger($this->jetonDecode['prenom'])
. ' WHERE id_utilisateur = ' . $this->bdd->proteger($this->jetonDecode['id'])
. ' -- '.__FILE__.':'.__LINE__
;
//var_dump($requete);
$this->bdd->executer($requete);
}
}
 
/**
* Répercute le nom et le prénom contenus dans le jeton SSO (si au
* moins un des deux n'est pas vide) dans tous les commentaires de
* l'auteur; si le jeton SSO est vide, ne fait rien (boulette-proof)
*
* @TODO gérer l'intitulé un jour
*/
protected function mettreAJourCoordonneesDansCommentaires() {
//echo "Mise à jour obs et images !!";
if ($this->jetonDecode != null && $this->jetonDecode['id'] != '' && ($this->jetonDecode['nom'] != '' || $this->jetonDecode['prenom'] != '')) {
$requete = 'UPDATE del_commentaire SET'
. ' utilisateur_nom = ' . $this->bdd->proteger($this->jetonDecode['nom']) . ', '
. ' utilisateur_prenom = ' . $this->bdd->proteger($this->jetonDecode['prenom'])
. ' WHERE ce_utilisateur = ' . $this->bdd->proteger($this->jetonDecode['id']) // s'assurer qu'il y a des ' autour de l'ID sans quoi les hash MD5 matcheront !
. ' -- '.__FILE__.':'.__LINE__
;
//var_dump($requete);
$this->bdd->executer($requete);
}
}
 
/**
* Essaye de trouver un jeton JWT non vide dans l'entête HTTP "Authorization"
*
* @return String un jeton JWT ou null
*/
protected function lireJetonEntete() {
$jwt = null;
$headers = apache_request_headers();
if (isset($headers["Authorization"]) && ($headers["Authorization"] != "")) {
$jwt = $headers["Authorization"];
}
return $jwt;
}
 
/**
* Vérifie un jeton auprès de l'annuaire
*
* @param String $jeton un jeton JWT
* @return true si le jeton est vérifié, false sinon
*/
protected function verifierJeton($jeton) {
$urlServiceVerification = $this->conteneur->getParametre("urlServiceBaseAuth") . "verifierjeton";
$urlServiceVerification .= "?token=" . $jeton;
 
// file_get_contents râle si le certificat HTTPS est auto-signé
//$retour = file_get_contents($urlServiceVerification);
 
// curl avec les options suivantes ignore le pb de certificat (pour tester en local)
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $urlServiceVerification);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// équivalent de "-k"
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
curl_close($ch);
$retour = $data;
 
$retour = json_decode($retour, true);
 
return ($retour === true);
}
 
/**
* Décode un jeton JWT (SSO) précédemment validé et retourne les infos
* qu'il contient (payload / claims)
* @param String $jeton un jeton JWT précédemment validé
*/
protected function decoderJeton($jeton) {
$parts = explode('.', $jeton);
$payload = $parts[1];
$payload = $this->urlsafeB64Decode($payload);
$payload = json_decode($payload, true);
 
return $payload;
}
 
/**
* Decode a string with URL-safe Base64.
* copié depuis firebase/jwt
*
* @param string $input A Base64 encoded string
* @return string A decoded string
*/
protected function urlsafeB64Decode($input) {
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
 
/**
* Retourne un profil d'utilisateur non connecté (anonyme), avec un identifiant de session
* PHP qui permet de suivre son activité
*/
public function getUtilisateurAnonyme() {
return array(
'connecte' => false,
'id_utilisateur' => session_id(),
'courriel' => '',
'mot_de_passe' => '',
'nom' => '',
'prenom' => '',
'admin' => '0',
'session_id' => session_id()
);
}
 
/**
* Récupère les données d'un utilisateur dans la BDD depuis son login; considère que
* l'utilisateur est légitime car il fournit un jeton SSO vérifié par l'annuaire
*
* @param String $login le courriel de l'utilisateur
* @return array les infos de l'utilisateur
*/
private function recupererUtilisateurEnBdd($login) {
$loginP = $this->bdd->proteger($login);
 
$requete = 'SELECT id_utilisateur, nom, prenom, intitule, courriel, admin'
. " FROM del_utilisateur_infos"
. " WHERE courriel = $loginP"
. ' -- ' . __FILE__ . ' : ' . __LINE__
;
return $this->bdd->recuperer($requete);
}
 
protected function completerInfosUtilisateur($utilisateur) {
$utilisateur['session_id'] = session_id();
$utilisateur['connecte'] = true;
return $utilisateur;
}
 
// @WARNING décompte des derniers événements désactivé pour raisons de perfs
// Mathias - 2016-10-05 @TODO faire mieux
protected function getEvenements($id_utilisateur) {
$sql = $this->conteneur->getSql();
 
$date = $this->getDerniereDateConsultationEvenements($id_utilisateur);
 
//$requete_activite = $sql->getRequeteNbEvenementsDepuisDate($id_utilisateur, $date);
//$resultats = $this->bdd->recupererTous($requete_activite);
 
$evenements = array();
//$nb_evenements = $resultats[0]['nb_evenements'];
//$evenements['nb_evenements'] = $nb_evenements;
$evenements['date_derniere_consultation_evenements'] = $date;
 
return $evenements;
}
 
public function getDerniereDateConsultationEvenements($id_utilisateur) {
$requete = "SELECT date_derniere_consultation_evenements FROM del_utilisateur_infos ".
"WHERE id_utilisateur = ".$this->bdd->proteger($id_utilisateur);
$date = $this->bdd->recuperer($requete);
$date = !empty($date['date_derniere_consultation_evenements']) ? $date['date_derniere_consultation_evenements'] : "0";
return $date;
}
 
public function setDerniereDateConsultationEvenements($id_utilisateur, $date) {
// Vérification que la ligne correspondant à l'utilisateur dans la table
// infos existe bien (sinon on la crée)
$infos_utilisateur = $this->obtenirInfosUtilisateur($id_utilisateur);
if (empty($infos_utilisateur)) {
$this->initialiserInfosUtilisateur($id_utilisateur);
}
$requete = "UPDATE del_utilisateur_infos SET date_derniere_consultation_evenements = ".$this->bdd->proteger($date)." ".
"WHERE id_utilisateur = ".$this->bdd->proteger($id_utilisateur);
$this->bdd->executer($requete);
}
 
protected function ajouterEvenements(&$utilisateur) {
$evenements = $this->getEvenements($utilisateur['id_utilisateur']);
$utilisateur = array_merge($utilisateur, $evenements);
}
 
public function obtenirPreferencesUtilisateur($id_utilisateur) {
$prefs_utilisateur = $this->obtenirInfosUtilisateur($id_utilisateur);
if (empty($prefs_utilisateur)) {
$this->initialiserInfosUtilisateur($id_utilisateur);
$prefs_utilisateur = $this->renvoyerInfosUtilisateurDefaut($id_utilisateur);
} else {
if (empty($prefs_utilisateur['preferences'])) {
$prefs_utilisateur['preferences'] = $this->obtenirTableauPreferenceDefaut();
} else {
$prefs_utilisateur['preferences'] = json_decode($prefs_utilisateur['preferences']);
}
$prefs_utilisateur['admin'] = $prefs_utilisateur['admin'];
}
return $prefs_utilisateur;
}
 
private function obtenirTableauPreferenceDefaut() {
return array('mail_notification_mes_obs' => '1', 'mail_notification_toutes_obs' => '0');
}
 
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'),
'date_derniere_consultation_evenements' => '0000-00-00 00:00:00');
}
 
public function obtenirInfosUtilisateur($id_utilisateur) {
$requete = 'SELECT * '.
'FROM del_utilisateur_infos '.
'WHERE id_utilisateur = '.$this->bdd->proteger($id_utilisateur).' '.
' -- '.__FILE__.' : '.__LINE__;
$prefs_utilisateur = $this->bdd->recuperer($requete);
return $prefs_utilisateur;
}
 
/**
* Ajoute un utilisateur à la table des profils locaux del_utilisateur_infos;
* suppose que l'utilisateur est correctement identifié (jeton décodé disponible)
*/
public function initialiserInfosUtilisateur($id_utilisateur) {
//var_dump("Initialisation infos utilisateur !!");
$preferences_defaut = $this->obtenirTableauPreferenceDefaut();
$prefsEncodeesP = $this->bdd->proteger(json_encode($preferences_defaut));
$idUtilisateurP = $this->bdd->proteger($id_utilisateur);
$nomUtilisateurP = $this->bdd->proteger($this->jetonDecode['nom']);
$prenomUtilisateurP = $this->bdd->proteger($this->jetonDecode['prenom']);
$courrielUtilisateurP = $this->bdd->proteger($this->jetonDecode['sub']);
$intituleUtilisateurP = $this->bdd->proteger($this->jetonDecode['intitule']);
 
$requete = 'INSERT INTO del_utilisateur_infos '.
'(id_utilisateur, intitule, prenom, nom, courriel, admin, preferences, date_premiere_utilisation, date_derniere_consultation_evenements )'.
"VALUES ($idUtilisateurP, $intituleUtilisateurP, $prenomUtilisateurP, $nomUtilisateurP, $courrielUtilisateurP, 0, $prefsEncodeesP, NOW(), NOW()) ".
'ON DUPLICATE KEY UPDATE date_premiere_utilisation = NOW() '.
' -- '.__FILE__.' : '.__LINE__;
return $this->bdd->executer($requete);
}
 
/**
* Vérifie qu'un utilisateur connu est identifié (mode non anonyme) et
* que son identifiant numérique est égal à $id_utilisateur; si non,
* retourne une erreur HTTP 401 et quitte le programme
*
* @param integer $id_utilisateur l'utilisateur attendu
*/
public function controleUtilisateurIdentifie($id_utilisateur) {
$ok = ($this->utilisateur['connecte'] === true && $this->utilisateur['id_utilisateur'] == $id_utilisateur);
if (! $ok) {
$message = "";
if ($this->utilisateur['connecte'] === true) {
$message = "Vous n'êtes pas propriétaire de la ressource demandée";
} else {
$message = "Vous n'êtes pas identifié";
}
$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE;
throw new Exception($message, $code);
}
}
 
// rétrocompat'
public function etreAdmin() {
return ($this->utilisateur['admin'] >= 1);
}
 
// rétrocompat'
public function etreUtilisateurAvecDroitAdmin() {
$etreAdmin = $this->etreAdmin();
if (! $etreAdmin) {
$message = "Vous ne pouvez pas accéder à ce service car vous n'avez pas les droits d'administrateur !\n";
$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE;
throw new Exception($message, $code);
}
return $etreAdmin;
}
}
 
/**
* Compatibilité avec nginx - merci http://php.net/manual/fr/function.getallheaders.php
*/
if (! function_exists('apache_request_headers')) {
function apache_request_headers() {
$headers = '';
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
/branches/v1.11-magnesium/services/bibliotheque/ControleAcces.php
New file
0,0 → 1,235
<?php
// declare(encoding='UTF-8');
/**
* Classe de controle d'accès HTTP AUTH aux services de DEL, à n'utiliser
* que dans ce cas (HTTP AUTH), et pas dans le cas de l'identification générale sur le SSO
*
* Cette classe propose des méthodes permettant :
* - l'authentification HTTP pour bloquer ou autoriser l'accès
* - de déterminer le statut d'admin des utilisateurs
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ControleAcces {
 
protected $conteneur;
protected $bdd;
 
public function __construct($conteneur) {
throw new Exception("obsolète - ne plus utiliser"); // go pyoubelz
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
}
 
/**
* Vérifie que l'IP du client est dans la liste "ip_autorisees" de la config
* @throws Exception
* @return boolean
*/
public function controlerIpAutorisees() {
$ipsAutorisees = $this->conteneur->getParametreTableau('ip_autorisees');
 
$remoteIp = filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP);
$serverIp = filter_input(INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP);
if (in_array($remoteIp, $ipsAutorisees) == false) {
if ($remoteIp != $serverIp) {// ATTENTION : maintenir ce test à l'intérieur du précédent
$message = "Accès interdit. \n".
"Vous n'êtes pas autorisé à accéder à ce service depuis '$remoteIp' !\n";
$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE;
throw new Exception($message, $code);
}
}
return true;
}
 
/**
* Exige qu'un administrateur s'autenthentifie à l'aide de HTTP AUTH
*/
public function demanderAuthentificationAdmin() {
if (!$this->etreAdminAutoriseParHttp()) {
$this->authentifierAdmin();
}
}
/**
* Exige qu'un utilisateur s'autenthentifie à l'aide de HTTP AUTH
*/
public function demanderAuthentificationUtilisateur() {
if (!$this->etreUtilisateurAutoriseParHttp()) {
$this->authentifierUtilisateur();
}
}
 
 
/**
* Lit les entêtes HTTP AUTH et vérifie si l'utilisateur
* existe (courriel / mot de passe); si $doitEtreAdmin est true,
* vérifiera également que l'utilisateur est administrateur de Del
*
* @return boolean true si l'utilisateur est identifié, false sinon
*/
protected function etreUtilisateurAutoriseParHttp($doitEtreAdmin=false) {
$identifiant = $this->getAuthIdentifiant();
$mdp = $this->getAuthMotDePasse();
$existe = $this->obtenirUtilisateur($identifiant, $mdp);
 
$autorisation = (isset($existe) && $existe) ? true :false; // c'est quoi ces tests de clodos ??
 
if ($doitEtreAdmin === true) {
$autorisation = ($autorisation && $this->etreAdmin($identifiant));
}
return $autorisation;
} /**
* Lit les entêtes HTTP AUTH et vérifie que l'utilisateur est identifié
* et est administrateur de Del
*
* @return boolean true si l'utilisateur est identifié et admin de Del, false sinon
*/
protected function etreAdminAutoriseParHttp() {
return $this->etreUtilisateurAutoriseParHttp(true);
}
 
/**
* Lit l'identifiant utilisateur dans les entêtes HTTP AUTH
* @return String l'identifiant utilisateur ou null
*/
protected function getAuthIdentifiant() {
$id = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : null;
return $id;
}
 
/**
* Lit le mot de passe dans les entêtes HTTP AUTH
* @return String le mot de passe ou null
*/
protected function getAuthMotDePasse() {
$mdp = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : null;
return $mdp;
}
 
 
 
/**
* Envoie un message HTTP 401 / une boîte de login HTTP AUTH avec des
* messages correspondant à la demande d'authentification d'un administrateur
* TODO: externaliser noms et adresses spécifiques à Tela Botanica
* @return boolean
*/
protected 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');
}
 
/**
* Envoie un message HTTP 401 / une boîte de login HTTP AUTH avec des
* messages correspondant à la demande d'authentification d'un utilisateur
* TODO: externaliser noms et adresses spécifiques à Tela Botanica
* @return boolean
*/
protected 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');
} /**
* Envoie l'authentification HTTP AUTH , et accepte un mode "debug" pour
* les petits malins
*/
protected 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;
}
 
 
 
/**
* Envoie un message HTTP 401 / une boîte de login HTTP AUTH
* @param string $message_accueil
* @param string $message_echec
*/
private function envoyerAuth($message_accueil, $message_echec) {
header('HTTP/1.0 401 Unauthorized');
header('WWW-Authenticate: realm="'.mb_convert_encoding($message_accueil, 'ISO-8859-1', 'UTF-8').'"');
header('Content-type: text/plain; charset=UTF-8');
print $message_echec;
exit(0);
}
 
/**
* Authentifie et récupère un utilisateur directement depuis la table des
* utilisateurs Del, utile pour l'authentification HTTP AUTH - ne pas
* utiliser pour les services Del qui doivent être branchés au SSO
*/
protected function obtenirUtilisateur($login, $motDePasse) {
$login = $this->bdd->proteger($login);
$motDePasse = $this->bdd->proteger($motDePasse);
$requete = 'SELECT id_utilisateur, nom, prenom, courriel, mot_de_passe '.
'FROM del_utilisateur AS du '.
"WHERE courriel = $login ".
" AND mot_de_passe = MD5($motDePasse) ".
' -- '.__FILE__.':'.__LINE__."\n";
$utilisateur = $this->bdd->recuperer($requete);
return $utilisateur;
}
 
/**
* Vérifie dans la table des utilisateurs Del qu'un utilisateur
* (identifié par son courriel) est administrateur de Del
*
* @param string $courriel
* @return boolean true si l'utilisateur est administrateur de Del, false sinon
*/
protected function etreAdmin($courriel) {
$courriel = $this->bdd->proteger($courriel);
$requete = 'SELECT dui.admin '.
'FROM del_utilisateur AS du LEFT JOIN del_user_infos AS dui ON (du.id_utilisateur = dui.id_utilisateur) '.
"WHERE du.courriel = $courriel ".
' -- '.__FILE__.':'.__LINE__."\n";
$infoUtilisateur = $this->bdd->recuperer($requete);
$etreAdmin = $this->verifierDroitAdmin($infoUtilisateur['admin']);
return $etreAdmin;
}
 
/**
* Vérifie que la valeur "admin" d'un profil utilisateur correspond à la valeur
* attendue dans la config
*
* @return true si sébon, false si sépabon
*/
protected function verifierDroitAdmin($droit) {
$droitAdmin = $this->conteneur->getParametre('droit_superadmin');
$etreAdmin = false;
if (isset($droit) && $droit == $droitAdmin) {
$etreAdmin = true;
}
return $etreAdmin;
}
 
}
/branches/v1.11-magnesium/services/bibliotheque/EnteteHttp.php
New file
0,0 → 1,21
<?php
// declare(encoding='UTF-8');
/**
* Classe contenant le contenu par défaut de l'entête d'une réponse http par défaut.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class EnteteHttp {
public $code = RestServeur::HTTP_CODE_OK;
public $encodage = 'utf-8';
public $mime = 'application/json';
}
/branches/v1.11-magnesium/services/bibliotheque/ResultatService.php
New file
0,0 → 1,21
<?php
// declare(encoding='UTF-8');
/**
* Classe contenant seulement le résultat d'un service.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ResultatService {
public $mime = 'application/json';
public $encodage = 'utf-8';
public $corps = '';
}
/branches/v1.11-magnesium/services/bibliotheque/SyndicationOutils.php
New file
0,0 → 1,80
<?php
 
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
 
class SyndicationOutils {
 
private $conteneur;
private $contexte;
 
public function __construct($conteneur) {
$this->conteneur = $conteneur;
$this->contexte = $this->conteneur->getContexte();
}
 
/**
* Verifier si le flux admin est demande
*/
public function fluxAdminDemande() {
return $this->contexte->getQS('admin') != null && $this->contexte->getQS('admin') == 1;
}
 
// @TODO ne devrait plus être utilisé
// @TODO réécrire si nécessaire
public function demanderAutorisationAdmin() {
throw new Exception("méthode obsolète - utiliser le SSO à la place"); // fuk lé jandarme
//$verification = $this->conteneur->getControleAcces();
//$verification->demanderAuthentificationAdmin();
}
 
/**
* Générer les métadonnées du flux (titre, dates, editeur etc.)
* */
public function construireDonneesCommunesAuFlux($nomFlux, $dateDernierElement) {
$donnees = array();
$donnees['guid'] = $this->creerUrlService();
$donnees['titre'] = $this->conteneur->getParametre("syndication.{$nomFlux}_titre");
$donnees['description'] = $this->conteneur->getParametre("syndication.{$nomFlux}_dsc");
$donnees['lien_service'] = $this->creerUrlService();
$donnees['lien_del'] = $this->conteneur->getParametre('img_appli_lien');
$donnees['editeur'] = $this->conteneur->getParametre('syndication.editeur');
$date_modification_timestamp = strtotime($dateDernierElement);
$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'] = $this->conteneur->getParametre("syndication.generateur_nom");
$donnees['generateur_version'] = $this->conteneur->getParametre("syndication.generateur_version");
return $donnees;
}
 
public function creerUrlService() {
$url = 'http://'.
$this->contexte->getServer('SERVER_NAME').
$this->contexte->getServer('REQUEST_URI');
return htmlspecialchars($url);
}
 
public function getUrlImage($id, $format = 'L') {
$url_tpl = $this->conteneur->getParametre('cel_img_url_tpl');
$url = sprintf($url_tpl, $id, $format);
return $url;
}
 
public 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;
}
}
/branches/v1.11-magnesium/services/bibliotheque/Contexte.php
New file
0,0 → 1,136
<?php
// declare(encoding='UTF-8');
/**
* Contexte permet d'encapsuler les super globales et de définir le contexte du web service courrant.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Contexte {
 
private $conteneur;
private $get;
private $getBrut;
private $post;
private $session;
private $cookie;
private $server;
private $urlRessource;
 
private $mapping = array('getPhp' => 'get',
'getQS' => 'getBrut',
'getPost' => 'post',
'getServer' => 'server',
'getSession' => 'session',
'getCookie' => 'cookie',
'getRessource' => 'urlRessource',
'setCookie' => 'cookie');
 
public function __construct($conteneur, &$server, &$get, &$post, &$session, &$cookie) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->server = $server;
$this->get = $this->nettoyerParametres($get);
$this->getBrut = $this->recupererParametresBruts();
$this->post = $post;
$this->session = $session;
$this->cookie = $cookie;
$this->urlRessource = $this->decouperUrlChemin();
}
 
public function __call($nom, $arguments) {
if (!isset($this->mapping[$nom])) {
$msg = "La méthode $nom n'existe pas dans l'objet {get_class()}";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
$attributNom = $this->mapping[$nom];
$data = $this->$attributNom;
 
if (substr($nom, 0, 3) == 'get') {
$cle = isset($arguments[0]) ? $arguments[0] : null;
return $this->getGenerique($data, $cle);
} else if (substr($nom, 0, 3) == 'set') {
$cle = isset($arguments[0]) ? $arguments[0] : null;
$valeur = isset($arguments[1]) ? $arguments[1] : null;
return $this->setGenerique($data, $cle, $valeur);
}
}
 
private function getGenerique($data, $cle) {
$retour = null;
if ($cle === null) {
$retour = $data;
} else if (isset($data[$cle])) {
$retour = $data[$cle];
}
return $retour;
}
 
private function setGenerique($data, $cle, $valeur) {
if ($valeur === null) {
unset($data[$cle]);
} else {
$data[$cle] = $valeur;
}
}
 
private function nettoyerParametres(Array $parametres) {
// Pas besoin d'utiliser urldecode car déjà fait par php pour les clés et valeur de $_GET
if (isset($parametres) && count($parametres) > 0) {
foreach ($parametres as $cle => $valeur) {
// les quotes, guillements et points-virgules ont été retirés des caractères à vérifier car
//ça n'a plus lieu d'être maintenant que l'on utilise protéger à peu près partout
$verifier = array('NULL', "\\", "\x00", "\x1a");
$parametres[$cle] = strip_tags(str_replace($verifier, '', $valeur));
}
}
return $parametres;
}
 
private function recupererParametresBruts() {
$parametres_bruts = array();
if (isset($this->server['QUERY_STRING']) && !empty($this->server['QUERY_STRING'])) {
$paires = explode('&', $this->server['QUERY_STRING']);
foreach ($paires as $paire) {
if ($paire != '' && substr_count($paire, '=') == 1) {
$nv = explode('=', $paire);
$nom = urldecode($nv[0]);
$valeur = urldecode($nv[1]);
$parametres_bruts[$nom] = $valeur;
}
}
$parametres_bruts = $this->nettoyerParametres($parametres_bruts);
}
return $parametres_bruts;
}
 
private function decouperUrlChemin() {
if (isset($this->server['REDIRECT_URL']) && $this->server['REDIRECT_URL'] != '') {
if (isset($this->server['REDIRECT_QUERY_STRING']) && !empty($this->server['REDIRECT_QUERY_STRING'])) {
$url = $this->server['REDIRECT_URL'].'?'.$this->server['REDIRECT_QUERY_STRING'];
} else {
$url = $this->server['REDIRECT_URL'];
}
} else {
$url = $this->server['REQUEST_URI'];
}
 
$tailleQueryString = strlen($this->server['QUERY_STRING']);
$tailleURL = ($tailleQueryString == 0) ? strlen($url) : -($tailleQueryString + 1);
 
$urlChaine = '';
if (strpos($url, $this->conteneur->getParametre('serveur.baseURL')) !== false) {
$urlChaine = substr($url, strlen($this->conteneur->getParametre('serveur.baseURL')), $tailleURL);
} else if (strpos($url, $this->conteneur->getParametre('serveur.baseAlternativeURL')) !== false) {
$urlChaine = substr($url, strlen($this->conteneur->getParametre('serveur.baseAlternativeURL')), $tailleURL);
}
return explode('/', $urlChaine);
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+16702488d5977ffc5aaaf3430bb115a5487d24c5
\ No newline at end of property
/branches/v1.11-magnesium/services/bibliotheque/ReponseHttp.php
New file
0,0 → 1,80
<?php
// declare(encoding='UTF-8');
/**
* Classe créant la réponse HTTP pour les services de DEL.
*
* Vérifie qu'aucune erreur n'a été générée. Si une erreur existe, retourne le contenu de l'erreur.
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class 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()) {
foreach ($this->erreurs as $erreur) {
$this->resultatService->corps .= $erreur['message']."\n";
}
} 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.11-magnesium/services/bibliotheque/Navigation.php
New file
0,0 → 1,204
<?php
// declare(encoding='UTF-8');
/**
* Navigation gère les url de navigation en fonction d'un départ et d'une limite
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Navigation {
 
private $conteneur;
private $parametresUrl;
private $ressourcesUrl;
private $serviceNom;
private $filtresPossibles;
private $filtresActifs;
 
private $total;
private $sansLimite;
 
/**
* Constructeur de la classe Navigation
* @param Array $parametresUrl (optionnel) la liste des paramètre issus du Conteneur
*/
public function __construct($conteneur) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
 
$contexte = $this->conteneur->getContexte();
$this->parametresUrl = $contexte->getQS();
$this->serviceNom = $contexte->getRessource(1);
 
$ressources = $contexte->getRessource();
$this->ressourcesUrl = implode('/', array_slice($ressources, 1));
 
$this->filtresPossibles = $this->conteneur->getparametreTableau($this->serviceNom.'.masques_possibles');
$this->chargerFiltresActifs();
}
 
private function chargerFiltresActifs() {
if ($this->parametresUrl != null) {
foreach ($this->parametresUrl as $paramNom => $valeur) {
if (in_array($paramNom, $this->filtresPossibles)) {
$this->filtresActifs[$paramNom] = $valeur;
}
}
}
}
 
/**
* Obtenir la valeur courante de départ
*/
public function getDepart() {
$navDepart = $this->getParamUrl('navigation.depart');
return ($navDepart == null) ? 0 : $navDepart ;
}
 
/**
* Obtenir la limite courante
*/
public function getLimite() {
$limiteParam = $this->getParamUrl('navigation.limite');
$limite = 10;
if ($limiteParam != null && is_numeric($limiteParam)) {
$limite = ($limiteParam < 1000) ? $limiteParam : 1000;// Pour éviter les abus !
}
return $limite;
}
 
private function getParamUrl($nom) {
$valeur = isset($this->parametresUrl[$nom]) ? $this->parametresUrl[$nom] : null;
return $valeur;
}
 
/**
* 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->parametresUrl;
$parametres['navigation.depart'] = $depart;
$parametres['navigation.limite'] = $limite;
 
$urlServiceBase = $this->conteneur->getParametre('url_service_base').$this->ressourcesUrl;
$urlNavigation = $this->conteneur->getUrl($urlServiceBase);
$urlNavigation->setOption(Url::OPTION_ENCODER_VALEURS, true);
$urlNavigation->setRequete($parametres);
$url = $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->obtenirUrlNavigation($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->obtenirUrlNavigation($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;
}
 
/**
* Changer la valeur de sans limite pour ne pas l'afficher dans l'entete
* */
public function setSansLimite() {
$this->sansLimite = true;
}
 
/**
* Génère un tableau contenant les informations pour l'entête des services renvoyant une liste de résultats.
*
* @return array Le tableau d'entête prés à être encodé en JSON.
*/
public function getEntete() {
$entete = array();
$entete['masque'] = $this->getChaineFiltresActifs();
 
$entete['total'] = $this->getTotal();
if ($this->sansLimite == false) {
$entete['depart'] = (int) $this->getDepart();
$entete['limite'] = (int) $this->getLimite();
 
$lienPrecedent = $this->recupererHrefPrecedent();
if ($lienPrecedent != null) {
$entete['href.precedent'] = $lienPrecedent;
}
 
$lienSuivant = $this->recupererHrefSuivant();
if ($lienSuivant != null) {
$entete['href.suivant'] = $lienSuivant;
}
}
 
return $entete;
}
 
/**
* Retourne les filtres au format chaine sous la forme filtre1=valeur1&filtre2=valeur2.
*
* @return String la chaine de caractères ou une chaine vide si pas de filtre.
*/
private function getChaineFiltresActifs() {
return (!empty($this->filtresActifs)) ? http_build_query($this->filtresActifs) : '';
}
 
/**
* Récupérer tout ou partie des filtres présent dans l'url.
*
* @param String $filtreNom (optionnel) le nom du filtre tel que présent dans l'url.
* @return mixed si un filtre est passé en paramètre retourn la valeur correspondante, si pas de paramétre
* retourne le tableau complet des filtres. False en cas d'erreur.
* */
public function getFiltre($filtreNom = null) {
$retour = false;
if ($filtreNom == null) {
$retour = $this->filtresActifs;
} else if ($filtreNom != null && isset($this->filtresActifs[$filtreNom])) {
$retour = $this->filtresActifs[$filtreNom];
}
return $retour;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+c6e827965fa2b8632258e19c6a4f96069d43d7a7
\ No newline at end of property
/branches/v1.11-magnesium/services/bibliotheque/Sql.php
New file
0,0 → 1,854
<?php
// declare(encoding='UTF-8');
/**
* Classe contenant des méthodes permettant de construire les requêtes SQL complexe concernant les images et obs.
* Rempli un tableau des clauses "join", "where", "group by" et "oder by" nécessaire à la *recherche* des ids des
* observations/images correspondantes aux filtres passés dans l'url du web service de recherche
* (ListeImages et ListeObservations).
*
* Attention, cela signifie que toutes les tables ne sont pas *forcément* jointées, par exemple si aucune
* contrainte ne le nécessite.
* La requête construite ici est utile pour récupérer la liste des ids d'observations/images qui match.
* Pour la récupération effective de "toutes" les données nécessaire au retour du web service en json, c'est une autre
* requête directement dans le web service qui s'en charge. Cette technique en deux étapes est la plus rapide !
*
* Note: toujours rajouter les préfixes de table (di, do ou du), en fonction de ce que défini
* les JOIN qui sont utilisés :
* - le préfixe de del_image est "di"
* - le préfixe de del_observation est "do"
* - le préfixe de del_utilisateur est "du"
*
* @category DEL
* @package Services
* @package Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Sql {
 
const APPLI_IMG = 'IMG';
const APPLI_OBS = 'OBS';
 
private $conteneur;
private $bdd;
private $parametres = array();
private $appli;
private $requete = array(
'join' => array(),
'where' => array(),
'groupby' => array(),
'orderby' => array());
 
private $champsPrenom = array('prenom_utilisateur');
private $champsNom = array('nom_utilisateur');
private $champsSousRequeteObs = array('masque.genre', 'masque.famille', 'masque.ns', 'masque.commune', 'masque.milieu', 'masque.pays');
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
}
 
public function setParametres(Array $parametres) {
$this->parametres = $parametres;
}
 
public function setAppli($appliType) {
if ($appliType == 'IMG' || $appliType == 'OBS') {
$this->appli = $appliType;
} else {
throw new Exception("Les types d'appli disponible sont : IMG (pour PictoFlora) et OBS (pour IdentiPlante)");
}
}
 
private function getPrefixe() {
return $this->appli === 'IMG' ? 'di' : 'do';
}
 
private function etreAppliImg() {
return $this->appli === 'IMG';
}
 
private function etreAppliObs() {
return $this->appli === 'OBS';
}
 
public function getRequeteSql() {
return $this->requete;
}
 
private function addJoin($join) {
if (!isset($this->requete['join'][$join])) {
$this->requete['join'][$join] = $join;
}
}
 
public function getJoin() {
return ($this->requete['join'] ? implode(' ', array_unique($this->requete['join'])).' ' : '');
}
 
private function addJoinDis($join) {
$this->requete['join']['dis'] = $join;
}
 
private function addWhere($idParam, $where) {
if (isset($this->parametres['_parametres_condition_or_'])
&& in_array($idParam, $this->parametres['_parametres_condition_or_'])) {
if ($this->etreAppliImg() && in_array($idParam, $this->champsSousRequeteObs)) {
$this->requete['where']['OR_SOUS_REQUETE'][] = $where;
} else {
$this->requete['where']['OR'][] = $where;
}
} else {
$this->requete['where']['AND'][] = $where;
}
}
 
public function getWhere() {
// Sous-requete spéciale pour éviter de rechercher dans la table obs jointe à img depuis Pictoflora...
if (isset($this->requete['where']['OR_SOUS_REQUETE']) && count($this->requete['where']['OR_SOUS_REQUETE']) > 0) {
$clauseWhereSousRequete = implode(' OR ', $this->requete['where']['OR_SOUS_REQUETE']);
$sousRequete = 'di.ce_observation IN '.
"(SELECT id_observation FROM del_observation AS do WHERE $clauseWhereSousRequete ) ";
$this->requete['where']['OR'][] = "( $sousRequete )";
unset($this->requete['join'][$this->getSqlJointureObs()]);
}
 
if (isset($this->requete['where']['OR']) && count($this->requete['where']['OR']) > 0) {
$this->requete['where']['AND'][] = '('.implode(' OR ', $this->requete['where']['OR']).')';
}
 
$where = ' TRUE ';
if (isset($this->requete['where']['AND']) && count($this->requete['where']['AND']) > 0) {
$where = implode(' AND ', $this->requete['where']['AND']).' ';
}
return $where;
}
 
private function addGroupBy($groupBy) {
$this->requete['groupby'][] = $groupBy;
}
 
public function getGroupBy() {
$groupby = '';
if (isset($this->requete['groupby']) && count($this->requete['groupby']) > 0) {
$groupby = 'GROUP BY '.implode(', ', array_unique($this->requete['groupby'])).' ';
}
return $groupby;
}
 
private function addOrderBy($orderby) {
$this->requete['orderby'][] = $orderby;
}
 
public function getOrderBy() {
$orderby = '';
if (isset($this->requete['orderby']) && count($this->requete['orderby']) > 0) {
$orderby = 'ORDER BY '.implode(', ', array_unique($this->requete['orderby'])).' ';
}
return $orderby;
}
 
public function getLimit() {
return 'LIMIT '.$this->parametres['navigation.depart'].','.$this->parametres['navigation.limite'].' ';
}
 
/**
* @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
*/
public function ajouterContraintes() {
$this->ajouterContrainteAuteur();
$this->ajouterContrainteDate();
$this->ajouterContraintePays();
$this->ajouterContrainteDepartement();
$this->ajouterContrainteIdZoneGeo();
$this->ajouterContrainteGenre();
$this->ajouterContrainteFamille();
$this->ajouterContrainteNs();
$this->ajouterContrainteNn();
$this->ajouterContrainteReferentiel();
$this->ajouterContrainteCommune();
$this->ajouterContraintePnInscrits();
}
 
private function ajouterContrainteAuteur() {
if (isset($this->parametres['masque.auteur'])) {
$auteur = $this->parametres['masque.auteur'];
// id du poster de l'obs
$prefixe = $this->getPrefixe();
 
if (is_numeric($auteur)) {
$this->ajouterContrainteAuteurId();
} elseif(preg_match('/@[a-z0-9-]+(?:\.[a-z0-9-]+)*\.[a-z]{2,}$/i', $auteur)) {
$this->ajouterContrainteAuteurEmail();
} else {
$this->ajouterContrainteAuteurIntitule();
}
}
}
 
private function ajouterContrainteAuteurId() {
$id = $this->parametres['masque.auteur'];
$prefixe = $this->getPrefixe();
$sqlTpl = "($prefixe.ce_utilisateur = %1\$d)";
$whereAuteur = sprintf($sqlTpl, $id);
$this->addWhere('masque.auteur', $whereAuteur);
}
 
private function ajouterContrainteAuteurEmail() {
$email = $this->parametres['masque.auteur'];
$prefixe = $this->getPrefixe();
$sqlTpl = "($prefixe.courriel_utilisateur LIKE %1\$s )";
$emailP = $this->bdd->proteger("$email%");
$whereAuteur = sprintf($sqlTpl, $emailP);
$this->addWhere('masque.auteur', $whereAuteur);
}
 
/**
* Retourne une clause where du style:
* CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
*/
private function ajouterContrainteAuteurIntitule() {
$auteurExplode = explode(' ', $this->parametres['masque.auteur']);
$nbreMots = count($auteurExplode);
 
if ($nbreMots == 1) {
$this->ajouterContrainteAuteurPrenomOuNom();
} else if ($nbreMots == 2) {
$this->ajouterContrainteAuteurPrenomEtNom();
}
}
 
private function ajouterContrainteAuteurPrenomOuNom() {
$prenomOuNom = $this->parametres['masque.auteur'];
 
$sqlTpl = 'CONCAT(%s,%s) LIKE %s';
$prefixe = $this->getPrefixe();
$champsPrenomSql = self::ajouterIfNullPourConcat($this->champsPrenom, $prefixe);
$champsNomSql = self::ajouterIfNullPourConcat($this->champsNom, $prefixe);
$auteurMotif = $this->bdd->proteger("%$prenomOuNom%");
 
$auteurWhere = sprintf($sqlTpl, $champsPrenomSql, $champsNomSql, $auteurMotif);
$this->addWhere('masque.auteur', $auteurWhere);
}
 
private function ajouterContrainteAuteurPrenomEtNom() {
list($prenom, $nom) = explode(' ', $this->parametres['masque.auteur']);
 
$sqlTpl = '(CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s)';
$prefixe = $this->getPrefixe();
$champsPrenomSql = self::ajouterIfNullPourConcat($this->champsPrenom, $prefixe);
$champsNomSql = self::ajouterIfNullPourConcat($this->champsNom, $prefixe);
$prenomMotif = $this->bdd->proteger("%$prenom%");
$nomMotif = $this->bdd->proteger("%$nom%");
 
$auteurWhere = sprintf($sqlTpl, $champsPrenomSql, $champsNomSql, $prenomMotif, $nomMotif);
$this->addWhere('masque.auteur', $auteurWhere);
}
 
/**
* 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".
*/
private static function ajouterIfNullPourConcat($champs, $prefixe) {
$champsProteges = array();
foreach ($champs as $champ) {
if (strstr($champ, '.') === false) {
$champ = "$prefixe.$champ";
}
$champsProteges[] = "IFNULL($champ, '')";
}
return implode(',', $champsProteges);
}
 
private function ajouterContrainteDate() {
if (isset($this->parametres['masque.date'])) {
$date = $this->parametres['masque.date'];
if (preg_match('/^\d{4}$/', $date) && $date < 2030 && $date > 1600) {
$sqlTpl = "YEAR(do.date_observation) = %d";
$dateWhere = sprintf($sqlTpl, $date);
$this->addWhere('masque.date', $dateWhere);
} else {
$sqlTpl = "do.date_observation = %s";
$dateP = $this->bdd->proteger($date);
$dateWhere = sprintf($sqlTpl, $dateP);
$this->addWhere('masque.date', $dateWhere);
}
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteDepartement() {
if (isset($this->parametres['masque.departement'])) {
$dept = $this->parametres['masque.departement'];
$deptMotif = $this->bdd->proteger("INSEE-C:$dept");
$this->addWhere('masque.departement', "do.ce_zone_geo LIKE $deptMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
private function ajouterContraintePays() {
if (isset($this->parametres['masque.pays'])) {
// Attention le standard contient parfois FX pour la france métropolitaine
// Dans ce cas particulier on cherche donc FR et FX
$this->parametres['masque.pays'] = strtoupper($this->parametres['masque.pays']);
if(strpos($this->parametres['masque.pays'], 'FR') !== false) {
$this->parametres['masque.pays'] = str_replace('FR', 'FR,FX', $this->parametres['masque.pays']);
}
$pays = explode(',', $this->parametres['masque.pays']);
$pays = implode(',', $this->bdd->proteger($pays));
$this->addWhere('masque.pays', "do.pays IN ($pays)");
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteIdZoneGeo() {
if (isset($this->parametres['masque.id_zone_geo'])) {
$idZgMotif = $this->bdd->proteger($this->parametres['masque.id_zone_geo']);
$this->addWhere('masque.id_zone_geo', "do.ce_zone_geo = $idZgMotif");
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteGenre() {
if (isset($this->parametres['masque.genre'])) {
$genre = $this->parametres['masque.genre'];
$genreMotif = $this->bdd->proteger("$genre%");
$this->addWhere('masque.genre', "do.nom_sel LIKE $genreMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteFamille() {
if (isset($this->parametres['masque.famille'])) {
$familleMotif = $this->bdd->proteger($this->parametres['masque.famille']);
$this->addWhere('masque.famille', "do.famille = $familleMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteNs() {
if (isset($this->parametres['masque.ns'])) {
$ns = $this->parametres['masque.ns'];
$nsMotif = $this->bdd->proteger("$ns%");
$this->addWhere('masque.ns', "do.nom_sel LIKE $nsMotif");
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteNn() {
if (isset($this->parametres['masque.nn'])) {
$sqlTpl = '(do.nom_sel_nn = %1$d OR do.nom_ret_nn = %1$d)';
$nnWhere = sprintf($sqlTpl, $this->parametres['masque.nn']);
$this->addWhere('masque.nn', $nnWhere);
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteReferentiel() {
if (isset($this->parametres['masque.referentiel'])) {
$ref = $this->parametres['masque.referentiel'];
$refMotif = $this->bdd->proteger("$ref%");
$this->addWhere('masque.referentiel', "do.nom_referentiel LIKE $refMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteCommune() {
if (isset($this->parametres['masque.commune'])) {
$commune = $this->parametres['masque.commune'];
$communeMotif = $this->bdd->proteger("$commune%");
$this->addWhere('masque.commune', "do.zone_geo LIKE $communeMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
/**
* Si masque.pninscritsseulement vaut true, les observations ayant un tag
* "plantnet" mais dont l'auteur n'est pas inscrit à TB seront éliminées
* (décision FlorisTic 2016-09)
*/
protected function ajouterContraintePnInscrits() {
if (isset($this->parametres['masque.pninscritsseulement'])) {
// avec la classe ParametresFiltrage, on ne passe là que si le masque vaut 1
$motifMotClePlantnet = "'%plantnet%'";
$this->addWhere('masque.pninscritsseulement', "((do.mots_cles_texte NOT LIKE $motifMotClePlantnet OR do.mots_cles_texte IS NULL) OR do.ce_utilisateur != 0)");
 
$this->ajouterJoinObsSiNecessaire();
}
}
private function ajouterJoinObsSiNecessaire() {
if ($this->etreAppliImg()) {
$this->addJoin($this->getSqlJointureObs());
}
}
private function getSqlJointureObs() {
$typeJointure = !empty($this->parametres['masque']) ? 'LEFT' : 'INNER';
return $typeJointure.' JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ';
}
 
// la constrainte de nostre bon roy
public function ajouterConstrainteAppliObs() {
$this->ajouterContrainteTagCel();
$this->ajouterContrainteType();
// TODO : ATTENTION -> vu que l'on utilise une vue basée sur les images, nous devons grouper par obs
$this->addGroupBy('do.id_observation');
}
 
private function ajouterContrainteType() {
// Les contraintes régissant les onglets sont issus de la réunion dont le compte rendu
// disponible ici : http://tela-botanica.net/intranet/wakka.php?wiki=Octobre2014
// Ce lien est à modifier pour pointer vers toute nouvelle réunion modifiant ce fonctionnement
if (isset($this->parametres['masque.type'])) {
if (array_key_exists('adeterminer', $this->parametres['masque.type'])) {
// A DETERMINER : toutes les observations qui ont le tag "aDeterminer"
// *ou* qui n'ont pas de nom d'espèce
// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
$this->addWhere('masque.type', '('.
'do.certitude = "aDeterminer" '.
'OR do.certitude = "douteux" '.
'OR do.mots_cles_texte LIKE "%aDeterminer%" '.
'OR do.nom_sel_nn IS NULL '.
'OR do.nom_sel_nn = 0 '.// il ne DEVRAIT pas y avoir d'entrées à 0, mais il y en a quand-même !!
')');
}
if (array_key_exists('validees', $this->parametres['masque.type'])) {
// VALIDEES : toutes les observations ayant un commentaire doté de proposition_retenue = 1
// ou bien possédant une proposition initiale avec un nom valide ayant totalisé un score d'au moins 4
// (ce qui correspond à au moins deux votes positifs dans la plupart des cas, dont un identifié)
$sous_requete_score_prop_votees = $this->getSousRequeteSommeVotesPropositions();
$this->addJoin('INNER JOIN del_commentaire AS dc '.
'ON ( '.
'do.id_observation = dc.ce_observation '.
'AND ( '.
'dc.proposition_retenue = 1 OR '.
'( '.
'dc.proposition_initiale = 1 '.
'AND dc.nom_sel_nn != 0 '.
'AND dc.nom_sel_nn IS NOT NULL '.
' AND dc.id_commentaire IN ('.$sous_requete_score_prop_votees.' >= 4) '.
') '.
') '.
')'
);
}
if(array_key_exists('aconfirmer', $this->parametres['masque.type'])) {
// A CONFIRMER : toutes les observations moins les validées et à confirmer
// i.e. : des observations avec un nom valide, qui ne sont pas à déterminer
// (ni certitude "aDeterminer" ou "douteuse", ni mot clé),
// ne possédant pas de proposition officiellement retenue
// et ayant une proposition initiale totalisant un score de moins de 4
$sous_requete_score_prop_votees = $this->getSousRequeteSommeVotesPropositions();
$this->addWhere('masque.type',
'('.
'do.id_observation IN ('.
'SELECT dc.ce_observation FROM del_commentaire dc WHERE dc.proposition_retenue = 0'.
' AND ( '.
'dc.proposition_initiale = 1 '.
'AND dc.nom_sel_nn != 0 '.
'AND dc.nom_sel_nn IS NOT NULL '.
'AND dc.id_commentaire IN ('.$sous_requete_score_prop_votees.' < 4) '.
') '.
') AND do.id_observation NOT IN ('.
'SELECT dc.ce_observation FROM del_commentaire dc '.
'WHERE '.
' dc.proposition_retenue = 1'.
') '.
'AND do.certitude != "douteux" AND do.certitude != "aDeterminer" '.
'AND do.mots_cles_texte NOT LIKE "%aDeterminer%" '.
'AND do.nom_sel_nn != 0 '.
'AND do.nom_sel_nn IS NOT NULL'.
') '
);
}
 
$this->ajouterJoinObsSiNecessaire();
}
}
private function getSousRequeteSommeVotesPropositions() {
// ATTENTION : un vote identifié compte 3 votes anonymes (dans les deux sens)
return 'SELECT ce_proposition '.
'FROM del_commentaire_vote dcv '.
'GROUP BY ce_proposition HAVING '.
'SUM(CASE '.
' WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP \'^-?[0-9]+$\' != 0 THEN 3 '.
' WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP \'^-?[0-9]+$\' != 0 THEN -3 '.
' WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP \'^-?[0-9]+$\' = 0 THEN 1 '.
' WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP \'^-?[0-9]+$\' = 0 THEN -1 '.
'END '.
') ';
}
 
public function ajouterConstrainteAppliImg() {
$this->ajouterContrainteMilieu();
$this->ajouterContrainteTri();
$this->ajouterContrainteTagCel();
$this->ajouterContrainteTagDel();
$this->ajouterContraintePnInscrits();
}
 
private function ajouterContrainteMilieu() {
if (isset($this->parametres['masque.milieu'])) {
$milieu = $this->parametres['masque.milieu'];
$milieuMotif = $this->bdd->proteger("%$milieu%");
$this->addWhere('masque.milieu', "do.milieu LIKE $milieuMotif");
 
$this->ajouterJoinObsSiNecessaire();
}
}
 
private function ajouterContrainteTri() {
if (isset($this->parametres['tri'])) {
$tri = $this->parametres['tri'];
 
if (isset($this->parametres['protocole']) && ($tri == 'moyenne-arithmetique' || $tri == 'points')) {
// $this->parametres['protocole'] *est* défini (cf Outils::filtrerUrlsParams...())
$sqlTpl = 'LEFT JOIN del_image_stat AS dis ON di.id_image = dis.ce_image AND dis.ce_protocole = %d';
$triSql = sprintf($sqlTpl, $this->parametres['protocole']);
$this->addJoinDis($triSql);
}
 
if (isset($this->parametres['ordre']) && $tri == 'tags') {
$typeJointure = ($this->parametres['ordre'] == 'desc') ? 'INNER' : 'LEFT';
$this->addJoin("$typeJointure JOIN del_image_stat AS dis ON di.id_image = dis.ce_image");
}
}
}
 
private function ajouterContrainteTagCel() {
if (isset($this->parametres['masque.tag_cel'])) {
if (isset($this->parametres['masque.tag_cel']['AND'])) {
$tags = $this->parametres['masque.tag_cel']['AND'];
$clausesWhere = array();
foreach ($tags as $tag) {
$tagMotif = $this->bdd->proteger("%$tag%");
if ($this->etreAppliImg()) {
$sousRequete = 'SELECT id_observation '.
'FROM del_observation '.
"WHERE mots_cles_texte LIKE $tagMotif ";
$sql = " (di.mots_cles_texte LIKE $tagMotif OR di.id_image IN ($sousRequete) ) ";
} else {
// WARNING : la sous-requête est la meilleure solution trouvée pour contrer le fonctionnement
// étrange de l'optimiseur de MYSQL 5.6 (à retester avec Mysql 5.7 et suivant).
$sousRequete = 'SELECT DISTINCT ce_observation '.
'FROM del_image '.
"WHERE mots_cles_texte LIKE $tagMotif ".
'AND ce_observation IS NOT NULL';
$sql = " (do.mots_cles_texte LIKE $tagMotif OR do.id_observation IN ($sousRequete)) ";
}
$clausesWhere[] = $sql;
}
$whereTags = implode(' AND ', $clausesWhere);
$this->addWhere('masque.tag_cel', "($whereTags)");
} else if (isset($this->parametres['masque.tag_cel']['OR'])) {
$tags = $this->parametres['masque.tag_cel']['OR'];
$tagMotif = $this->bdd->proteger(implode('|', $tags));
$sqlTpl = "CONCAT(IFNULL(do.mots_cles_texte,''),IFNULL(di.mots_cles_texte,'')) REGEXP %s";
$tagSql = sprintf($sqlTpl, $tagMotif);
 
$this->addWhere('masque.tag_cel', $tagSql);
 
if ($this->etreAppliObs()) {
$this->addJoin('LEFT JOIN del_image AS di ON (di.ce_observation = do.id_observation) ');
}
}
$this->ajouterJoinObsSiNecessaire();
}
}
 
/**
* Plusieurs solutions sont disponibles dans les anciennes versions (voir DelTk et l'historique SVN de ce fichier).
*/
private function ajouterContrainteTagDel() {
if (isset($this->parametres['masque.tag_del'])) {
$nbTags = $this->getNombreDeTags();
if($nbTags > 1) {
// sous-requêtes car le GROUP BY avec GROUP_CONCAT est *trop* lent
if (isset($this->parametres['masque.tag_del']['AND'])) {
foreach ($this->parametres['masque.tag_del']['AND'] as $mc) {
$sousRequete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
"AND tag_normalise LIKE '%$mc%' ";
$this->addWhere('masque.tag_del', "di.id_image IN ($sousRequete)");
}
} else if (isset($this->parametres['masque.tag_del']['OR'])) {
$tagsMotif = "'(" . implode('|', $this->parametres['masque.tag_del']['OR']) . ")'";
$sousRequete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
"AND tag_normalise REGEXP $tagsMotif ";
$this->addWhere('masque.tag_del', "di.id_image IN ($sousRequete)");
}
} else {
// Si un seul tag est demandé il se trouve dans le OR dans le cas de la recherche
// spécifique et dans le AND dans le cas de la recherche générale
// WTF?
$tag = "";
if(isset($this->parametres['masque.tag_del']['OR'][0])) {
$tag = $this->parametres['masque.tag_del']['OR'][0];
} else if(isset($this->parametres['masque.tag_del']['AND'][0])) {
$tag = $this->parametres['masque.tag_del']['AND'][0];
}
$sousRequete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'AND tag_normalise LIKE '.$this->bdd->proteger($tag.'%');
 
$this->addWhere('masque.tag_del', "di.id_image IN ($sousRequete)");
}
}
}
private function getNombreDeTags() {
$somme = 0;
if (isset($this->parametres['masque.tag_del']['AND'])) {
$somme = count($this->parametres['masque.tag_del']['AND']);
} else {
$somme = count($this->parametres['masque.tag_del']['OR']);
}
return $somme;
}
 
/**
* Partie spécifique à PictoFlora:
* Attention : si le critère de tri n'est pas suffisant, les résultats affichés peuvent varier à chaque appel
* de la même page de résultat de PictoFlora.
*/
public function definirOrdreSqlAppliImg() {
$ordre = $this->parametres['ordre'];
$tri = isset($this->parametres['tri']) ? $this->parametres['tri'] : '';
switch ($tri) {
case 'moyenne-arithmetique' :
$this->addOrderBy("dis.moyenne $ordre, dis.nb_votes $ordre, id_image $ordre");
break;
case 'points' :
$this->addOrderBy("dis.nb_points $ordre, dis.moyenne $ordre, dis.nb_votes $ordre, id_image $ordre");
break;
case 'tags' :
$this->addOrderBy("dis.nb_tags $ordre, id_image $ordre");
break;
case 'date_observation' :
$this->addOrderBy("date_observation $ordre, ce_observation $ordre");
break;
case 'date_transmission' :
default:
$this->addOrderBy("di.date_transmission $ordre, ce_observation $ordre");
}
}
 
public function definirOrdreSqlAppliObs() {
$ordre = $this->parametres['ordre'];
 
// parmi self::$tri_possible
$tri = isset($this->parametres['tri']) ? $this->parametres['tri'] : '';
switch ($tri) {
case 'date_observation' :
$this->addOrderBy("date_observation $ordre, id_observation $ordre");
break;
case 'nb_commentaires' :
$sql_nb_comms = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc WHERE ce_observation = id_observation)';
$this->addOrderBy("$sql_nb_comms $ordre, id_observation $ordre");
break;
case 'date_transmission' :
default:
$this->addOrderBy("do.date_transmission $ordre, id_observation $ordre");
}
}
 
public function getAliasDesChamps($champsEtAlias, $select = null, $prefix = null) {
$arr = ($select) ? array_intersect_key($champsEtAlias, array_flip($select)) : $champsEtAlias;
$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));
}
 
public function getVotesDesImages($idsImages, $protocole = null) {
if (!$idsImages) return;
 
$mappingVotes = $this->conteneur->getParametreTableau('votes.mapping');
$mappingProtocoles = $this->conteneur->getParametreTableau('protocoles.mapping');
$selectVotes = array('id_vote', 'ce_image', 'ce_protocole', 'ce_utilisateur', 'valeur', 'date');
$selectProtocole = array('id_protocole', 'intitule', 'descriptif', 'tag');
$voteChamps = $this->getAliasDesChamps($mappingVotes, $selectVotes, 'v'); // "v": cf alias dans la requête
$protoChamps = $this->getAliasDesChamps($mappingProtocoles, $selectProtocole, 'p');
$idImgsConcat = implode(',', $idsImages);
 
$requete = "SELECT $voteChamps, $protoChamps ".
'FROM del_image_vote AS v '.
' INNER JOIN del_image_protocole AS p ON (v.ce_protocole = p.id_protocole) '.
"WHERE v.ce_image IN ($idImgsConcat) ".
($protocole ? " AND v.ce_protocole = $protocole " : '').
"ORDER BY FIELD(v.ce_image, $idImgsConcat) ".
'-- '.__FILE__.':'.__LINE__;
return $this->bdd->recupererTous($requete);
}
 
/**
* Ajoute les informations sur le protocole et les votes aux images.
*
* ATTENTION : 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 ListeImages::reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions
* simultanément lorsque c'est encore possible.
*/
// TODO : supprimer cette "subtilité" source d'erreurs
public function ajouterInfosVotesProtocoles($votes, &$images) {
if (!$votes) return;
 
$mappingVotes = $this->conteneur->getParametreTableau('votes.mapping');
$mappingProtocoles = $this->conteneur->getParametreTableau('protocoles.mapping');
 
// pour chaque vote
foreach ($votes as $vote) {
$imgId = $vote['image.id'];
$protoId = $vote['protocole.id'];
 
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($mappingProtocoles));
$images[$imgId]['protocoles_votes'][$protoId] = $protocole;
}
 
$chpsVotes = array('id_vote', 'ce_image', 'ce_utilisateur', 'valeur', 'date');
$voteSelection = array_intersect_key($mappingVotes, array_flip($chpsVotes));
$vote = array_intersect_key($vote, array_flip($voteSelection));
$images[$imgId]['protocoles_votes'][$protoId]['votes'][$vote['vote.id']] = $vote;
}
}
 
public function getTotalLignesTrouvees() {
$resultat = $this->bdd->recuperer('SELECT FOUND_ROWS() AS nbre -- '.__FILE__.':'.__LINE__);
return intval($resultat['nbre']);
}
public function getRequeteIdObsMonactiviteTout($id_utilisateur, $limite = "") {
/*
Une action c'est :
- Quelqu'un commente mon observation
- Quelqu'un fait une proposition sur mon observation
- Quelqu'un vote pour ma proposition
- Quelqu'un commente ma proposition ou mon commentaire
- Quelqu'un vote pour une proposition sur mon observation
- Quelqu'un commente une proposition ou un commentaire sur mon observation
*/
// on selectionne aussi la combinaison des champs de date afin que la liste soit triée correctement
// pas d'autre meilleure solution mais attention ce comportement dépend entièrement de mysql
$requete = "SELECT SQL_CALC_FOUND_ROWS DISTINCT id_observation, ".$this->getCombinaisonChampsDateMax()." as date_max FROM del_observation do ".
$this->getJointureMonActivite($id_utilisateur).
$this->getConditionMonActivite($id_utilisateur).
"ORDER BY date_max DESC ".
$limite;
 
return $requete;
}
public function getRequeteNbEvenementsDepuisDate($id_utilisateur, $date) {
$requete = "SELECT COUNT(DISTINCT id_observation) as nb_evenements FROM del_observation do ".
$this->getJointureMonActivite($id_utilisateur).
$this->getConditionMonActivite($id_utilisateur).
"AND ".$this->getCombinaisonChampsDateMax()." > '".$date."' ".
"ORDER BY ".$this->getCombinaisonChampsDateMax()." DESC ";
 
return $requete;
}
public function getEvenementsObs($idsObsConcat, $id_utilisateur) {
$sous_champ_date_max = $this->getCombinaisonChampsDateMax()." as date_max";
$sous_champ_date = "dc.date as date_com, dc.nom_sel as nom_sel_com, dcpr.ce_commentaire_parent as parent_com, dcv.date as date_vote, ".
"do.date_observation as date_obs, dcp.date_validation as date_validation, dcpr.date as date_com_reponse, ".
"dcvp.nom_sel as nom_sel_com_parent";
$sous_champs_utilisateurs = "dc.ce_utilisateur as utilisateur_commentaire, dcp.ce_utilisateur as utilisateur_commentaire_valide, ".
"dcv.ce_utilisateur as utilisateur_vote_commentaire, do.ce_utilisateur as utilisateur_observation, ".
"dcp.ce_validateur as utilisateur_validation, dcvp.ce_utilisateur as utilisateur_commentaire_vote, ".
"dcpr.ce_utilisateur as utilisateur_commentaire_reponse";
 
$sous_champs_infos = "dc.nom_sel as proposition_commentaire_nom_sel, dc.texte as proposition_commentaire_texte, dcp.nom_sel as proposition_validee_nom_sel, ".
"dcvp.nom_sel as proposition_commentaire_nom_sel_votee, dcpr.texte as proposition_commentaire_texte_commente";
$requete = "SELECT DISTINCT id_observation, ".$sous_champs_utilisateurs.", ".$sous_champ_date_max.", ".$sous_champ_date.", ".$sous_champs_infos." ".
"FROM del_observation do ".
$this->getJointureMonActivite($id_utilisateur).
$this->getConditionMonActivite($id_utilisateur).
"AND id_observation IN ($idsObsConcat) ORDER BY date_max DESC";
 
$evenements = $this->bdd->recupererTous($requete);
return $evenements;
}
public function getJointureMonActivite($id_utilisateur) {
return // quelqu'un commente mon observation ou fait une proposition
"LEFT JOIN del_commentaire dc ON do.id_observation = dc.ce_observation ".
" AND do.ce_utilisateur = ".$id_utilisateur." ".
" AND dc.ce_utilisateur != ".$id_utilisateur." ".
// quelqu'un valide ma proposition (et ce n'est pas moi qui l'ai validée)
"LEFT JOIN del_commentaire dcp ON do.id_observation = dcp.ce_observation ".
" AND dcp.nom_sel IS NOT NULL AND dcp.ce_validateur != ".$id_utilisateur." ".
" AND dcp.ce_validateur != 0 ".
" AND dcp.date_validation IS NOT NULL ".
" AND dcp.ce_utilisateur = ".$id_utilisateur." ".
// quelqu'un vote pour ma proposition ou sur une proposition sur une de mes observations
"LEFT JOIN del_commentaire dcvp ON do.id_observation = dcvp.ce_observation ".
"LEFT JOIN del_commentaire_vote dcv ON dcv.ce_proposition = dcvp.id_commentaire ".
"AND (dcvp.ce_utilisateur = $id_utilisateur OR do.ce_utilisateur = $id_utilisateur) ".
"AND dcv.ce_utilisateur != $id_utilisateur ".
"AND dcv.ce_utilisateur != dcvp.ce_utilisateur ".
// Quelqu'un répond à l'un de mes commentaires ou commente une de mes propositions
"LEFT JOIN del_commentaire dcpr ON do.id_observation = dcpr.ce_observation ".
"AND dcpr.ce_commentaire_parent = dcvp.id_commentaire AND dcvp.ce_utilisateur = $id_utilisateur ";
}
public function getConditionMonActivite($id_utilisateur, $type = "autres") {
//TODO: gérer les cas suivants :
// demander les activités des autres sur mes obs ou propositions (c'est dejà le cas)
// demander mes activités
// demander toutes les activités (combinaisons des deux cas ci dessus)
return // Vérification que l'évènement me concerne (de près ou ou de loin)
"WHERE (do.ce_utilisateur = $id_utilisateur OR dc.ce_utilisateur = $id_utilisateur ".
"OR dcp.ce_utilisateur = $id_utilisateur OR dcv.ce_utilisateur = $id_utilisateur ".
"OR dcvp.ce_utilisateur = $id_utilisateur) AND ".
// mais qu'il y a au moins eu une action de la part d'une autre personne
"(dc.ce_utilisateur IS NOT NULL OR dcp.ce_utilisateur IS NOT NULL OR dcv.ce_utilisateur IS NOT NULL) ";
}
private function getCombinaisonChampsDateMax() {
return "GREATEST(IFNULL(dc.date,0), IFNULL(dcv.date,0), IFNULL(do.date_observation,0), IFNULL(dcp.date_validation,0), IFNULL(dcpr.date,0))";
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+d671d5d650c4560e1a9b099d98db1e0c78f00002
\ No newline at end of property
/branches/v1.11-magnesium/services/README
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.11-magnesium/services/index.php
New file
0,0 → 1,56
<?php
/**
* Initialise le chargement et l'exécution des services web.
*
* @category DEL
* @package Services
* @subpackage Bibliotheque
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
// Permet d'afficher le temps d'execution du service
$temps_debut = (isset($_GET['chrono']) && $_GET['chrono'] == 1) ? microtime(true) : '';
 
// Autoloader pour les namespaces, à base de routes
function __autoload($nom_classe) {
//echo "AUTOLOAD\n";
$dernierAS = strrpos($nom_classe, "\\");
$ns = substr($nom_classe, 0, $dernierAS);
$nom = substr($nom_classe, strrpos($nom_classe, "\\") + 1);
//echo "Recherche : $nom / $ns\n";
// Routes selon les namespaces
$routes = array('TelaBotanica\Del\Commun' => Config::get('chemin_del_commun'));
if (array_key_exists($ns, $routes)) {
//echo "Route trouvée: " . $routes[$ns] . "\n";
$fichier_a_inclure = dirname(__FILE__) . DS . $routes[$ns] . DS . $nom . '.php';
if (file_exists($fichier_a_inclure)) {
include_once $fichier_a_inclure;
return null;
}
}
}
 
// 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();
}
/branches/v1.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/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.11-magnesium/services/modules/0.1/MotsCles.php
New file
0,0 → 1,148
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-service "mots-clés" de DEL.
*
* URLs possibles :
* GET
* http://localhost/del/services/0.1/mots-cles?image=#id&auteur.id=#id => les différents mots-clés des images en fonction des paramètres.
* PUT
* http://localhost/del/services/0.1/mots-cles?image=#id&auteur.id=#id&mot_cle=motCle1,motCle2,... => Ajoute différents mots-clés pour une image d'un utilisateur
* DELETE
* http://localhost/del/services/0.1/mots-cles?image=#id&auteur.id=#id => les différents mots-clés des images en fonction des paramètres.
*
* @category DEL
* @package Services
* @subpackage MotsCles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class MotsCles extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'mots_cles';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
public function supprimer($ressources) {
$this->methode = 'supprimer';
$this->initialiserRessourcesEtParametres($ressources);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$resultat = '';
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if (!isset($this->ressources) || empty($this->ressources)) {
$this->sousServiceNom = 'liste-mots-cles';
}
} else if ($this->methode == 'ajouter') {
$this->sousServiceNom = 'ajouter-mot-cle';
} else if ($this->methode == 'supprimer') {
if (isset($this->ressources[0]) && count($this->ressources) == 1 && is_numeric($this->ressources[0])) {
$this->sousServiceNom = 'supprimer-mot-cle';
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service «".$this->serviceNom."» !\n".
"Les URLs disponibles sont : \n".
" - en GET : mots-cles?image=#idImg&auteur.id=#idUtilisateur \n".
" - en PUT : mots-cles?image=#idImg&auteur.id=#idUtilisateur&mots_cles=motCle1,motCle2,...".
" - en DELETE : mots-cles/#idMotCle";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemin = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$retour = '';
$service = null;
if (file_exists($chemin)) {
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);
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/Protocoles.php
New file
0,0 → 1,127
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services de Protocoles.
*
* Urls possibles :
* http://localhost/service:del:0.1/protocoles => tous les protocoles
*
* @category DEL
* @package Services
* @subpackage Protocoles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Protocoles extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'protocoles';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if (count($this->ressources) == 0) {
$this->sousServiceNom = 'liste-protocoles';
}
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : protocoles (paramètres : aucun) \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
 
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter();
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"est introuvable par le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/Utilisateurs.php
New file
0,0 → 1,146
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services liés aux utilisateurs.
*
* @category DEL
* @package Services
* @subpackage Utilisateurs
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Utilisateurs extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'utilisateurs';
private $sousServiceNom = null;
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;
$this->conteneur = new Conteneur();
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$resultat = $this->traiterRessources();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
 
private function traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
//S'il n'y a pas de ressources => envoyer sur identification anonyme
if (!isset($this->ressources) || empty($this->ressources)) {
$this->sousServiceNom = 'identification';
} else {
if(count($this->ressources) == 2 && $this->ressources[1] == 'preferences') {
$this->sousServiceNom = 'preferences';
} else if(count($this->ressources) == 2 && $this->ressources[1] == 'activite') {
$this->sousServiceNom = 'activite';
} else {
$this->sousServiceNom = 'identification';
}
}
} else if ($this->methode == 'modifier') {
if (count($this->ressources) == 2 && $this->ressources[1] == 'preferences') {
$this->sousServiceNom = 'preferences';
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" * en GET : \n".
" - utilisateurs : identification anonyme à partir des cookies (par défaut quelque soit l'url)\n".
" - utilisateurs/#login/#mot-de-passe : connecte l'utilisateur\n".
" - utilisateurs/deconnecter : déconnecte l'utilisateur actuellement connecté par cookie\n".
" - utilisateurs/#id/preferences : fourni les préférences de l'utilisateur #id\n".
" * en POST : \n".
" - utilisateurs/#id/preferences : permet de modifier \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources, $this->parametres);
} elseif ($this->methode == 'modifier') {
$retour = $service->modifier($this->ressources, $this->parametres);
}
}
}
 
if (is_null($service)) {
$ressource = $this->serviceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/syndication/Tags.php
New file
0,0 → 1,219
<?php
// declare(encoding='UTF-8');
/**
* Service fournissant des informations concernant les tags sur les images de DEL en fonction d'un protocole
* au format RSS1, RSS2 ou ATOM.
*
* @category DEL
* @package Services
* @subpackage Syndication
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Tags {
 
private $categorie = 'Tag';
private $conteneur;
private $bdd;
private $navigation;
private $syndication;
private $mapping = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->navigation = $this->conteneur->getNavigation();
$this->syndication = $this->conteneur->getSyndicationOutils();
$this->mapping = $this->conteneur->getParametreTableau('syndication.mapping');
}
 
public function consulter() {
if ($this->syndication->fluxAdminDemande()) {
$this->syndication->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersVotesImage();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
/**
* Formater les données pour mettre en page le RSS
* */
private function formaterPourRss($elements) {
$derniere_info_en_date = reset($elements);
$donnees = $this->syndication->construireDonneesCommunesAuFlux('tag', $derniere_info_en_date['date']);
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
/**
* 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'] = htmlentities($this->categorie);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->formaterNomTagueur($info);
return $item;
}
 
private function creerGuidItem($element) {
$guid = sprintf($this->conteneur->getParametre('syndication.tag_guid_tpl'), $element['id_tag']);
return $guid;
}
 
private function creerLienItem($element) {
// TODO : ajouter un lien vers la plateforme validation de picto lorsqu'elle sera dispo
$lien = sprintf($this->conteneur->getParametre('img_fiche_tpl'), $element['id_image']);
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->syndication->getUrlImage($donnees['id_image']);
$miniatureUrl = $this->syndication->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 creerAuteurImg($info) {
$auteur = $this->formaterNomAuteurImg($info).
($this->syndication->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->syndication->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() {
$elements = array();
$idsTags = $this->getIdsDerniersVotesImage();
if (!empty($idsTags)) {
$idsTagsConcat = implode(', ', $idsTags);
$requete = 'SELECT DISTINCT id_tag, tag, date, '.
' do.id_observation, do.nom_sel, do.zone_geo, do.date_observation, di.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_image AS di '.
' ON ce_image = id_image '.
' INNER JOIN del_observation AS do '.
' ON di.ce_observation = do.id_observation '.
' LEFT JOIN del_utilisateur_infos AS duo '.
' ON do.ce_utilisateur = duo.id_utilisateur '.
' LEFT JOIN del_utilisateur_infos AS du '.
' ON if((CHAR_LENGTH(dit.ce_utilisateur) <> 32),CAST(dit.ce_utilisateur AS unsigned),0) '.
' = du.id_utilisateur '.
'WHERE actif = 1 '.
" AND id_tag IN ($idsTagsConcat) ".
'ORDER BY date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
$elements = $this->bdd->recupererTous($requete);
}
return $elements;
}
 
/**
* Retrouver les ids des derniers votes image
* */
private function getIdsDerniersVotesImage() {
$requete = 'SELECT DISTINCT id_tag '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
// Pas de filtre pour ce service. Utiliser le principe des autres ws de syndication si on devait en rajouter.
'ORDER BY date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
 
$idsTags = array();
if ($resultats != false && is_array($resultats)) {
foreach ($resultats as $infos) {
$idsTags[] = $infos['id_tag'];
}
}
return $idsTags;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+0aaf0e201a3d29d801b57d08d5878bb16dbdbb41
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/syndication/Votes.php
New file
0,0 → 1,228
<?php
// declare(encoding='UTF-8');
/**
* Service fournissant des informations concernant les votes sur les images de DEL en fonction d'un protocole
* au format RSS1, RSS2 ou ATOM.
*
* @category DEL
* @package Services
* @subpackage Syndication
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Votes {
 
private $categorie = 'Vote protocole';
private $conteneur;
private $bdd;
private $navigation;
private $syndication;
private $mapping = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->navigation = $this->conteneur->getNavigation();
$this->syndication = $this->conteneur->getSyndicationOutils();
$this->mapping = $this->conteneur->getParametreTableau('syndication.mapping');
}
 
public function consulter() {
if ($this->syndication->fluxAdminDemande()) {
$this->syndication->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersVotesImage();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
/**
* Formater les données pour mettre en page le RSS
* */
private function formaterPourRss($elements) {
$derniere_info_en_date = reset($elements);
$donnees = $this->syndication->construireDonneesCommunesAuFlux('vote', $derniere_info_en_date['date_vote']);
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
/**
* 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'] = htmlentities($this->categorie);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->creerVotant($info);
return $item;
}
 
private function creerGuidItem($element) {
$guid = sprintf($this->conteneur->getParametre('syndication.vote_guid_tpl'), $element['id_vote']);
return $guid;
}
 
private function creerLienItem($element) {
$lien = sprintf($this->conteneur->getParametre('img_fiche_tpl'), $element['ce_image']);
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->syndication->getUrlImage($donnees['ce_image']);
$miniatureUrl = $this->syndication->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 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() {
$elements = array();
$idsVotes = $this->getIdsDerniersVotesImage();
if (!empty($idsVotes)) {
$idsVotesConcat = implode(', ', $idsVotes);
$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_image AS di '.
' ON divo.ce_image = di.id_image '.
' INNER JOIN del_observation AS do '.
' ON di.ce_observation = do.id_observation '.
' INNER JOIN del_image_protocole AS dip '.
' ON ce_protocole = id_protocole '.
' LEFT JOIN del_utilisateur_infos AS duo '.
' ON do.ce_utilisateur = duo.id_utilisateur '.
' LEFT JOIN del_utilisateur_infos AS duv '.
' ON if((CHAR_LENGTH(divo.ce_utilisateur) <> 32),CAST(divo.ce_utilisateur AS unsigned),0) '.
' = duv.id_utilisateur '.
"WHERE id_vote IN ($idsVotesConcat) ".
'ORDER BY divo.date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().
' -- '.__FILE__.' : '.__LINE__;
$elements = $this->bdd->recupererTous($requete);
}
return $elements;
}
 
/**
* Retrouver les ids des derniers votes image
* */
private function getIdsDerniersVotesImage() {
$clauseWhere = $this->chargerClauseWhere();
$requete = 'SELECT DISTINCT id_vote '.
'FROM del_image_vote '.
($clauseWhere != '' ? "WHERE $clauseWhere " : '').
'ORDER BY date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
 
$idsVotes = array();
if ($resultats != false && is_array($resultats)) {
foreach ($resultats as $infos) {
$idsVotes[] = $infos['id_vote'];
}
}
return $idsVotes;
}
 
private function chargerClauseWhere() {
$where = array();
$filtres = $this->navigation->getFiltre();
if (!empty($filtres)) {
$filtrePossibles = $this->conteneur->getParametreTableau('syndication.vote_filtres');
foreach ($filtres as $cleFiltre => $valeur) {
if (in_array($cleFiltre, $filtrePossibles)) {
$champ = $this->mapping[$cleFiltre];
$valeurP = $this->bdd->proteger($valeur);
$where[] = " $champ = $valeurP ";
}
}
}
 
return (!empty($where)) ? implode('AND', $where) : '';
}
}
/branches/v1.11-magnesium/services/modules/0.1/syndication/Commentaires.php
New file
0,0 → 1,265
<?php
// declare(encoding='UTF-8');
/**
* Service fournissant des informations concernant les commentaire de DEL au format RSS1, RSS2 ou ATOM.
*
* @category DEL
* @package Services
* @subpackage Syndication
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Commentaires {
 
private $categorie = 'Commentaires';
private $conteneur;
private $contexte;
private $bdd;
private $navigation;
private $mapping = array();
private $syndication;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->contexte = $this->conteneur->getContexte();
$this->bdd = $this->conteneur->getBdd();
$this->navigation = $this->conteneur->getNavigation();
$this->syndication = $this->conteneur->getSyndicationOutils();
$this->mapping = $this->conteneur->getParametreTableau('syndication.mapping');
}
 
public function consulter() {
if ($this->syndication->fluxAdminDemande()) {
$this->syndication->demanderAutorisationAdmin();
}
 
$donnees_brutes = $this->getDerniersCommentaires();
$commentaires_formates = $this->formaterPourRss($donnees_brutes) ;
return $commentaires_formates;
}
 
private function formaterPourRss($elements) {
$derniere_info_en_date = reset($elements);
$donnees = $this->syndication->construireDonneesCommunesAuFlux('commentaire', $derniere_info_en_date['date']);
foreach ($elements as $element) {
$donnees['items'][] = $this->construireDonneesCommunesAuxItems($element);
}
return $donnees;
}
 
private function construireDonneesCommunesAuxItems($info) {
$item = array();
$date_modification_timestamp = $this->syndication->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'] = htmlentities($this->categorie);
$item['description'] = $this->creerDescription($info, $item);
$item['description_encodee'] = htmlspecialchars($this->creerDescription($info, $item));
$item['modifier_par'] = $this->creerAuteur($info);
return $item;
}
 
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'] : null));
$dateObs = null;
if (strpos($element['dob_date_observation'], '0000') === false) {
$dateObs = htmlspecialchars(strftime('%d %B %Y', strtotime($element['dob_date_observation'])));
}
 
$titre = "$intitule par $auteur pour $nomSelActuel";
$titre .= ($zoneGeo != null) ? " à $zoneGeo" : "";
$titre .= ($dateObs != null) ? " 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('syndication.commentaire_guid_tpl'), $element['id_commentaire']);
return $guid;
}
 
private function creerLienItem($element) {
$lien = sprintf($this->conteneur->getParametre('obs_fiche_tpl'), $element['dob_id_observation']);
return $lien;
}
 
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->syndication->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->syndication->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() {
$commentaires = array();
$idsCommentaires = $this->getIdsDerniersCommentaires();
if (!empty($idsCommentaires)) {
$idsCommentairesconcat = implode(', ', $idsCommentaires);
$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, '.
' dob.prenom_utilisateur AS observateur_prenom, dob.nom_utilisateur AS observateur_nom, dob.courriel_utilisateur AS observateur_courriel '.
'FROM del_commentaire AS dc '.
' INNER JOIN del_observation AS dob '.
' ON dob.id_observation = dc.ce_observation '.
"WHERE id_commentaire IN ($idsCommentairesconcat) ".
'ORDER BY dc.date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
$commentaires = $this->bdd->recupererTous($requete);
}
return $commentaires;
}
 
private function getIdsDerniersCommentaires() {
$clauseWhere = $this->chargerClauseWhere();
$requete = 'SELECT DISTINCT dc.id_commentaire '.
'FROM del_commentaire AS dc '.
' INNER JOIN del_observation AS dob ON (dc.ce_observation = dob.id_observation) '.
'WHERE proposition_initiale != 1 '.
'AND dc.date >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) '.
($clauseWhere != '' ? "AND $clauseWhere " : '').
'ORDER BY dc.date DESC '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
//echo $requete;
$resultats = $this->bdd->recupererTous($requete);
 
$idsCommentaires = array();
if ($resultats != false && is_array($resultats)) {
foreach ($resultats as $infos) {
$idsCommentaires[] = $infos['id_commentaire'];
}
}
return $idsCommentaires;
}
 
/**
* Charger la clause WHERE en fonction des paramètres de masque
* */
private function chargerClauseWhere() {
$where = array();
$filtres = $this->navigation->getFiltre();
if (!empty($filtres)) {
$filtrePossibles = $this->conteneur->getParametreTableau('syndication.commentaire_filtres');
foreach ($filtres as $cleFiltre => $valeur) {
if (in_array($cleFiltre, $filtrePossibles)) {
if (isset($this->mapping[$cleFiltre])) {
$champ = $this->mapping[$cleFiltre];
}
switch ($cleFiltre) {
case 'espece':
$valeurP = $this->bdd->proteger($valeur.'%');
$where[] = " (dob.$champ LIKE $valeurP OR dc.$champ LIKE $valeurP) ";
break;
case 'auteur':
$where[] = $this->creerFiltreAuteur($valeur);
break;
default:
$valeurP = $this->bdd->proteger($valeur);
$where[] = " $champ = $valeurP ";
}
}
}
}
return (!empty($where)) ? implode(' AND ', $where) : '';
}
 
private function creerFiltreAuteur($auteurId) {
$whereAuteur = '';
if (is_numeric($auteurId)) {
$whereAuteur = " dc.ce_utilisateur = $auteurId ";
} else {
$auteurIdMotif = $this->bdd->proteger($auteurId.'%');
 
if (strpos($auteurId, '@') !== false) {
$whereAuteur = " dc.utilisateur_courriel LIKE $auteurIdMotif ";
} else {
$tableauNomPrenom = explode(' ', $auteurId, 2);
if (count($tableauNomPrenom) == 1) {
$whereAuteur = " (dc.utilisateur_nom LIKE $auteurIdMotif OR dc.utilisateur_prenom LIKE $auteurIdMotif) ";
} else if (count($tableauNomPrenom) == 2) {
// on teste potentiellement un nom prenom ou bien un prénom nom
$nomMotif = $this->bdd->proteger($tableauNomPrenom[0].'%');
$prenomMotif = $this->bdd->proteger($tableauNomPrenom[1].'%');
 
$whereAuteur = ' ('.
"(dc.utilisateur_nom LIKE $nomMotif AND dc.utilisateur_prenom LIKE $prenomMotif) ".
'OR '.
"(dc.utilisateur_nom LIKE $prenomMotif AND dc.utilisateur_prenom LIKE $nomMotif) ".
') ';
}
}
}
return $whereAuteur;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+9ac625ab6837ca8768dbe4ffc3ec13c73f2b5009
\ No newline at end of property
/branches/v1.11-magnesium/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.11-magnesium/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.11-magnesium/services/modules/0.1/syndication/squelettes/rss2.tpl.xml
New file
0,0 → 1,25
<?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.11-magnesium/services/modules/0.1/syndication/squelettes/atom.tpl.xml
New file
0,0 → 1,33
<?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.11-magnesium/services/modules/0.1/determinations/ListeImagesDeterminationsProbables.php
New file
0,0 → 1,361
<?php
// declare(encoding='UTF-8');
/**
* Web service récupèrant toutes les données de la table del_obs_images
* pour retourner une liste d'images associées à la détermination la plus probable.
*
* Possibilité de ne renvoyer que les images les mieux notées pour un protocole donné.
*
* @category DEL
* @package Services
* @subpackage Determinations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ListeImagesDeterminationsProbables {
 
private $indexImagesIds = array();
private $conteneur;
private $navigation;
private $bdd;
 
private $erreurs = array();
private $parametres = array();
private $protocoles = array();
 
private $idsImagesOrdonnees = array();
private $resultats = array();
private $propositions = array();
private $votes = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
$this->chargerProtocoles();
}
 
private function chargerProtocoles() {
$requete = 'SELECT id_protocole FROM del_image_protocole -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
if ($resultats) {
foreach ($resultats as $infos) {
$this->protocoles[] = $infos['id_protocole'];
}
sort($this->protocoles);
}
}
 
public function consulter($parametres) {
$this->parametres = $parametres;
$this->verifierParametres();
 
// Lancement du service
$this->idsImagesOrdonnees = $this->getIdsImages();
$this->modifierEnteteTotal();
$infos = $this->chargerInfos();
if ($infos) {
$this->traiterResultats($infos);
$this->completerResutlats();
}
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $this->resultats);
return $resultat;
}
 
private function verifierParametres() {
$this->verifierParamProtocole();
$this->verifierParamVote();
 
if (!empty($this->erreurs)) {
$msg = "Erreur de configuration :\n".implode("\n\n", $this->erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
private function verifierParamProtocole() {
if (isset($this->parametres['masque.protocole'])) {
$protocoleExistant = in_array($this->parametres['masque.protocole'], $this->protocoles);
 
if (!is_numeric($this->parametres['masque.protocole']) || $protocoleExistant === false) {
$protocolesListe = implode(', ', $this->protocoles);
$msg = "La valeur pour le protocole doit être un entier compris dans les numéros de protocole ".
"existants : $protocolesListe";
$this->erreurs[] = $msg;
}
}
}
 
private function verifierParamVote() {
if (isset($this->parametres['masque.valeur_min_vote'])) {
$minVote = $this->parametres['masque.valeur_min_vote'];
if (!is_numeric($minVote) || ($minVote < 0 && $minVote > 5)) {
$this->erreurs[] = "La valeur minimum de valeur des votes doit être un entier compris entre 0 et 5.";
}
}
}
 
private function getIdsImages() {
$whereClause = $this->getClauseWhere();
$depart = $this->navigation->getDepart();
$limite = $this->navigation->getLimite();
 
$requete = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS di.id_image '.
'FROM del_image AS di '.
' LEFT JOIN del_image_stat AS ds ON (di.id_image = ds.ce_image) '.
"$whereClause ".
'ORDER BY moyenne DESC, id_image DESC '.
"LIMIT $depart, $limite ".
' -- '.__FILE__.' : '.__LINE__;
//Debug::printr($requete);
$resultats = $this->bdd->recupererTous($requete);
$idImgs = array();
if ($resultats !== false ) {
foreach ($resultats as $resultat) {
$idImgs[] = $resultat['id_image'];
}
}
return $idImgs;
}
 
private function getClauseWhere() {
$where = array();
if (isset($this->parametres['masque.protocole'])) {
$protocoleIdP = $this->bdd->proteger($this->parametres['masque.protocole']);
$where[] = "ds.ce_protocole = $protocoleIdP ";
}
if (isset($this->parametres['masque.valeur_vote_min'])) {
$voteP = $this->bdd->proteger($this->parametres['masque.valeur_vote_min']);
$where[] = "moyenne >= $voteP";
}
return (count($where) > 0) ? 'WHERE '.implode(' AND ', $where).' ' : '';
}
 
private function chargerInfos() {
$idImgsConcat = implode(',', $this->idsImagesOrdonnees);
 
$requete = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS '.
'di.id_image, di.mots_cles_texte AS mots_cles_texte_image, '.
'do.id_observation, nom_referentiel, nom_ret, '.
'nom_ret_nn, nt, famille, ce_zone_geo, zone_geo, date_observation, '.
'do.ce_utilisateur, do.nom_utilisateur, do.prenom_utilisateur, '.
'di.prenom_utilisateur as prenom, di.nom_utilisateur as nom '. // retrocompatibilité redondance cheloute
'FROM del_image AS di '.
' INNER JOIN del_observation AS do ON (di.ce_observation = do.id_observation) '.
' LEFT JOIN del_image_stat AS ds ON (di.id_image = ds.ce_image) '.
"WHERE di.id_image IN ($idImgsConcat) ".
'ORDER BY moyenne DESC, id_image DESC '.
' -- '.__FILE__.' : '.__LINE__;
//Debug::printr($requete);
return $this->bdd->recupererTous($requete);
}
 
private function modifierEnteteTotal() {
$requete = 'SELECT FOUND_ROWS() AS nbre -- '.__FILE__.' : '.__LINE__;
$compte = $this->bdd->recuperer($requete);
$total = ($compte !== false) ? (int) $compte['nbre'] : 0;
$this->navigation->setTotal($total);
}
 
/**
* Retourner un tableau d'images formaté en fonction des liaisons trouvées
* @param $infos les infos sur les images et observations
* */
private function traiterResultats($infos) {
//Debug::printr($infos);
foreach ($infos as $info) {
$idImage = $info['id_image'];
$index = $this->formaterIndexResultat($info);
 
$this->obsIds[] = $info['id_observation'];
$this->indexImagesIds[$idImage] = $index;
$this->resultats[$index] = array(
'id_image' => $idImage,
'id_observation' => $info['id_observation'],
'auteur.intitule' => $this->formaterIntituleAuteur($info),
'binaire.href' => $this->formaterBinaireHref($info),
'determination.famille' => $info['famille'],
'determination.referentiel' => $info['nom_referentiel'],
'determination.ns' => $info['nom_ret'],
'determination.nn' => $info['nom_ret_nn'],
'determination.nt' => $info['nt'],
'date_observation' => $info['date_observation'],
'localite' => $this->formaterLieu($info),
'mots_cles_image_cel' => $this->formaterMotsClesCel($info),
'mots_cles_image_del' => ''
);
}
}
 
private function formaterIndexResultat($infos) {
return $infos['id_image'].'-'.$infos['id_observation'];
}
 
private function formaterIntituleAuteur($infos) {
if ($infos['ce_utilisateur'] == 0) {
$infos['prenom'] = $infos['prenom_utilisateur'];
$infos['nom'] = $infos['nom_utilisateur'];
}
$intitule = $infos['prenom'].' '.$infos['nom'];
return $intitule;
}
 
private function formaterBinaireHref($infos) {
return sprintf(
$this->conteneur->getParametre('cel_img_url_tpl'),
$infos['id_image'],
$this->conteneur->getParametre('determinations.format_image')
);
}
 
private function formaterLieu($infos) {
$lieuFormate = '';
if ($infos['ce_zone_geo']) {
$lieu = $infos['zone_geo'];
$id_zone_geo = $infos['ce_zone_geo'];
if (strpos($infos['ce_zone_geo'], 'INSEE-C:') === 0) {
$id_zone_geo = str_replace('INSEE-C:', '', $infos['ce_zone_geo']);
$id_zone_geo = strlen($id_zone_geo) >= 5 ? substr($id_zone_geo, 0, 2) : $id_zone_geo;
}
$lieuFormate = "$lieu ($id_zone_geo)";
}
return $lieuFormate;
}
 
/**
* 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 $infos le tableau contenant les infos sur une image.
* @return string la chaine filtrée
*/
private function formaterMotsClesCel($infos) {
$motsClesAffiches = $this->conteneur->getParametreTableau('determinations.mots_cles_cel_affiches');
$motsClesCel = explode(',', $infos['mots_cles_texte_image']);
$motsCles = array_intersect($motsClesAffiches, $motsClesCel);
return implode(',', $motsCles);
}
 
private function completerResutlats() {
$this->chargerVotes();
$this->chargerPropositions();
 
$this->completerMotsCles();
 
foreach ($this->resultats as $index => $infos) {
if ($this->doitRemplacerObservationParProposition($index)) {
$id_obs = $infos['id_observation'];
$this->resultats[$index]['determination.famille'] = $this->propositions[$id_obs]['famille'];
$this->resultats[$index]['determination.ns'] = $this->propositions[$id_obs]['nom_sel'];
$this->resultats[$index]['determination.nn'] = $this->propositions[$id_obs]['nom_sel_nn'];
$this->resultats[$index]['determination.nt'] = $this->propositions[$id_obs]['nt'];
$this->resultats[$index]['determination.referentiel'] = $this->propositions[$id_obs]['nom_referentiel'];
}
$this->completerUrlFicheEflore($index);
}
}
 
private function chargerVotes() {
$idsObs = implode(',', $this->obsIds);
$requete = 'SELECT ce_proposition, valeur, ce_utilisateur '.
'FROM del_commentaire_vote '.
'WHERE ce_proposition IN '.
'( SELECT id_commentaire '.
' FROM del_commentaire '.
" WHERE ce_observation IN ($idsObs) AND nom_sel IS NOT NULL ) ".
'ORDER BY ce_proposition '.
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
if ($resultats !== false) {
foreach ($resultats as $vote) {
if (!isset($this->votes[$vote['ce_proposition']])) {
$this->votes[$vote['ce_proposition']] = 0;
}
$valeur = ($vote['valeur'] == 1) ? 1 : -1;
$this->votes[$vote['ce_proposition']] += is_numeric($vote['ce_utilisateur']) ? 3 * $valeur : $valeur;
}
}
}
 
private function chargerPropositions() {
$idsObs = implode(',', $this->obsIds);
$requete = 'SELECT * '.
'FROM del_commentaire '.
"WHERE ce_observation IN ($idsObs) ".
'AND nom_sel IS NOT NULL '.
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
 
foreach($resultats as $proposition) {
$id_proposition = $proposition['id_commentaire'];
$id_obs = $proposition['ce_observation'];
$proposition['valeur'] = (isset($this->votes[$id_proposition])) ? $this->votes[$id_proposition] : -1;
 
if (!isset($this->propositions[$id_obs])) {
$this->propositions[$id_obs] = $proposition;
} else {
$score_actuel = $proposition['valeur'];
$score_precedent = $this->propositions[$id_obs]['valeur'];
if ($score_actuel >= $score_precedent) {
$this->propositions[$id_obs] = $proposition;
}
}
}
}
 
private function completerMotsCles() {
$idsImages = implode(',', array_keys($this->indexImagesIds));
$requete = 'SELECT tag, ce_image '.
'FROM del_image_tag '.
"WHERE ce_image IN ($idsImages) ".
'AND actif = 1 '.
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
 
foreach ($resultats as $info) {
$index = $this->indexImagesIds[$info['ce_image']];
$tag = ($this->resultats[$index]['mots_cles_image_del'] != '') ? ','.$info['tag'] : $info['tag'];
$this->resultats[$index]['mots_cles_image_del'] .= $tag ;
}
}
 
private function doitRemplacerObservationParProposition($index) {
$idObs = $this->resultats[$index]['id_observation'];
return ((isset($this->propositions[$idObs])
&& $this->propositions[$idObs] != null
&& $this->propositions[$idObs]['nom_sel_nn'] != 0)
&& ($this->propositions[$idObs]['valeur'] > 0 || $this->resultats[$index]['determination.nn'] == 0)
);
}
 
private function completerUrlFicheEflore($index) {
if (isset($this->resultats[$index]['determination.nn'])) {
$urlTpl = $this->conteneur->getParametre('determinations.url_fiche_eflore_tpl');
$nn = (int) $this->resultats[$index]['determination.nn'];
$ref = $this->resultats[$index]['determination.referentiel'];
$code_ref = $this->getCodeReferentiel($ref);
if (is_int($nn) && ! isset($code_ref)) {
$code_ref = 'bdtfx';
}
$this->resultats[$index]['url_fiche_eflore'] = sprintf($urlTpl, $code_ref, $nn);
}
}
 
private function getCodeReferentiel($ref) {
$code = $ref;
if ($position = strpos($ref, '_')) {
$code = substr($ref, 0, $position);
} else if ($position = strpos($ref, ':')) {
$code = substr($ref, 0, $position);
}
return $code;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+474012b08dfb5d9bd5f4a18e3e5eeb6903cfb084
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/determinations/squelettes/validation_determination.tpl.txt
New file
0,0 → 1,16
IdentiPlante : un telabotaniste vous a aidé
 
Bonjour <?= $auteur_obs_fmt ?>,
Un telabotaniste vous a aidé.
 
<?= $validateur_fmt; ?> (<?= $lien_profil ?>) a validé la détermination proposée par le réseau
sur votre observation <?= $id_obs; ?> (<?= $lien_obs; ?>")
 
Pour savoir pourquoi ce telabotaniste a validé votre observation suivez ce lien : <?= $lien_wiki; ?>.
 
 
Si vous avez besoin d'un renseignement écrivez nous à identiplante_remarque@tela-botanica.org
 
Bonne continuation sur nos outils botaniques !
---------
L'équipe de Tela Botanica
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/determinations/squelettes/validation_determination.tpl.html
New file
0,0 → 1,78
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
<!--
body {
font-family: Arial,Helvetica,Verdana,sans-serif;
font-size: 14px;
}
h1 > a {
color:#fd8c13;
}
.emphase {
color: #777777;
}
h3 {
font-size:14px;
color:#454341 !important;
margin:0px;
}
h3 > a {
color:#454341;
}
h1 a, h3 a, h4 a {
color: inherit;
border-bottom: dotted 1px;
}
h3 a {
border-color:#CCCCCC;
}
a {
cursor: pointer;
color:#598000;
text-decoration:none;
border-bottom:1px dotted #95ae5d;
}
a:hover {
color:#FD8C13;
border-bottom:1px dotted #95ae5d;
}
.lire-suite {
padding-left:10px;
background:url(http://www.tela-botanica.org/sites/commun/generique/images/graphisme/petit_carre.png) no-repeat 0px 4px;
}
.description_message {
color: #333333;
font-size: 1.1em;
font-style: italic;
padding-top: 10px;
}
-->
</style>
</head>
<body>
<div>
<h1><span class="emphase">IdentiPlante</span> : un telabotaniste vous a aid&eacute; </h1>
</div>
<div class="description_message">
Bonjour <?= $auteur_obs_fmt ?>,<br />
Un telabotaniste vous a aidé.
<br />
<br />
<a href="<?= $lien_profil ?>"><?= $validateur_fmt; ?></a> a validé la détermination proposée par le réseau sur votre observation <a href="<?= $lien_obs; ?>"><?= $id_obs; ?></a> .
<br />
Pour savoir pourquoi ce telabotaniste a validé votre observation cliquez ici : <a href="<?= $lien_wiki; ?>"><?= $lien_wiki; ?></a>.
<br />
</div>
<br />
<br />
Si vous avez besoin d'un renseignement écrivez nous à identiplante_remarque@tela-botanica.org
<br />
Bonne continuation sur nos outils botaniques !<br />
<div>---------</div>
L'&eacute;quipe de Tela Botanica<br />
<br/>
</body>
</html>
/branches/v1.11-magnesium/services/modules/0.1/determinations/ValiderDetermination.php
New file
0,0 → 1,198
<?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 DEL
* @package Services
* @subpackage Determinations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ValiderDetermination {
 
private $conteneur;
private $bdd;
private $idObs = null;
private $idProposition = null;
private $idAuteurObs = null;
private $idValidateurObs = null;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->bdd = $this->conteneur->getBdd();
}
 
public function modifier($ressources, $parametres) {
$this->verifierParametres($ressources, $parametres);
$this->idProposition = $ressources[1];
$this->idValidateurObs = $this->validateurEstPresent($parametres) ? $parametres['validateur.id'] : $parametres['auteur.id'] ;
$retourCel = $this->modifierObservationParDetermination();
 
if (preg_match('/^(OK|Not Modified)$/i', $retourCel) == false) {
$msg = "Erreur: le web service du CEL a retourné : $retourCel";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($msg, $code);
}
return 'OK';
}
 
private function validateurEstPresent($parametres) {
return isset($parametres['validateur.id']) && is_numeric($parametres['validateur.id']) && $parametres['validateur.id'] != 0;
}
 
private function verifierParametres($ressources, $parametres) {
$erreurs = array();
if (!is_numeric($ressources[1])) {
$erreurs[] = "La ressource 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[] = "Le paramètre 'auteur.id' est manquant.";
}
// Le paramètre validateur.id (id de la personne validant la détemrinatiuon)
// est là pour éviter que le $_POST ne soit vide
if (isset($parametres['validateur.id']) && !is_numeric($parametres['validateur.id'])) {
$erreurs[] = "Le paramètre 'validateur.id' doit être un entier.";
}
 
if ($erreurs) {
$msg = "Erreur dans les paramètres d'appel au web service :\n\n" . implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* 1) récupère l'observation, autrement échec
* 2) récupère et l'id auteur de l'obs et vérifie qu'il correspond à l'id de l'utilisateur actuel
* 3) prépare les nouvelles valeurs à transférer au service CEL
* 4) effectue la mise à jour
* 5) si tout s'est passé comme convenu, marque la proposition comme "retenue"
*/
private function modifierObservationParDetermination() {
$propositionInfos = $this->getInfosProposition();
$this->idObs = $propositionInfos['ce_observation'];
$this->idAuteurObs = $this->getIdAuteurObs();
$this->verifierDroitUtilisateur();
 
$parametres = array(
'id_observation' => $this->idObs,
'nom_sel_nn' => $propositionInfos['nom_sel_nn'],
'nom_referentiel' => $propositionInfos['nom_referentiel']
);
 
$urlBase = $this->conteneur->getParametre('urlServiceCelObs');
$url = $urlBase.$this->idObs;
$retour = $this->conteneur->getRestClient()->modifier($url, $parametres);
 
// TODO: check sur HTTP code == 200, plutôt que sur texte
if ($retour == 'ok' || $retour == 'OK') {
$this->mettreAJourPropositionRetenue();
if($this->idAuteurObs != $this->idValidateurObs) {
$this->avertirAuteurObservation();
}
}
return $retour;
}
 
private function getInfosProposition() {
$idPropositionP = $this->bdd->proteger($this->idProposition);
$requete = "SELECT id_commentaire, ce_observation, nom_sel_nn, nom_referentiel ".
'FROM del_commentaire '.
"WHERE id_commentaire = $idPropositionP ".
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recuperer($requete);
if (! $resultat) {
throw new Exception("Cette proposition est invalide.", RestServeur::HTTP_CODE_ERREUR);
}
return $resultat;
}
 
private function getIdAuteurObs() {
$obsIdP = $this->bdd->proteger($this->idObs);
$requete = 'SELECT ce_utilisateur '.
'FROM del_observation '.
"WHERE id_observation = $obsIdP ".
' -- '.__FILE__.' : '.__LINE__;
$auteurInfo = $this->bdd->recuperer($requete);
return $auteurInfo['ce_utilisateur'];
}
private function getInfosObs() {
$obsIdP = $this->bdd->proteger($this->idObs);
$requete = 'SELECT * '.
'FROM del_observation '.
"WHERE id_observation = $obsIdP ".
' -- '.__FILE__.' : '.__LINE__;
$obsInfos = $this->bdd->recuperer($requete);
return $obsInfos;
}
 
private function verifierDroitUtilisateur() {
$gestionUtilisateur = $this->conteneur->getUtilisateur();
$utilisateurInfos = $gestionUtilisateur->getUtilisateur();
$utilisateurId = $utilisateurInfos['id_utilisateur'];
 
// si l'utilisateur connecté n'est ni auteur de l'observation, ni au moins administrateur de niveau 1
if (($this->idAuteurObs != $utilisateurId) && $utilisateurInfos['admin'] < 1) {
$msg = "Seul l'utilisateur ayant saisi l'observation, un administrateur ou un validateur peut la valider : veuillez vous identifier.\n";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
/**
* Remet à 0 le status "proposition_retenue" pour toutes les propositions faites sur cette
* observation à l'exception de celle désormais validée (qui voit son ce_validateur et sa date validation mise à jour)
*/
private function mettreAJourPropositionRetenue() {
$requete = 'UPDATE del_commentaire '.
"SET proposition_retenue = IF(id_commentaire = {$this->idProposition}, 1, 0), ".
"ce_validateur = IF(id_commentaire = {$this->idProposition}, {$this->idValidateurObs} , ce_validateur), ".
"date_validation = IF(id_commentaire = {$this->idProposition}, NOW() , date_validation) ".
"WHERE ce_observation = {$this->idObs} ".
' -- '.__FILE__.' : '.__LINE__;
 
return $this->bdd->requeter($requete);
}
private function avertirAuteurObservation() {
// le validateur est forcément celui qui est actuellement connecté
$gestionUtilisateur = $this->conteneur->getUtilisateur();
$utilisateurInfos = $gestionUtilisateur->getUtilisateur();
$infos_obs = $this->getInfosObs();
$donnees = array(
'auteur_obs_fmt' => $infos_obs['prenom_utilisateur'].' '.$infos_obs['nom_utilisateur'],
'validateur_fmt' => $utilisateurInfos['prenom']." ".$utilisateurInfos['nom'],
'lien_profil' => sprintf($this->conteneur->getParametre('message.lien_profil'), $this->idValidateurObs),
'id_obs' => $this->idObs,
'lien_obs' => sprintf($this->conteneur->getParametre('obs_fiche_tpl'), $this->idObs),
'lien_wiki' => $this->conteneur->getParametre('message.lien_wiki_validation')
);
$sujet = $this->conteneur->getParametre('message.titre_message_validation');
$squelettePhp = $this->conteneur->getSquelettePhp();
$squeletteHtml = dirname(__FILE__).DS.'squelettes'.DS.'validation_determination.tpl.html';
$corpsHtml = $squelettePhp->analyser($squeletteHtml, $donnees);
$squeletteTxt = dirname(__FILE__).DS.'squelettes'.DS.'validation_determination.tpl.txt';
$corpsTxt = $squelettePhp->analyser($squeletteTxt, $donnees);
$messagerie = $this->conteneur->getMessagerie();
$messagerie->envoyerHtml($infos_obs['courriel_utilisateur'],
$sujet,
$corpsHtml,
$corpsTxt);
}
}
/branches/v1.11-magnesium/services/modules/0.1/Ontologie.php
New file
0,0 → 1,89
<?php
// declare(encoding='UTF-8');
/**
* Classe de récupération d'ontologies diverses
*
* @category DEL
* @package Services
* @subpackage Ontologies
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Ontologie extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'ontologie';
 
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';
$this->conteneur = new Conteneur($parametres);
if(!isset($ressources[0])) {
$message = "Le nom de l'ontologie doit être renseigné ".
"Les ontologies disponibles sont : \n".
" - en GET : pays \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
} else {
$fonction = 'get'.ucwords($ressources[0]);
if(method_exists($this, $fonction)) {
$resultat = $this->$fonction();
$reponseHttp = new ReponseHttp();
$reponseHttp->setResultatService($resultat);
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
} else {
$message = "L'ontologie demandée n'existe pas ".
"Les ontologies disponibles sont : \n".
" - en GET : pays \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
}
}
public function getPays() {
$url = $this->conteneur->getParametre('url_service_base_eflore')."iso-3166-1/zone-geo?masque.statut=officiellement%20attribu%C3%A9&navigation.limite=1000";
$restClient = $this->conteneur->getRestClient();
$resultatJson = $restClient->consulter($url);
$liste_pays = json_decode($resultatJson, true);
 
$pays_fmt = array();
foreach($liste_pays['resultat'] as $pays) {
// Les pays renvoyé par le web service sont tous en majuscule
$nom = mb_convert_case($pays['nom'], MB_CASE_TITLE, 'UTF-8');
$pays_fmt[] = array('code_iso_3166_1' => $pays['code'], 'nom_fr' => $nom);
}
// Tri par nom plutot que par code
usort($pays_fmt, array($this, "trierPays"));
$resultat = new ResultatService();
$resultat->corps = $pays_fmt;
return $resultat;
}
protected function trierPays($a, $b) {
return strcmp($a['nom_fr'], $b['nom_fr']);
}
}
?>
/branches/v1.11-magnesium/services/modules/0.1/plantnet/Changements.php
New file
0,0 → 1,311
<?php
/**
* Le web service plantnet récupère toutes les infos de la vue del_plantnet.
* Ordonées par date de modification.
* Les images sont regroupées en observations.
* Les tags, les votes et les propositions de determinations sont intégrés à l'observation.
*
* @category DEL
* @package Services
* @subpackage Plantnet
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Samuel DUFOUR-KOWALSKI <samuel.dufour@cirad.fr>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class Changements {
 
private $conteneur;
private $navigation;
private $bdd;
 
private $parametres = array();
private $ressources = array();
private $date_defaut = '1900-01-01';
private $ordre_defaut = 'asc';
private $idsObsImg = array();
private $infosObsImg = array();
 
 
public function __construct(Conteneur $conteneur = null) {
/* restore_exception_handler(); */
/* restore_error_handler(); */
/* ini_set("display_errors", "1"); */
 
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->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['date'])) {
$this->parametres['date'] = $this->date_defaut;
}
 
if (! isset($parametres['ordre'])) {
$this->parametres['ordre'] = $this->ordre_defaut;
} else {
$parametres['ordre'] = strtolower($parametres['ordre']);
if (! in_array($parametres['ordre'], array('asc', 'desc'))) {
$this->parametres['ordre'] = $this->ordre_defaut;
}
}
 
// Lancement du service
$this->idsObsImg = $this->getIdsObsImg();
$infos = array();
$total = 0;
if ($this->idsObsImg) {
$total = $this->getTotal();
 
$this->infosObsImg = $this->recupererInfos();
$infos = $this->formaterInfos();
$infos = $this->chargerPropositionPlusProbable($infos);
$infos = $this->orderArray($infos);
}
$this->navigation->setTotal($total);
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $infos);
return $resultat;
}
 
/*-------------------------------------------------------------------------------
CHARGEMENT DES IMAGES
--------------------------------------------------------------------------------*/
 
private function getIdsObsImg() {
$date_debut = "'{$this->parametres['date']}'";
$limite = @min(intval($this->parametres['navigation.limite']), 1000);
$limite = $limite ? $limite : 100; // 0 => 10
$depart = intval(@$this->parametres['navigation.depart']);
$ordre = $this->parametres['ordre'];
 
$requete =
'SELECT SQL_CALC_FOUND_ROWS p.id_observation, p.id_image, '.
'GROUP_CONCAT(iv.valeur) AS votes, '.
'GROUP_CONCAT(DISTINCT tag) AS tags, '.
'modif_date '.
 
'FROM del_plantnet AS p '.
' JOIN del_observation_modif_date '.
' ON (p.id_observation = del_observation_modif_date.id_observation '.
' AND modif_date >= '.$date_debut.') '.
' LEFT JOIN del_image_vote AS iv '.
' ON (id_image = iv.ce_image AND iv.ce_protocole = 3) '.
' LEFT JOIN del_image_tag AS it '.
' ON (id_image = it.ce_image AND it.actif = 1) '.
' LEFT JOIN del_commentaire AS c '.
' ON (p.id_observation = c.ce_observation) '.
' LEFT JOIN del_commentaire_vote AS cv '.
' ON (c.id_commentaire = cv.ce_proposition) '.
'GROUP BY id_image, p.id_observation '.
'ORDER BY modif_date ' . $ordre . ' '.
'LIMIT '.$depart.', '.$limite.
' -- '.__FILE__.':'.__LINE__;
 
return $this->bdd->recupererTous($requete);
}
 
private function getTotal() {
$compte = $this->bdd->recuperer('SELECT FOUND_ROWS() AS nbre');
$total = (int) $compte['nbre'];
return $total;
}
 
// recupere les donnée associées (fait en 2 requetes pour optimiser)
private function recupererInfos() {
// recuperer les ids
$idsImg = array();
foreach ($this->idsObsImg as $ids) {
$id = $ids['id_image'];
$idsImg[] = $id;
}
$idsImgConcat = implode(',', $idsImg);
 
$requete = 'SELECT '.
'id_observation, id_image, '.
'nom_sel, '.
'nom_referentiel, nom_ret, famille, '.
'zone_geo, latitude, longitude, '.
'date_observation, date_creation, date_transmission, '.
'mots_cles_texte, '.
'ce_utilisateur, prenom_utilisateur, nom_utilisateur, courriel_utilisateur, '.
'i_mots_cles_texte AS mots_cles_texte_image, nom_original AS nom_image '.
'FROM del_plantnet AS p '.
"WHERE id_image IN ($idsImgConcat) ".
' -- '.__FILE__.':'.__LINE__;
// recuperer les donnees
$resultats = $this->bdd->recupererTous($requete);
 
// regroupe les données par id_image
$img_data = array();
foreach ($resultats as $infos) {
$idImg = $infos['id_image'];
$img_data[$idImg] = $infos;
}
return $img_data;
}
 
/**
* Retourner un tableau d'images formaté en fonction des liaisons trouvées
* @param $liaisons les liaisons de la table del_obs_images
*/
private function formaterInfos() {
// regroupe les observations
$obs = array();
$imgCelTpl = $this->conteneur->getParametre('cel_img_url_tpl');
foreach ($this->idsObsImg as $ids) {
$idobs = $ids['id_observation'];
$idimg = $ids['id_image'];
 
$imgdata = $this->infosObsImg[$idimg];
 
if (!isset($obs[$idobs])) {
$obs[$idobs] = array();
}
 
$obs[$idobs]['id_observation'] = $idobs;
$obs[$idobs]['auteur_id'] = $imgdata['ce_utilisateur'];
$obs[$idobs]['auteur_prenom'] = $imgdata['prenom_utilisateur'];
$obs[$idobs]['auteur_nom'] = $imgdata['nom_utilisateur'];
$obs[$idobs]['auteur_courriel'] = $imgdata['courriel_utilisateur'];
 
$obs[$idobs]['mots_cles_obs_cel'] = $this->formaterMotsClesCel($imgdata['mots_cles_texte']);
 
$obs[$idobs]['date_observation'] = $imgdata['date_observation'];
$obs[$idobs]['date_publication'] = $imgdata['date_transmission'];
$obs[$idobs]['date_creation'] = $imgdata['date_creation'];
$obs[$idobs]['date_changement'] = $ids['modif_date'];
 
$obs[$idobs]['nom_sel'] = $imgdata['nom_sel'];
$obs[$idobs]['nom_referentiel'] = $imgdata['nom_referentiel'];
$obs[$idobs]['nom_ret'] = $imgdata['nom_ret'];
//$obs[$idobs]['nn'] = $imgdata['nom_ret_nn'];
//$obs[$idobs]['nt'] = $imgdata['nt'];
$obs[$idobs]['famille'] = $imgdata['famille'];
 
$obs[$idobs]['zone_geo'] = $imgdata['zone_geo'];
$obs[$idobs]['latitude'] = floatval($imgdata['latitude']);
$obs[$idobs]['longitude'] = floatval($imgdata['longitude']);
 
if (!isset($obs[$idobs]['images'])) {
$obs[$idobs]['images'] = array();
}
 
$img_obj = array(
'id_image' => $idimg,
'nom_image' => $imgdata['nom_image'],
'url' => sprintf($imgCelTpl, $idimg, 'O'),
'votes' => array_map('intval', explode(',', $ids['votes'])),
'tags' => explode(',', $ids['tags']),
'mots_cles_img_cel' => $this->formaterMotsClesCel($imgdata['mots_cles_texte_image'])
);
// push
$obs[$idobs]['images'][] = $img_obj;
}
return $obs;
}
 
/**
* Charger les votes pour chaque image
*/
private function chargerPropositionPlusProbable(&$obs) {
$obsIds = array_keys($obs);
$idsObsConcat = implode(',', $obsIds);
 
$requete = 'SELECT ce_observation, id_commentaire, valeur, nom_sel, nom_sel_nn, nom_ret, cv.ce_utilisateur '.
'FROM del_commentaire_vote AS cv, del_commentaire AS c '.
"WHERE ce_observation IN ($idsObsConcat) ".
'AND nom_sel IS NOT NULL '.
'AND c.id_commentaire = cv.ce_proposition '.
' -- '.__FILE__.':'.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
 
// calcul des votes
// un vote identifié a un facteur de 3
// additionne tous les vote par ce_proposition
$votes = array(); // map ce_proposition -> score
foreach ($resultats as $vote) {
if (!isset($votes[$vote['id_commentaire']])) {
$votes[$vote['id_commentaire']] = 0;
}
$valeur = ($vote['valeur'] == 1) ? 1 : -1;
$votes[$vote['id_commentaire']] += is_numeric($vote['ce_utilisateur']) ? 3 * $valeur : $valeur;
}
 
foreach ($resultats as $vote) {
$idobs = $vote['ce_observation'];
 
if (!isset($obs[$idobs]['determinations'])) {
$obs[$idobs]['determinations'] = array();
}
 
$obs[$idobs]['determinations'][$vote['id_commentaire']] =
array('nom_sel' => $vote['nom_sel'],
'nom_ret' => $vote['nom_ret'],
'score' => $votes[$vote['id_commentaire']],
'nn' => $vote['nom_sel_nn']);
}
return $obs;
}
 
private function orderArray(&$obs) {
$ret = array();
foreach ($obs as $o) {
$ret[] = $o;
}
 
function cmpDesc($a, $b) {
return ($a['date_changement'] < $b['date_changement']) ? 1 : -1;
}
function cmpAsc($a, $b) {
return cmpDesc($b, $a);
}
 
if ($this->parametres['ordre'] == 'desc') {
usort($ret, 'cmpDesc');
} else {
usort($ret, 'cmpAsc');
}
return $ret;
}
 
/**
* 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) {
$mots_cles_cel_affiches = "fleur,fleurs,feuille,feuilles,ecorce,fruit,fruits,port,plantnet,plantscan_new,plantnet-mobile";
 
$result = array_intersect(
explode(',', $mots_cles_cel_affiches), // $tabMotsClesAffiches
explode(',', $chaineMotCleCel)); // $tabMotsClesCel
 
if (count($result) === 0) {
return array();
}
$ret = explode(',', implode(',', $result));
return $ret;
}
 
}
Property changes:
Added: svnkit:entry:sha1-checksum
+688f54846ca97b444ba8c783a83a3fc869a30bf3
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/communes/ListeCommunes.php
New file
0,0 → 1,50
<?php
// declare(encoding='UTF-8');
/**
* Web service fournissant une liste de noms de communes correspondants au terme recherché.
*
* @category DEL
* @package Services
* @subpackage Communes
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ListeCommunes {
 
private $conteneur;
private $navigation;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
}
 
public function consulter() {
$communes = $this->chargerCommunes();
$total = $this->compterCommunes($communes);
$this->navigation->setTotal($total);
$this->navigation->setSansLimite();
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $communes);
return $resultat;
}
 
private function chargerCommunes() {
$urlCelTpl = $this->conteneur->getParametre('urlServiceCelCommune');
$url = $urlCelTpl.$this->navigation->getFiltre('masque.nom');
$restClient = $this->conteneur->getRestClient();
$resultatJson = $restClient->consulter($url);
$resultat = json_decode($resultatJson);
return $resultat;
}
 
private function compterCommunes($communes) {
return count($communes);
}
}
/branches/v1.11-magnesium/services/modules/0.1/commentaires/ListeCommentaires.php
New file
0,0 → 1,108
<?php
// declare(encoding='UTF-8');
/**
* Retourne la liste commentaires correspondant aux filtres passés dans l'url :
* http://localhost/del/services/0.1/commentaires => liste tous les commentaires
* Filtres : voir le fichier de config : commentaires.masques_possibles
*
* @category DEL
* @package Services
* @subpackage Commentaires
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ListeCommentaires {
 
private $conteneur;
private $navigation;
private $bdd;
 
private $mapping = array();
private $mappingInverse = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = ($conteneur == null) ? new Conteneur() : $conteneur;
$this->navigation = $this->conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
 
$this->mapping = $this->conteneur->getParametreTableau('commentaires.mapping');
$this->mappingInverse = array_flip($this->mapping);
}
 
/**
* 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) {
// Lancement du service
$commentaires = $this->chargerCommentaires();
$total = $this->compterCommentairesTotal();
 
$commentaires = $this->formaterCommentaires($commentaires);
$this->navigation->setTotal($total);
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $commentaires);
return $resultat;
}
 
/**
* Chargement depuis la bdd de tous les commentaires
* */
private function chargerCommentaires() {
$requete = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS * '.
'FROM del_commentaire '.
'WHERE '.$this->creerClauseWhere().' '.
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recupererTous($requete);
return is_array($resultat) ? $resultat : array();
}
 
private function creerClauseWhere() {
$where = array();
$filtres = $this->navigation->getFiltre();
if (!empty($filtres)) {
foreach ($filtres as $cle => $valeur) {
$where[] = $this->mappingInverse[$cle].' = '.$this->bdd->proteger($valeur);
}
}
$clauseWhere = (!empty($where)) ? ' '.implode(' AND ', $where).' ' : ' 1 ';
return $clauseWhere;
}
 
/**
* Compter le nombre total de commentaires dans la base vis à vis des filtres de l'url.
* Utilisation du mécanisme SQL_CALC_FOUND_ROW de Mysql pour éviter une deuxième requete avec un COUNT.
*/
private function compterCommentairesTotal() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
 
/**
* Formater les commentaires
* @param $commentaires les commentaires à mettre à jour
* @return $commentaires les commentaires mis à jour
* */
private function formaterCommentaires($commentaires) {
$retour = array();
foreach ($commentaires as $idCommentaire => $infos) {
$idCommentaire = $infos['id_commentaire'];
foreach ($this->mapping as $nomChampBdd => $nomAttributSortie) {
$retour[$idCommentaire][$nomAttributSortie] = $infos[$nomChampBdd];
}
}
return $retour;
}
}
/branches/v1.11-magnesium/services/modules/0.1/commentaires/CommentaireDetails.php
New file
0,0 → 1,68
<?php
// declare(encoding='UTF-8');
/**
* Retourne le contenu d'un commentaire pour un identifiant donné.
* http://localhost/service:del:0.1/commentaires/#id => retourne le contenu d'un commentaire d'id #id
*
* @category DEL
* @package Services
* @subpackage Commentaires
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class CommentaireDetails {
 
private $conteneur;
private $bdd;
private $idCommentaire;
 
private $mapping = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->bdd = $this->conteneur->getBdd();
 
$this->mapping = $this->conteneur->getParametreTableau('commentaires.mapping');
}
 
public function consulter($ressources) {
$this->idCommentaire = $ressources[0];
 
// Lancement du service
$commentaire = $this->chargerCommentaire();
$commentaire = $this->formaterCommentaires($commentaire);
 
// Mettre en forme le résultat et l'envoyer pour affichage*/
$resultat = new ResultatService();
$resultat->corps = $commentaire;
 
return $resultat;
}
 
private function chargerCommentaire() {
$requete = 'SELECT * '.
'FROM del_commentaire '.
'WHERE id_commentaire = '.$this->idCommentaire.' '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recuperer($requete);
if ($resultat === false) {
$message = "Aucune information ne correspond au commentaire # «{$this->idCommentaire}».";
throw new Exception($message, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
return is_array($resultat) ? $resultat : array();
}
 
private function formaterCommentaires($infos) {
$retour = array();
foreach ($this->mapping as $nomChampBdd => $nomAttributSortie) {
$retour[$nomAttributSortie] = $infos[$nomChampBdd];
}
return $retour;
}
}
/branches/v1.11-magnesium/services/modules/0.1/commentaires/SupprimerCommentaire.php
New file
0,0 → 1,134
<?php
// declare(encoding='UTF-8');
/**
* Permet de supprimer un commentaire.
*
* @category DEL
* @package Services
* @subpackage Commentaires
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class SupprimerCommentaire {
 
private $conteneur;
private $navigation;
private $bdd;
private $utilisateur;
 
private $commentaireId;
private $utilisateurId;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
$this->utilisateur = $this->conteneur->getUtilisateur();
}
 
public function supprimer($ressources) {
$this->commentaireId = $ressources[0];
 
$utilisateur = $this->utilisateur->getUtilisateurIdentifie();
$this->verifierIdentificationUtilisateur($utilisateur);
$this->utilisateurId = $utilisateur['id_utilisateur'];
 
// 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->etreCommentaireSansEnfant() && $this->etreUtilisateurAutorise() &&
$this->nePasEtreDeterminationInitiale()) {
$this->supprimerCommentaire();
$this->supprimerVotesAssocies();
}
 
$resultat = new ResultatService();
return $resultat;
}
 
private function verifierIdentificationUtilisateur($utilisateur) {
if ($utilisateur == null) {
$msg = "Ce service nécessite d'être identifié.";
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
private function nePasEtreDeterminationInitiale() {
$idCommentaireP = $this->bdd->proteger($this->commentaireId);
$requete = 'SELECT * '.
'FROM del_commentaire '.
"WHERE id_commentaire = $idCommentaireP ".
"AND (proposition_initiale = 1 OR proposition_retenue = 1) ".
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
if (!empty($resultats)) {
$msg = "Impossible de supprimer la proposition initiale ou la proposition retenue.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
return true;
}
 
private function etreCommentaireSansEnfant() {
$idCommentaireP = $this->bdd->proteger($this->commentaireId);
$requete = 'SELECT * '.
'FROM del_commentaire '.
"WHERE (ce_proposition = $idCommentaireP ".
"OR ce_commentaire_parent = $idCommentaireP) ".
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
if (!empty($resultats)) {
$msg = "Impossible de supprimer le commentaire car il a des réponses.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
return true;
}
 
private function etreUtilisateurAutorise() {
if (! $this->etreProprietaire() && ! $this->utilisateur->etreAdmin()) {
$msg = "Impossible de supprimer le commentaire car l'utilisateur n'a pas les droits requis.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
return true;
}
 
private function etreProprietaire() {
$requete = 'SELECT * '.
'FROM del_commentaire '.
"WHERE id_commentaire = {$this->commentaireId} ".
"AND ce_utilisateur = {$this->utilisateurId} ".
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
return !empty($resultats);
}
 
private function supprimerCommentaire() {
$commentaireIdP = $this->bdd->proteger($this->commentaireId);
$utilisateurIdP = $this->bdd->proteger($this->utilisateurId);
$requete = 'DELETE FROM del_commentaire '.
"WHERE id_commentaire = $commentaireIdP ".
"AND ce_utilisateur = $utilisateurIdP ".
' -- '.__FILE__.' : '.__LINE__;
$retour = $this->bdd->requeter($requete);
if (!$retour) {
$msg = 'Erreur lors de la suppression.';
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
private function supprimerVotesAssocies() {
$commentaireId = $this->bdd->proteger($this->commentaireId);
$requete = 'DELETE FROM del_commentaire_vote '.
"WHERE ce_proposition = $commentaireId ".
' -- '.__FILE__.' : '.__LINE__;
$retour = $this->bdd->requeter($requete);
if (!$retour) {
$msg = 'Erreur lors de la suppression des votes associés.';
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
}
/branches/v1.11-magnesium/services/modules/0.1/commentaires/AjouterCommentaire.php
New file
0,0 → 1,272
<?php
// declare(encoding='UTF-8');
/**
* Permet d'ajouter un commentaire.
*
* @category DEL
* @package Services
* @subpackage Commentaires
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class AjouterCommentaire {
 
private $conteneur;
private $navigation;
private $bdd;
private $parametres = array();
 
private $mapping = array();
private $erreurs = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->navigation = $this->conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
 
$this->mapping = $this->conteneur->getParametreTableau('commentaires.mapping');
}
 
public function ajouter($parametres) {
$this->parametres = $parametres;
$this->verifierParametres();
 
$this->completerParametresUtilisateur();
$this->gererPropositionInitiale();
// Dernière chance de rattachement au référentiel d'un nom
// sans nn (cas du copier-coller ou bien de l'appli tierce
// qui envoie des infos incomplètes)
$this->tenterEnrichissementTaxonomique();
$idCommentaireAjoute = $this->insererCommentaire();
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('id_commentaire' => $idCommentaireAjoute);
 
return $resultat;
}
 
/**
* Vérifie notamment que l'auteur du vote est désigné soit par un ID, soit
* par un triplet (nom, prénom, adresse courriel)
*/
private function verifierParametres() {
if (!isset($this->parametres['observation'])) {
$this->erreurs[] = "Impossible d'ajouter un commentaire sans identifiant d'observation (paramètre 'observation').";
}
 
if (!isset($this->parametres['auteur.id'])) {
$this->verifierParamsAuteurAnonyme();
}
 
$this->verifierParamsNonVide();
 
if (isset($this->parametres['nom_sel_nn']) && !isset($this->parametres['nom_referentiel'])) {
$this->erreurs[] = "Si le paramètre «nom_sel_nn» est présent, le paramètre «nom_referentiel» doit l'être aussi.";
}
 
if (!empty($this->erreurs)) {
$msg = "Erreur de configuration :\n".implode("\n\n", $this->erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
private function verifierParamsAuteurAnonyme() {
$paramsAuteur = array('auteur.nom', 'auteur.prenom', 'auteur.courriel');
$paramsAuteurManquant = array();
foreach ($paramsAuteur as $param) {
if (!isset($this->parametres[$param])) {
$paramsAuteurManquant[] = $param;
}
}
 
if (!empty($paramsAuteurManquant)) {
$msgAuteurTpl = "Si le parametre 'auteur.id' n'est pas utilisé, il est nécessaire d'indiquer les ".
"nom (paramètre 'auteur.nom'), prénom (paramètre 'auteur.prenom') et courriel ".
"(paramètre 'auteur.courriel') de l'auteur.\nLes paramètres suivant sont abscents : %s\n";
$this->erreurs[] = sprintf($msgAuteurTpl, implode(', ', $paramsAuteurManquant));
}
}
 
private function verifierParamsNonVide() {
$paramsNonVide = array('nom_sel', 'nom_referentiel', 'nom_sel_nn');
foreach ($paramsNonVide as $param) {
if (isset($this->parametres[$param]) && trim($this->parametres[$param]) == '' ) {
$this->erreurs[] = "S'il est présent le paramètre «$param» ne peut pas être vide.";
}
}
}
 
/**
* Si l'auteur du vote est désigné par un ID, va chercher ses nom, prénom, courriel;
* s'il est désigné par un triplet (nom, prénom, adresse courriel), va chercher son ID
*/
private function completerParametresUtilisateur() {
$utilisateur = (isset($this->parametres['auteur.id'])) ? $this->obtenirUtilisateurAvecId() : $this->obtenirUtilisateurSansId();
if ($utilisateur != false) {
foreach ($utilisateur as $param => $valeur) {
$this->parametres[$param] = $valeur;
}
}
}
 
/**
* On suppose que si l'utilisateur envoie sa proposition avec un ID, c'est
* qu'il est connu d'IP, donc qu'on trouvera ses coordonnées dans
* del_utilisateur_infos
*
* @TODO valider cette hypothèse
*/
private function obtenirUtilisateurAvecId() {
$auteurIdP = $this->bdd->proteger($this->parametres['auteur.id']);
$requete = "SELECT id_utilisateur AS 'auteur.id', nom AS 'auteur.nom', prenom AS 'auteur.prenom', courriel AS 'auteur.courriel' ".
'FROM del_utilisateur_infos '.
"WHERE id_utilisateur = $auteurIdP ".
' -- '.__FILE__.' : '.__LINE__;
$utilisateur = $this->bdd->recuperer($requete);
return $utilisateur;
}
 
/**
* Pour un utilisateur désigné par un triplet (nom, prenom, adresse courriel), demande
* son ID à l'annuaire - vue la base de données (2017-03-24), aucun tuple ne contient
* une adresse courriel sans contenir d'ID, mais beaucoup ne contiennent ni l'un ni
* l'autre (code stupide, tentative de correction ajd)
*/
private function obtenirUtilisateurSansId() {
$nomP = $this->bdd->proteger($this->parametres['auteur.nom']);
$prenomP = $this->bdd->proteger($this->parametres['auteur.prenom']);
$courrielP = $this->bdd->proteger($this->parametres['auteur.courriel']);
 
// Si l'utilisateur s'est déjà connecté à DeL au moins une fois, on récupère ses
// nom et prénom connus dans la base; on lui interdit d'usurper sa propre identité
$requete = "SELECT id_utilisateur AS 'auteur.id', nom AS 'auteur.nom', prenom AS 'auteur.prenom', ".
"courriel AS 'auteur.courriel' ".
'FROM del_utilisateur_infos '.
"WHERE courriel = $courrielP ".
' -- '.__FILE__.' : '.__LINE__;
$utilisateur = $this->bdd->recuperer($requete);
 
// si l'utilisateur n'a pas été trouvé, on devrait aller le chercher dans
// l'annuaire, au cas où il soit inscrit à TB mais ne se soit pas connecté
// @TODO faire un appel au service annuaire/identite-par-courriel
 
return $utilisateur;
}
 
private function gererPropositionInitiale() {
if ($this->verifierExistencePropositionInitiale() === false) {
$this->creerPropositionInitiale();
// TODO : en cas d'échec de la création de la proposition ajouter un log...
}
}
 
private function verifierExistencePropositionInitiale() {
$idObsP = $this->bdd->proteger($this->parametres['observation']);
$requete = 'SELECT COUNT(*) >= 1 AS existe '.
'FROM del_commentaire '.
"WHERE ce_observation = $idObsP ".
' AND proposition_initiale = 1 '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recuperer($requete);
return $resultat['existe'] == 1;
}
 
private function creerPropositionInitiale() {
$idObsP = $this->bdd->proteger($this->parametres['observation']);
 
$requete = '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_utilisateur, nom_utilisateur, courriel_utilisateur, nom_sel, '.
"IF(nom_sel_nn = 0, NULL, nom_sel_nn), IF(nom_ret = '', NULL, nom_ret), IF(nom_ret_nn = 0, NULL, nom_ret_nn), ".
"IF(nt = 0, NULL, nt), IF(famille = '', NULL, famille), IF(nom_sel_nn = 0, NULL, nom_referentiel), NOW(), '1' ".
'FROM del_observation AS do '.
"WHERE id_observation = $idObsP ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultat = $this->bdd->executer($requete);
 
return $resultat;
}
 
private function insererCommentaire() {
$champs = $this->creerEnteteChamps();
$values = $this->creerClauseValues();
$requete = "INSERT INTO del_commentaire (date, $champs) VALUES (NOW(), $values) ".
' -- '.__FILE__.' : '.__LINE__;
 
$retour = $this->bdd->executer($requete);
if ($retour == null) {
$msgTpl = "Erreur inopinée lors de l'insertion du commentaire lié à l'observation «%s».";
$msg = sprintf($msgTpl, $this->parametres['observation']);
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
 
$idCommentaire = $this->bdd->recupererIdDernierAjout();
return $idCommentaire;
}
 
private function creerEnteteChamps() {
return $this->creerSuiteChampsOuValeurs('champs');
}
 
private function creerClauseValues() {
return $this->creerSuiteChampsOuValeurs('valeurs');
}
 
private function creerSuiteChampsOuValeurs($mode) {
$suite = array();
foreach ($this->mapping as $nomChampBdd => $nomAttributSortie) {
if (isset($this->parametres[$nomAttributSortie]) && $this->parametres[$nomAttributSortie] != null) {
if ($mode == 'valeurs') {
$suite[] = $this->bdd->proteger($this->parametres[$nomAttributSortie]);
} else if ($mode == 'champs') {
$suite[] = $nomChampBdd;
} else {
$msg = "Erreur interne : mauvais paramètre passé à la méthode 'creerSuiteChampsOuValeurs'";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
}
return implode(', ', $suite);
}
private function tenterEnrichissementTaxonomique() {
if($this->commentaireEstPropositionSansNn()) {
// TODO: utiliser le référentiel de l'obs si celui-ci
// n'est pas fourni dans le commentaire et prendre le résultat
// si celui-ci est unique
$referentiel = $this->parametres['nom_referentiel'];
$requete = urlencode($this->parametres['nom_sel']);
$url = sprintf($this->conteneur->getParametre('nomstaxons.url_autocompletion_tpl'), $referentiel, $requete);
$restClient = $this->conteneur->getRestClient();
// Un retour vide est possible (un cas normal où il n'y a pas de résultat)
// mais il fait planter le retour du service si on active l'affichage des erreurs
// donc on passe sciemment les erreurs sous silence (car cette erreur n'en est pas une)
$resultatJson = @$restClient->consulter($url);
$resultats = json_decode($resultatJson, true);
 
// On ne fait l'affectation que si l'on est sur (donc si un seul résultat)
if (isset($resultats['resultat']) && count($resultats['resultat']) == 1) {
$info = array_pop($resultats['resultat']);
$this->parametres['nom_sel_nn'] = $info['num_nom'];
}
}
}
private function commentaireEstPropositionSansNn() {
// Pas besoin de tester si c'est vide, normalement verifierParametres
// l'a déjà fait au-dessus
return isset($this->parametres['nom_sel'])
&& isset($this->parametres['nom_referentiel'])
&& !isset($this->parametres['nom_sel_nn']);
}
}
/branches/v1.11-magnesium/services/modules/0.1/Statistiques.php
New file
0,0 → 1,135
<?php
// declare(encoding='UTF-8');
/**
* Accès aux sous-services de statistiques pour Identiplante / Pictoflora
*
* @see Documentation : <http://www.tela-botanica.org/wikini/DevInformatiques/wakka.php?wiki=AppliDelStats>
*
* @category DEL
* @package Services
* @subpackage Statistiques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Statistiques extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'statistiques';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
$this->sousServiceNom = 'statistiques-par-annee';
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service {$this->serviceNom} !\n".
self::getDoc();
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
public static function getDoc() {
return "Les URLs disponibles pour ce service sont :\n".
" * en GET :\n".
" - statistiques/tout\n".
" - statistiques/moyenneObsSansNomParMois\n".
" - statistiques/moyenneObsIdentifieesParMois\n".
" - statistiques/pourcentageObsIdentifieesEnFinDAnnee\n".
" - statistiques/pourcentageObsIdentifieesEnFinDAnneePlusPlus\n".
" - statistiques/moyenneActionsParJour\n".
" - statistiques/personnesEnvoyantUnePropositionParMois\n";
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
 
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter();
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"est introuvable par le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/mots_cles/ListeMotsCles.php
New file
0,0 → 1,106
<?php
// declare(encoding='UTF-8');
/**
* Récupère des listes de mots clés associés aux images
*
* @category DEL
* @package Services
* @subpackage MotsCles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ListeMotsCles {
 
private $conteneur;
private $navigation;
private $bdd;
 
private $mapping = array();
private $mappingInverse = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
 
$this->mapping = $this->conteneur->getParametreTableau('mots-cles.mapping');
$this->mappingInverse = array_flip($this->mapping);
}
 
/**
* 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) {
// Lancement du service
$motsCles = $this->chargerMotsCles();
$total = $this->compterMotsClesTotal();
 
$motsClesSortie = $this->formaterMotsCles($motsCles);
$this->navigation->setTotal($total);
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $motsClesSortie);
return $resultat;
}
 
private function chargerMotsCles() {
$requete = 'SELECT DISTINCT SQL_CALC_FOUND_ROWS * '.
'FROM del_image_tag '.
'WHERE actif = 1 '.$this->creerClauseWhere().
'LIMIT '.$this->navigation->getDepart().','.$this->navigation->getLimite().' '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recupererTous($requete);
return is_array($resultat) ? $resultat : array();
}
 
private function creerClauseWhere() {
$where = array();
$filtres = $this->navigation->getFiltre();
if (!empty($filtres)) {
foreach ($filtres as $cle => $valeur) {
$where[] = $this->mappingInverse[$cle].' = '.$this->bdd->proteger($valeur);
}
}
$clauseWhere = (!empty($where)) ? ' AND '.implode(' AND ', $where).' ' : '';
return $clauseWhere;
}
 
/**
* Compter le nombre total de commentaires dans la base vis à vis des filtres de l'url.
* Utilisation du mécanisme SQL_CALC_FOUND_ROW de Mysql pour éviter une deuxième requete avec un COUNT.
*/
private function compterMotsClesTotal() {
$requete = 'SELECT FOUND_ROWS() AS nbre ';
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
 
/**
* Formater les mots clés pour la sortie.
*
* @param $mots_cles les mots clés à formater
* @return $mots_cles les mots clés mis à jour au format de la sortie du web service
*/
private function formaterMotsCles($mots_cles) {
$retour = array();
foreach ($mots_cles as $mot_cle) {
// Boucle sur le mapping pour respecter l'ordre des champs de sortie
foreach ($this->mapping as $nomChampBdd => $nomAttributSortie) {
if (isset($mot_cle[$nomChampBdd])) {
$retour[$mot_cle['id_tag']][$nomAttributSortie] = $mot_cle[$nomChampBdd];
}
}
}
return $retour;
}
}
/branches/v1.11-magnesium/services/modules/0.1/mots_cles/SupprimerMotCle.php
New file
0,0 → 1,76
<?php
// declare(encoding='UTF-8');
/**
* Supprime un mot clé par son identifiant
*
* @category DEL
* @package Services
* @subpackage MotsCles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class SupprimerMotCle {
 
private $conteneur;
private $bdd;
private $ressources = array();
private $motCleId;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->bdd = $this->conteneur->getBdd();
}
 
/**
* Supprime un mot-clé si les données fournis en paramètres sont valides.
*
* @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
*/
public function supprimer($ressources) {
$this->ressources = $ressources;
$this->verifierRessources();
 
$this->motCleId = $this->ressources[0];
$this->supprimerMotCle();
TelaBotanica\Del\Commun\MotsClesImage::updateStats($this->bdd, $this->motCleId);
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
return $resultat;
}
 
private function verifierRessources() {
$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)) {
$msg = "Erreur de configuration :\n".implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
private function supprimerMotCle() {
$idMotCleP = $this->bdd->proteger($this->motCleId);
$requete = 'UPDATE del_image_tag '.
'SET actif = 0, date_modification = NOW() '.
"WHERE id_tag = $idMotCleP ".
' -- '.__FILE__.' : '.__LINE__;
$retour = $this->bdd->executer($requete);
if ($retour == null) {
$msg = "Erreur lors de la suppression du mot-clé d'id $idMotCleP";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
return $retour;
}
}
/branches/v1.11-magnesium/services/modules/0.1/mots_cles/AjouterMotCle.php
New file
0,0 → 1,120
<?php
// declare(encoding='UTF-8');
/**
* Ajoute un ou plusieurs mots-clés en les associant à un identifiant d'image
*
* @category DEL
* @package Services
* @subpackage MotsCles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class AjouterMotCle {
 
private $conteneur;
private $bdd;
private $parametres = array();
private $idDernierAjout = null;
private $nbreMotsClesAjoutes = null;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->bdd = $this->conteneur->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->parametres = $parametres;
 
// Gestion des configuration du script
$this->verifierParametres();
$this->insererMotCle();
TelaBotanica\Del\Commun\MotsClesImage::updateStats($this->bdd, $this->parametres['image']);
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = array('nbre' => $this->nbreMotsClesAjoutes, 'id' => $this->idDernierAjout);
 
return $resultat;
}
 
public function verifierParametres() {
$erreurs = array();
 
if (!isset($this->parametres['image'])) {
$erreurs[] = " - impossible d'ajouter un mot clé sans l'indication de l'identifiant de l'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 l'identifiant de l'auteur associé";
}
 
if (!empty($erreurs)) {
$msg = "Erreur de configuration :\n".implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
/**
* Insère un mot clé dans la table
* */
private function insererMotCle() {
$clauseValues = $this->creerClauseValues();
$requete = 'INSERT INTO del_image_tag '.
'(ce_image, ce_utilisateur, tag, tag_normalise, date, actif, date_modification) '.
"VALUES $clauseValues ".
' -- '.__FILE__.' : '.__LINE__;
$nbreInsertion = $this->bdd->executer($requete);
$idDernierAjout = $this->bdd->recupererIdDernierAjout();
if ($nbreInsertion == null) {
$msgTpl = "Erreur lors de l'insertion d'un des tags «%s» pour l'image «%s» de l'auteur «%s»";
$msg = sprintf($msgTpl, $this->parametres['mot_cle'], $this->parametres['image'], $this->parametres['auteur.id']);
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
$this->nbreMotsClesAjoutes = $nbreInsertion;
$this->listerIdAjoutes($idDernierAjout, $nbreInsertion);
}
 
private function listerIdAjoutes($dernierId, $nbreInsertion) {
$this->idDernierAjout[] = $dernierId;
for ($i = 1 ; $i <= ($nbreInsertion - 1); $i++ ) {
$this->idDernierAjout[] = ++$dernierId;
}
}
 
private function creerClauseValues() {
$id_image = intval($this->parametres['image']);
$idImageP = $this->bdd->proteger($id_image);
$id_auteur = $this->parametres['auteur.id'];
$idAuteurP = $this->bdd->proteger($id_auteur);
$mots_cles = explode(',', $this->parametres['mot_cle']);
 
$values = array();
foreach ($mots_cles as $mot_cle) {
$motCleP = $this->bdd->proteger(trim($mot_cle));
$mot_cle_normalise = TelaBotanica\Del\Commun\MotsClesImage::normaliserMotCle($mot_cle);
$motCleNormaliseP = $this->bdd->proteger($mot_cle_normalise);
 
$values[] = "($idImageP, $idAuteurP, $motCleP, $motCleNormaliseP, NOW(), 1, NOW())";
}
$clauseValues = implode(',', $values);
return $clauseValues;
}
}
/branches/v1.11-magnesium/services/modules/0.1/protocoles/ListeProtocoles.php
New file
0,0 → 1,66
<?php
// declare(encoding='UTF-8');
/**
* Récupère tous les protocoles avec id, nom et descriptif
*
* @category DEL
* @package Services
* @subpackage Protocoles
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ListeProtocoles {
 
private $conteneur;
private $navigation;
private $bdd;
 
private $mapping = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
}
 
public function consulter() {
// Gestion des configuration du script
$this->mapping = $this->conteneur->getParametreTableau('protocoles.mapping');
 
$protocoles = $this->chargerProtocoles();
$protocoles = $this->formaterProtocoles($protocoles);
$this->navigation->setTotal(count($protocoles));
 
$resultat = new ResultatService();
$resultat->corps = array(
'entete' => $this->navigation->getEntete(),
'resultats' => $protocoles);
return $resultat;
}
 
private function chargerProtocoles() {
$requete = 'SELECT * FROM del_image_protocole -- '.__FILE__.' : '.__LINE__;
return $this->bdd->recupererTous($requete);
}
 
private function formaterProtocoles($protocoles) {
$protocolesRetour = array();
foreach ($protocoles as $protocole) {
$protocoleFormate = array();
$idProtocole = $protocole['id_protocole'];
foreach($protocole as $champProtocole => $valeur) {
if (isset($this->mapping[$champProtocole])) {
$protocoleFormate[$this->mapping[$champProtocole]] = $valeur;
}
}
$protocolesRetour[$idProtocole] = $protocoleFormate;
}
return $protocolesRetour;
}
}
/branches/v1.11-magnesium/services/modules/0.1/Images.php
New file
0,0 → 1,227
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services "images" de DEL.
*
* Cette classe se charge toujours de :
* - vérifier l'existance des ressources (services) demandés
* - vérifier la cohérence et le format des paramêtres passées dans l'url
* En fonction, de la compléxité du service, elle peut :
* - dans un premier temps, exécuter directement les actions : consulter, ajouter, modifier, supprimer.
* - dans un second temps, charger dynamiquement d'éventuelles sous-classes du service en fonction des ressources présentes dans l'URL.
*
* URLs possibles :
* GET :
* http://localhost/del/services/0.1/images/ => toutes les images : classe ListeImages
* http://localhost/del/services/0.1/images/#idImg/votes => tous les votes d'une image (#idImg) classés par protocole : classe VotesImage
*
* Non Implémenté : http://localhost/del/services/0.1/images/#id => une image donnée => en test pour remplacer les appels à eflore/cel
* Non Implémenté : http://localhost/del/services/0.1/images/#id/votes?protocole=#id => tous les votes d'une image et d'un protocole donné
*
* PUT :
* http://localhost/del/services/0.1/images/#idImg => ajouter un vote sur une image donnée (#idImg)
*
* POST :
* http://localhost/del/services/0.1/images/#idImg => modifier un vote sur une image donnée (#idImg)
*
* DELETE :
* http://localhost/del/services/0.1/images/#idImg => supprimer une image donnée (#idImg)
* http://localhost/del/services/0.1/images/#idImg/votes/#idVote => supprimer un vote (#idVote) d'une image donnée (#idImg)
*
* @category DEL
* @package Services
* @subpackage Images
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
restore_error_handler();
restore_exception_handler();
error_reporting(E_ALL);
 
class Images extends RestService {
 
private $conteneur;
private $cheminCourant;
private $parametres = array();
private $ressources = array();
private $methode;
private $serviceNom = 'images';
private $sousServiceNom;
 
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->conteneur = new Conteneur();
$this->cheminCourant = dirname(__FILE__).DS;
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
public function supprimer($ressources) {
$this->methode = 'supprimer';
$this->initialiserRessourcesEtParametres($ressources);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$resultat = $this->traiterRessources();
if ($resultat === true || $resultat === false) {
return $resultat;
}
 
$reponseHttp = new ReponseHttp();
$reponseHttp->setResultatService($resultat);
$reponseHttp->emettreLesEntetes();
return $reponseHttp->getCorps();
}
 
private function traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
$this->analyserRessoucesConsultation();
} else if ($this->methode == 'modifier' || $this->methode == 'ajouter') {
$this->analyserRessoucesModification();
} else if ($this->methode == 'supprimer') {
$this->analyserRessoucesSuppression();
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function analyserRessoucesConsultation() {
if (count($this->ressources) == 0) {
$this->sousServiceNom = 'liste-images';
} else if (count($this->ressources) == 2) {
if ($this->etreRessourceIdentifiant(0) && $this->verifierRessourceValeur(1, 'votes')) {
$this->sousServiceNom = 'votes-image';
}
}
}
 
private function analyserRessoucesModification() {
if (count($this->ressources) == 2) {
if ($this->etreRessourceIdentifiant(0) && $this->verifierRessourceValeur(1, 'votes')) {
$this->sousServiceNom = 'votes-image';
}
}
}
 
private function analyserRessoucesSuppression() {
if (count($this->ressources) == 1 && $this->etreRessourceIdentifiant(0)) {
$this->sousServiceNom = 'liste-images';
} else if (count($this->ressources) == 3) {
if ($this->etreRessourceIdentifiant(0) && $this->verifierRessourceValeur(1, 'votes') && $this->etreRessourceIdentifiant(2) ) {
$this->sousServiceNom = 'votes-image';
}
}
}
 
private function etreRessourceIdentifiant($num) {
$presenceId = false;
if (isset($this->ressources[$num]) && is_numeric($this->ressources[$num])) {
$presenceId = true;
}
return $presenceId;
}
 
private function verifierRessourceValeur($num, $valeur) {
$ok = false;
if (isset($this->ressources[$num]) && $this->ressources[$num] == $valeur) {
$ok = true;
}
return $ok;
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
$this->getDoc();
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
public function getDoc() {
return "Les URLs disponibles sont : \n".
" * en GET : \n".
" - images => toutes les images\n".
" - images/#idImg/votes => tous les votes d'une image (#idImg) classés par protocole\n".
" * en PUT : \n".
" - /images/#idImg/votes => ajouter un vote sur une image donnée (#idImg)\n".
" * en POST : \n".
" - /images/#idImg/votes => modifier un vote sur une image donnée (#idImg)\n".
" * en DELETE : \n".
" - /images/#idImg => supprimer une image donnée (#idImg)\n".
" - /images/#idImg/votes/#idVote => supprimer un vote (#idVote) d'une image donnée (#idImg)";
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
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)) {
$ressource = implode('/', $this->ressources);
$msg = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !\n".$this->getDoc();
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
return $retour;
}
 
private function obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
/branches/v1.11-magnesium/services/modules/0.1/utilisateurs/Preferences.php
New file
0,0 → 1,57
<?php
// declare(encoding='UTF-8');
/**
* Permet la gestion des préférences utilisateur
*
* @category DEL
* @package Services
* @subpackage Utilisateurs
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class Preferences extends GestionUtilisateur {
 
/**
* Renvoie les préférences d'un utilisateur
* @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];
$this->controleUtilisateurIdentifie($id_utilisateur);
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $this->obtenirPreferencesUtilisateur($id_utilisateur);
return $resultat;
}
 
/**
* Modifie les préférences de l'utilisateur
* @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 d ans le post
* */
public function modifier($ressources, $parametres) {
$id_utilisateur = $ressources[0];
$this->controleUtilisateurIdentifie($id_utilisateur);
$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 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).' '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->executer($requete);
return $resultat;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+f73e59fae7ac415df9c26a992af76cb1c409a1ec
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/utilisateurs/Activite.php
New file
0,0 → 1,35
<?php
// declare(encoding='UTF-8');
/**
* Permet de consulter l'activité d'un utilisateur de Del
*
* @category DEL
* @package Services
* @subpackage Utilisateurs
* @version 0.1
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Activite extends GestionUtilisateur {
 
/**
* Retourne l'activité de l'utilisateur en cours, ou false si aucun utilisateur connu
* n'est identifié (mode anonyme) @TODO retrouner un truc mieux
* */
public function consulter($ressources, $parametres) {
 
if ($this->utilisateur['connecte'] === true) {
$id_utilisateur = $this->utilisateur['id_utilisateur'];
$activite = $this->getEvenements($id_utilisateur);
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $activite;
 
return $resultat;
}
return false;
}
}
/branches/v1.11-magnesium/services/modules/0.1/utilisateurs/Identification.php
New file
0,0 → 1,30
<?php
// declare(encoding='UTF-8');
/**
* Gestion de l'identification des utilisateurs
*
* @category DEL
* @package Services
* @subpackage Utilisateurs
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Identification extends GestionUtilisateur {
 
public function consulter($ressources, $parametres) {
$utilisateur = $this->utilisateur;
if ($utilisateur['connecte'] === true) {
$this->ajouterEvenements($utilisateur);
}
 
$resultat = new ResultatService();
$resultat->corps = $utilisateur;
 
return $resultat;
}
}
/branches/v1.11-magnesium/services/modules/0.1/Nomstaxons.php
New file
0,0 → 1,135
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services Noms et Taxons utilisés par DEL.
*
* @category DEL
* @package Services
* @subpackage NomsTaxons
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Nomstaxons extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'nomstaxons';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if (count($this->ressources) == 0
&& $this->verifierPresenceParametre('masque.nom')
&& $this->verifierPresenceParametre('masque.referentiel')) {
$this->sousServiceNom = 'liste-taxons';
}
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function verifierPresenceParametre($cle) {
if (isset($this->parametres[$cle]) && trim($this->parametres[$cle]) == '') {
$message = "Le service demandé '{$this->serviceNom}' ".
"nécessite l'utilisation de paramètres (non vide) : masque.nom & masque.referentiel\n";
throw new Exception($message, RestServeur::HTTP_CODE_ECHEC_CONDITION);
}
return true;
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : nomstaxons (paramètres : masque.nom & masque.referentiel) \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
 
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter();
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"est introuvable par le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/statistiques/StatistiquesParAnnee.php
New file
0,0 → 1,1077
<?php
/**
* Statistiques par année sur l'utilisation de Identiplante / Pictoflora
*
* @see Documentation : <http://www.tela-botanica.org/wikini/DevInformatiques/wakka.php?wiki=AppliDelStats>
*
* @category DEL
* @package Services
* @subpackage Statistiques
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class StatistiquesParAnnee {
 
private $conteneur;
private $contexte;
private $navigation;
private $bdd;
 
private $annee = null;
private $type = 'tout';
private $methode = '';
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->contexte = $conteneur->getContexte();
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
}
 
public function consulter() {
$this->intitialiserParametresEtRessources();
$this->verifierPreRequis();
 
$resultat = new ResultatService();
$resultat->corps = call_user_func(array($this, $this->methode));
return $resultat;
}
 
private function intitialiserParametresEtRessources() {
$this->type = $this->contexte->getRessource(2) != null ? $this->contexte->getRessource(2) : $this->type;
$this->methode = $this->obtenirNomMethode($this->type);
$this->annee =(int) $this->contexte->getQS('annee') != null ? intval($this->contexte->getQS('annee')) : null;
}
 
private function verifierPreRequis() {
$erreurs = array();
 
if ($this->annee != null && !is_int($this->annee)) {
$erreurs[] = "Le paramètre 'annee' doit être un entier.";
}
if (method_exists($this, $this->obtenirNomMethode($this->type)) === false) {
$erreurs[] = "Les stats de type '{$this->type}' n'existent pas.";
}
if (!empty($erreurs)) {
$msg = "Erreur de configuration :\n".implode("\n", $erreurs)."\n\n".Statistiques::getDoc();
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
/**
* Ouh la jolie méthode magique !!
* @param unknown $mot
* @return string
*/
private function obtenirNomMethode($mot) {
$classeNom = 'get'.str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
 
/**
* Statistiques sur les observations
* "MPM" = moyenne par mois
* "TC" = tag à déterminer ou certitude incertaine
* - Nombre d'observations au total
* - Nombre d'obs sans identification (ou tag ou certitude)
* - Nombre d'obs sans identification
* - Nombre d'obs avec tag à déterminer ou certitude incertaine
* - Nombre d'obs avec tag à déterminer ou certitude incertaine et retenue
* - Nombre d'obs avec tag à déterminer ou certitude incertaine et avec consensus
* - Nombre d'obs avec tag à déterminer ou certitude incertaine et avec consensus mais non validées
* - Nombre d'observations ayant une proposition retenue (parmi les observations de l'année)
* - Nombre d'observations ayant une proposition retenue ou en consensus (parmi les observations de l'année)
*/
private function getObservations() {
$nbObsTotal = $this->getNbObsTotal();
$nbObsSansIdentOuTC = $this->getNbObsSansIdentOuTC();
$nbObsSansIdent = $this->getNbObsSansIdent();
$nbObsTC = $this->getNbObsTC();
$nbObsTCRetenue = $this->getNbObsTCRetenue();
$nbObsTCConsensus = $this->getNbObsTCConsensus();
$nbObsTCConsensusNonValide = $this->getNbObsTCConsensusNonValide();
$nbPropositionsRetenuesObsAnnee = $this->getNbPropositionsRetenuesObsAnnee();
$nbPropositionsConsensusObsAnnee = $this->getNbPropositionsConsensusObsAnnee();
return array(
'nbObsTotal' => $nbObsTotal,
'nbObsTotalMPM' => $this->getNbObsTotal(true),
'nbObsSansIdentOuTC' => $nbObsSansIdentOuTC,
'nbObsSansIdentOuTCMPM' => $this->getNbObsSansIdentOuTC(true),
'nbObsSansIdentOuTCPC' => $nbObsTotal == 0 ? 0 : round(($nbObsSansIdentOuTC / $nbObsTotal) * 100, 2),
'nbObsSansIdent' => $nbObsSansIdent,
'nbObsSansIdentMPM' => $this->getNbObsSansIdent(true),
'nbObsSansIdentPC' => $nbObsTotal == 0 ? 0 : round(($nbObsSansIdent / $nbObsTotal) * 100, 2),
'nbObsTC' => $nbObsTC,
'nbObsTCMPM' => $this->getNbObsTC(true),
'nbObsTCPC' => $nbObsTotal == 0 ? 0 : round(($nbObsTC / $nbObsTotal) * 100, 2),
'nbObsTCRetenue' => $nbObsTCRetenue,
'nbObsTCRetenueMPM' => $this->getNbObsTCRetenue(true),
'nbObsTCRetenuePC' => $nbObsTotal == 0 ? 0 : round(($nbObsTCRetenue / $nbObsTotal) * 100, 2),
'nbObsTCConsensus' => $nbObsTCConsensus,
'nbObsTCConsensusMPM' => $this->getNbObsTCConsensus(true),
'nbObsTCConsensusPC' => $nbObsTotal == 0 ? 0 : round(($nbObsTCConsensus / $nbObsTotal) * 100, 2),
'nbObsTCConsensusNonValide' => $nbObsTCConsensusNonValide,
'nbObsTCConsensusNonValideMPM' => $this->getNbObsTCConsensusNonValide(true),
'nbObsTCConsensusNonValidePC' => $nbObsTotal == 0 ? 0 : round(($nbObsTCConsensusNonValide / $nbObsTotal) * 100, 2),
'nbPropositionsRetenuesObsAnnee' => $nbPropositionsRetenuesObsAnnee,
'nbPropositionsRetenuesObsAnneeMPM' => $this->getNbPropositionsRetenuesObsAnnee(true),
'nbPropositionsRetenuesObsAnneePC' => $nbObsTotal == 0 ? 0 : round(($nbPropositionsRetenuesObsAnnee / $nbObsTotal) * 100, 2),
'nbPropositionsConsensusObsAnnee' => $nbPropositionsConsensusObsAnnee,
'nbPropositionsConsensusObsAnneeMPM' => $this->getNbPropositionsConsensusObsAnnee(true),
'nbPropositionsConsensusObsAnneePC' => $nbObsTotal == 0 ? 0 : round(($nbPropositionsConsensusObsAnnee / $nbObsTotal) * 100, 2),
);
}
 
/**
* Nombre d'observations au total
*/
private function getNbObsTotal($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date_transmission) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs sans identification (ou tag ou certitude)
*/
private function getNbObsSansIdentOuTC($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (mots_cles_texte LIKE '%determiner%' OR nom_sel_nn = '' OR nom_sel_nn IS NULL OR certitude IN ('aDeterminer','douteux'))";
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs sans identification
*/
private function getNbObsSansIdent($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (nom_sel_nn = '' OR nom_sel_nn IS NULL)";
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs avec tag à déterminer ou certitude incertaine
*/
private function getNbObsTC($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (mots_cles_texte LIKE '%determiner%' OR certitude IN ('aDeterminer','douteux'))";
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs avec tag à déterminer ou certitude incertaine et retenue
*/
private function getNbObsTCRetenue($mpm=false) {
$requete = "SELECT COUNT(*) AS nb_total FROM del_commentaire WHERE proposition_retenue = 1 AND ce_observation IN (SELECT id_observation FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (certitude IN ('aDeterminer','douteux')))";
if ($mpm) {
// @TODO vérifier que grouper sur "date" est pertinent
// date_transmission n'est pas dispo pour grouper ici :-/
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs avec tag à déterminer ou certitude incertaine et avec consensus
*/
private function getNbObsTCConsensus($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (mots_cles_texte LIKE '%determiner%' OR certitude IN ('aDeterminer','douteux')) AND id_observation IN"
. " (SELECT ce_observation FROM del_commentaire WHERE proposition_initiale = 1"
. " AND nom_sel_nn != 0"
. " AND nom_sel_nn IS NOT NULL"
. " AND id_commentaire IN (SELECT ce_proposition FROM del_commentaire_vote dcv";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) <= ' . $this->annee;
}
$requete .= " GROUP BY ce_proposition HAVING SUM(CASE"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN 3"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN -3"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN 1"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN -1"
. " END) >= 4))";
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'obs avec tag à déterminer ou certitude incertaine et avec consensus mais non validées
*/
private function getNbObsTCConsensusNonValide($mpm=false) {
$requete = "SELECT COUNT(id_observation) AS nb_total FROM del_observation WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date_transmission) = ' . $this->annee . " AND";
}
$requete .= " (mots_cles_texte LIKE '%determiner%' OR certitude IN ('aDeterminer','douteux')) AND id_observation IN"
. " (SELECT ce_observation FROM del_commentaire WHERE proposition_initiale = 1 AND proposition_retenue = 0"
. " AND nom_sel_nn != 0"
. " AND nom_sel_nn IS NOT NULL"
. " AND id_commentaire IN (SELECT ce_proposition FROM del_commentaire_vote dcv";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) <= ' . $this->annee;
}
$requete .= " GROUP BY ce_proposition HAVING SUM(CASE"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN 3"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN -3"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN 1"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN -1"
. " END) >= 4))";
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Statistiques sur les propositions, les commentaires et les votes
* "MPM" = moyenne par mois
* - Nombre de votes
* - Nombre de commentaires
* - Nombre d'actions par jour
* - Nombres de propositions sur toutes les obs
* - Nombre d'observations ayant une proposition retenue (parmi toutes les observations)
* - Nombre d'observations ayant une proposition retenue ou en consensus (parmi toutes les observations)
* - Nombre de propositions sur les obs d'une année
*/
private function getPropositions() {
return array(
'nbVotes' => $this->getNbVotes(),
'nbVotesMPM' => $this->getNbVotes(true),
'nbMoyenActionsParJour' => $this->getNbMoyenActionsParJour(),
'nbCommentaires' => $this->getNbCommentaires(),
'nbCommentairesMPM' => $this->getNbCommentaires(true),
'nbPropositionsToutesObs' => $this->getNbPropositionsToutesObs(),
'nbPropositionsToutesObsMPM' => $this->getNbPropositionsToutesObs(true),
'nbPropositionsRetenuesToutesObs' => $this->getNbPropositionsRetenuesToutesObs(),
'nbPropositionsRetenuesToutesObsMPM' => $this->getNbPropositionsRetenuesToutesObs(true),
'nbPropositionsConsensusToutesObs' => $this->getNbPropositionsConsensusToutesObs(),
'nbPropositionsConsensusToutesObsMPM' => $this->getNbPropositionsConsensusToutesObs(true),
'nbPropositionsObsAnnee' => $this->getNbPropositionsObsAnnee(),
'nbPropositionsObsAnneeMPM' => $this->getNbPropositionsObsAnnee(true),
);
}
 
/**
* Nombre moyen d'actions par jour (commentaire, proposition, vote)
*/
private function getNbMoyenActionsParJour() {
$requete = "SELECT (SELECT (SELECT count(*) FROM del_commentaire";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= ") + (SELECT count(*) FROM del_commentaire_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= ") + (SELECT count(*) FROM del_commentaire";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date_validation) = ' . $this->annee;
}
$requete .= ")) / (";
if ($this->annee != null) {
$requete .= "SELECT IF( YEAR(CURDATE()) = " . $this->annee . ", DAYOFYEAR(CURDATE()), 365) ";
} else {
$requete .= "365";
}
$requete .= ")";
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre de propositions faites sur toutes les observations (hors initiales)
*/
private function getNbPropositionsToutesObs($mpm=false) {
$requete = "SELECT COUNT(DISTINCT id_commentaire) AS nb_total FROM del_commentaire WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date) = ' . $this->annee . " AND";
}
$requete .= " (nom_sel_nn IS NOT NULL OR nom_sel_nn != '') AND proposition_initiale = 0";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'observations ayant une proposition retenue (parmi toutes les observations)
*/
private function getNbPropositionsRetenuesToutesObs($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) AS nb_total FROM del_commentaire WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date) = ' . $this->annee . " AND";
}
$requete .= " proposition_retenue = 1";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'observations ayant une proposition retenue ou en consensus (parmi toutes les observations)
*/
private function getNbPropositionsConsensusToutesObs($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) AS nb_total FROM del_commentaire dc WHERE"
. " dc.proposition_retenue = 1"
. " OR (dc.proposition_initiale = 1"
. " AND dc.nom_sel_nn != 0"
. " AND dc.nom_sel_nn IS NOT NULL"
. " AND dc.id_commentaire IN"
. " (SELECT ce_proposition FROM del_commentaire_vote dcv WHERE";
if ($this->annee != null) {
$requete .= " year(date) < " . $this->annee . " AND";
}
$requete .= " ce_proposition NOT IN (SELECT ce_proposition FROM del_commentaire_vote dcv";
if ($this->annee != null) {
$requete .= " WHERE year(date) < " . ($this->annee - 1);
}
$requete .= " GROUP BY ce_proposition"
. " HAVING SUM(CASE"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN 3"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN -3"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN 1"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN -1"
. " END) >= 4)"
. " GROUP BY ce_proposition"
. " HAVING SUM(CASE"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN 3"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN -3"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN 1"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN -1"
. " END) >= 4"
. " )"
. " )";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'observations ayant une proposition retenue (parmi les observations de l'année)
*/
private function getNbPropositionsObsAnnee($mpm=false) {
if ($this->annee == null) {
return null;
}
$requete = "SELECT COUNT(DISTINCT ce_observation) AS nb_total FROM del_commentaire WHERE YEAR(date) = " . $this->annee . " AND "
. "(nom_sel_nn IS NOT NULL OR nom_sel_nn != '') AND proposition_initiale = 0 AND ce_observation in"
. " (SELECT id_observation FROM del_observation WHERE year(date_transmission) = " . $this->annee . ")";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'observations ayant une proposition retenue ou en consensus (parmi les observations de l'année)
*/
private function getNbPropositionsRetenuesObsAnnee($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) AS nb_total FROM del_commentaire WHERE ";
if ($this->annee != null) {
$requete .= "YEAR(date) = " . $this->annee . " AND ";
}
$requete .= "proposition_retenue = 1 AND ce_observation in (SELECT id_observation FROM del_observation ";
if ($this->annee != null) {
$requete .= "WHERE year(date_transmission) = " . $this->annee;
}
$requete .= ")";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre de proposition en consensus faites sur les observations d'une année
*/
private function getNbPropositionsConsensusObsAnnee($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) AS nb_total FROM del_commentaire dc WHERE ce_observation in ("
. " SELECT id_observation FROM del_observation ";
if ($this->annee != null) {
$requete .= "WHERE year(date_transmission) = " . $this->annee ;
}
$requete .= ") AND ("
. " dc.proposition_retenue = 1 OR (dc.proposition_initiale = 1"
. " AND dc.nom_sel_nn != 0 AND dc.nom_sel_nn IS NOT NULL AND dc.id_commentaire IN"
. " (SELECT ce_proposition FROM del_commentaire_vote dcv";
if ($this->annee != null) {
$requete .= " WHERE year(date) <= " . $this->annee;
}
$requete .= " GROUP BY ce_proposition"
. " HAVING SUM(CASE"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN 3"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' != 0 THEN -3"
. " WHEN valeur = 1 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN 1"
. " WHEN valeur = 0 AND dcv.ce_utilisateur REGEXP '^-?[0-9]+$' = 0 THEN -1"
. " END) >= 4)"
. "))";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre de votes
*/
private function getNbVotes($mpm=false) {
$requete = "SELECT COUNT(DISTINCT id_vote) AS nb_total FROM del_commentaire_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre de commentaires
*/
private function getNbCommentaires($mpm=false) {
$requete = "SELECT COUNT(DISTINCT id_commentaire) AS nb_total FROM del_commentaire WHERE ce_proposition != '' AND (nom_sel IS NULL OR nom_sel = '')";
if ($this->annee != null) {
$requete .= ' AND YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Statistiques sur les utilisateurs d'Identiplante
* "AF" = ayant fait un(e)
* "MPM" = moyenne par mois
* - Nombre total d'utilisateurs
* - Nombre d'utilisateurs ayant fait une proposition
* - Nombre d'utilisateurs identifiés ayant fait un vote
* - Nombre d'utilisateurs anonymes ayant fait un vote
* - Nombre d'utilisateurs ayant fait un commentaire
* - Nombre d'utilisateurs ayant fait une action
*/
private function getUtilisateursIp() {
return array(
'nbUtilisateursTotal' => $this->getNbUtilisateursIpTotal(),
'nbUtilisateursAFProposition' => $this->getNbUtilisateursAFProposition(),
'nbUtilisateursAFCommentaire' => $this->getNbUtilisateursAFCommentaire(),
'nbUtilisateursAFVote' => $this->getNbUtilisateursAFVote(),
'nbUtilisateursAnonymesAFVote' => $this->getNbUtilisateursAnonymesAFVote(),
'nbUtilisateursAFAction' => $this->getNbUtilisateursAFAction(),
'nbUtilisateursAFPropositionMPM' => $this->getNbUtilisateursAFProposition(true),
'nbUtilisateursAFCommentaireMPM' => $this->getNbUtilisateursAFCommentaire(true),
'nbUtilisateursAFVoteMPM' => $this->getNbUtilisateursAFVote(true),
'nbUtilisateursAnonymesAFVoteMPM' => $this->getNbUtilisateursAnonymesAFVote(true),
'nbUtilisateursAFActionMPM' => $this->getNbUtilisateursAFActionMPM()
);
}
 
/**
* Nombre total d'utilisateurs d'Identiplante
*/
private function getNbUtilisateursIpTotal() {
$requete = "SELECT COUNT(DISTINCT ce_utilisateur) AS nb_total FROM del_commentaire";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs d'Identiplante ayant fait au moins une proposition
*/
private function getNbUtilisateursAFProposition($mpm=false) {
$requete = "SELECT COUNT(DISTINCT utilisateur_courriel) AS nb_total FROM del_commentaire WHERE ce_proposition = '' AND nom_sel IS NOT NULL AND nom_sel != ''";
if ($this->annee != null) {
$requete .= ' AND YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs d'Identiplante ayant fait au moins un commentaire
*/
private function getNbUtilisateursAFCommentaire($mpm=false) {
$requete = "SELECT COUNT(DISTINCT utilisateur_courriel) AS nb_total FROM del_commentaire WHERE ce_proposition != '' AND (nom_sel IS NULL OR nom_sel = '')";
if ($this->annee != null) {
$requete .= ' AND YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs d'Identiplante identifiés prenant part aux votes
*/
private function getNbUtilisateursAFVote($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_utilisateur) AS nb_total FROM del_commentaire_vote WHERE ce_utilisateur REGEXP '^-?[0-9]+$'";
if ($this->annee != null) {
$requete .= ' AND YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs d'Identiplante anonymes prenant part aux votes
*/
private function getNbUtilisateursAnonymesAFVote($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_utilisateur) AS nb_total FROM del_commentaire_vote WHERE ce_utilisateur NOT REGEXP '^-?[0-9]+$'";
if ($this->annee != null) {
$requete .= ' AND YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs d'Identiplante ayant fait une action (commentaire, vote, proposition)
*/
private function getNbUtilisateursAFAction() {
$requete = "SELECT COUNT(*) AS nb_total FROM (SELECT ce_utilisateur FROM del_commentaire_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " UNION SELECT ce_utilisateur FROM del_commentaire";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " ) AS action";
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Moyenne par mois du nombre d'utilisateurs d'Identiplante ayant fait une action
* (commentaire, vote, proposition) -> n'est pas encapsulable par encapsulerMPM()
*/
private function getNbUtilisateursAFActionMPM() {
$requete = "SELECT avg(nb_total) FROM (SELECT count(*) as nb_total FROM"
. " (SELECT * FROM (SELECT ce_utilisateur, date FROM del_commentaire_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " UNION SELECT ce_utilisateur, date FROM del_commentaire";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " ) AS action GROUP BY ce_utilisateur) AS utildate GROUP BY CONCAT(year(date),month(date))) as truc";
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Liste des utilisateurs dont les propositions ont été votées positivement
*/
private function getListeMeilleursProposeurs() {
$liste = array();
$requete = "SELECT * FROM (SELECT utilisateur_courriel, ce_utilisateur, count(prop) as nb_prop"
. " FROM (SELECT ce_proposition as prop, COUNT(DISTINCT id_vote) AS nb_vote FROM del_commentaire_vote where";
if ($this->annee != null) {
$requete .= " year(date) = " . $this->annee . " AND";
}
$requete .= " valeur = 1 GROUP BY ce_proposition) AS vote, del_commentaire WHERE nb_vote > 3 AND prop = id_commentaire AND ce_utilisateur != 0 GROUP BY ce_utilisateur)"
. " AS utlisateurs WHERE nb_prop > 10 ORDER BY nb_prop DESC LIMIT 20";
$resultat = $this->bdd->recupererTous($requete);
 
// Formatage de la liste avec les intitulés des utilisateurs
$ids = array_column($resultat, 'ce_utilisateur');
$ids = array_filter($ids, 'is_numeric'); // on oublie les ids de session et autres facéties
$infosUtilisateurs = $this->recupererIntitulesUtilisateursParIds($ids, true);
foreach ($resultat as &$util) {
$ce = $util['ce_utilisateur'];
$util['intitule'] = isset($infosUtilisateurs[$ce]['intitule']) ? $infosUtilisateurs[$ce]['intitule'] : null;
}
 
return array(
'liste' => $resultat
);
}
 
/**
* Liste des utilisateurs ayant fait le plus de votes positifs
* @TODO et le plus de votes sur des propositions retenues (ou ayant atteint un consensus)
*/
private function getListeMeilleursVoteurs() {
$liste = array();
$requete = "SELECT * FROM (SELECT courriel, ce_utilisateur, COUNT(DISTINCT id_vote) AS nombre FROM del_commentaire_vote, del_utilisateur_infos where";
if ($this->annee != null) {
$requete .= " year(date) = " . $this->annee . " AND";
}
$requete .= " ce_utilisateur = id_utilisateur AND valeur = 1 GROUP BY ce_utilisateur) AS utilisateurs WHERE nombre > 100 ORDER BY nombre DESC LIMIT 20";
$resultat = $this->bdd->recupererTous($requete);
 
// Formatage de la liste avec les intitulés des utilisateurs
$ids = array_column($resultat, 'ce_utilisateur');
$ids = array_filter($ids, 'is_numeric'); // on oublie les ids de session et autres facéties
$infosUtilisateurs = $this->recupererIntitulesUtilisateursParIds($ids, true);
foreach ($resultat as &$util) {
$ce = $util['ce_utilisateur'];
$util['intitule'] = isset($infosUtilisateurs[$ce]['intitule']) ? $infosUtilisateurs[$ce]['intitule'] : null;
}
 
return array(
'liste' => $resultat
);
}
 
/**
* Liste des utilisateurs ayant fait au moins une proposition par mois toute l'année
*/
private function getListeProposeursReguliers() {
$liste = array();
$requete = "SELECT cal.nbmois, SUM(somme) / cal.nbmois as moyenne, ce_utilisateur, utilisateur_courriel FROM (SELECT count(*) as somme,"
. " CONCAT(YEAR(date),'-',MONTH(date)) as anneemois, ce_utilisateur, utilisateur_courriel, id_commentaire FROM del_commentaire"
. " WHERE ce_proposition = '' AND nom_sel_nn != '' AND nom_sel_nn IS NOT NULL";
if ($this->annee != null) {
$requete .= " AND year(date) = " . $this->annee;
}
$requete .= " GROUP BY anneemois, ce_utilisateur, utilisateur_courriel) as ppm, (SELECT count(distinct CONCAT(YEAR(date),'-',MONTH(date))) as nbmois FROM del_commentaire WHERE";
if ($this->annee != null) {
$requete .= " year(date) = " . $this->annee . " AND";
}
$requete .= " ce_proposition = '' AND nom_sel_nn != '' AND nom_sel_nn IS NOT NULL) as cal GROUP BY ce_utilisateur, utilisateur_courriel HAVING SUM(somme) / cal.nbmois >= 1"
. " ORDER BY moyenne DESC"; // @ TODO limite ?
$resultat = $this->bdd->recupererTous($requete);
 
// Formatage de la liste avec les intitulés des utilisateurs
$ids = array_column($resultat, 'ce_utilisateur');
$ids = array_filter($ids, 'is_numeric'); // on oublie les ids de session et autres facéties
$infosUtilisateurs = $this->recupererIntitulesUtilisateursParIds($ids, true);
foreach ($resultat as &$util) {
$ce = $util['ce_utilisateur'];
$util['intitule'] = isset($infosUtilisateurs[$ce]['intitule']) ? $infosUtilisateurs[$ce]['intitule'] : null;
}
 
return array(
'liste' => $resultat
);
}
 
/**
* Statistiques sur Sauvages de ma Rue (Sdmr)
* "MPM" = moyenne par mois
* - Nombre total d'observations sdmr
* - Nombre d'observations Sauvages de PACA
*/
private function getSdmr() {
return array(
'nbObsSdmrTotal' => $this->getNbObsSdmrTotal(),
'nbObsSdmrTotalMPM' => $this->getNbObsSdmrTotal(true),
'nbObsSdmrPACA' => $this->getNbObsSdmrPACA(),
'nbObsSdmrPACAMPM' => $this->getNbObsSdmrPACA(true)
);
}
 
/**
* Nombre total d'observations Sauvages de ma Rue
*/
private function getNbObsSdmrTotal($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) as nb_total FROM del_commentaire WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date) <= ' . $this->annee . ' AND';
}
$requete .= " ce_observation in (SELECT id_observation FROM del_observation WHERE mots_cles_texte like '%sauvages%')";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total d'observations Sauvages de ma Rue en PACA
*/
private function getNbObsSdmrPACA($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_observation) as nb_total FROM del_commentaire WHERE";
if ($this->annee != null) {
$requete .= ' YEAR(date) <= ' . $this->annee . ' AND';
}
$requete .= " ce_observation in (SELECT id_observation FROM del_observation"
. " WHERE SUBSTR(ce_zone_geo, 9 , 2) IN (13,04,05,06,83,84) AND mots_cles_texte like '%sauvages%')";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Statistiques sur les images de Pictoflora
* "MPM" = Moyenne par mois
* - Nombre total d'images
* - Nombre d'images ayant au moins un mot-clé
* - Nombre d'images ayant au moins un vote
* - Nombre d'images de l'année ayant au moins un mot-clé
* - Nombre d'images de l'année ayant au moins un vote
*/
private function getImages() {
return array(
'nbImagesTotal' => $this->getNbImagesTotal(),
'nbImagesTotalMPM' => $this->getNbImagesTotal(true),
'nbImagesAyantTag' => $this->getNbImagesAyantTag(),
'nbImagesAyantTagMPM' => $this->getNbImagesAyantTag(true),
'nbImagesAyantVote' => $this->getNbImagesAyantVote(),
'nbImagesAyantVoteMPM' => $this->getNbImagesAyantVote(true),
'nbImagesAnneeAyantTag' => $this->getNbImagesAnneeAyantTag(),
'nbImagesAnneeAyantTagMPM' => $this->getNbImagesAnneeAyantTag(true),
'nbImagesAnneeAyantVote' => $this->getNbImagesAnneeAyantVote(),
'nbImagesAnneeAyantVoteMPM' => $this->getNbImagesAnneeAyantVote(true),
);
}
 
/**
* Nombre total d'images Pictoflora
*/
private function getNbImagesTotal($mpm=false) {
$requete = "SELECT COUNT(*) AS nb_total FROM del_image";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date_transmission) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete, 'date_transmission');
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total d'images Pictoflora ayant au moins un mot-clé
*/
private function getNbImagesAyantTag($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_image) AS nb_total FROM del_image_tag";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total d'images Pictoflora ayant au moins un vote
*/
private function getNbImagesAyantVote($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_image) AS nb_total FROM del_image_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total d'images Pictoflora de l'année ayant au moins un mot-clé
*/
private function getNbImagesAnneeAyantTag($mpm=false) {
if ($this->annee == null) {
return null;
}
$requete = "SELECT COUNT(DISTINCT ce_image) AS nb_total FROM del_image_tag WHERE YEAR(date) = " . $this->annee
. " AND ce_image IN (SELECT id_image FROM del_image WHERE year(date_transmission) = " . $this->annee . ")";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total d'images Pictoflora de l'année ayant au moins un vote
*/
private function getNbImagesAnneeAyantVote($mpm=false) {
if ($this->annee == null) {
return null;
}
$requete = "SELECT COUNT(DISTINCT ce_image) AS nb_total FROM del_image_vote WHERE YEAR(date) = " . $this->annee
. " AND ce_image IN (SELECT id_image FROM del_image WHERE year(date_transmission) = " . $this->annee . ")";
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Statistiques sur les votes et mots-clés (tags) de Pictoflora
* "MPM" = moyenne par mois
* - Nombre total de votes
* - Nombre total de mots clés
* - Nombre d'images ayant le mot-clé "defiPhoto"
*/
private function getTagsVotes() {
return array(
'nbTagsTotal' => $this->getNbTagsTotal(),
'nbTagsTotalMPM' => $this->getNbTagsTotal(true),
'nbVotesTotal' => $this->getNbVotesTotal(),
'nbVotesTotalMPM' => $this->getNbVotesTotal(true),
// devra être paramétré par une liste de mots-clés
//'nbImagesTagDP' => $this->getNbImagesTagDP(),
//'nbImagesTagDPMPM' => $this->getNbImagesTagDP(true)
);
}
 
/**
* Nombre total de mots clés Pictoflora
*/
private function getNbTagsTotal($mpm=false) {
$requete = "SELECT COUNT(*) AS nb_total FROM del_image_tag";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre total de votes Pictoflora
*/
private function getNbVotesTotal($mpm=false) {
$requete = "SELECT COUNT(*) AS nb_total FROM del_image_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Statistiques sur les utilisateurs de Pictoflora
* "MPM" = Moyenne par mois
* "AF" = Ayant fait
* - Nombre d'utilisateurs ayant ajouté un mot-clé
* - Nombre d'utilisateurs ayant voté
* - Nombre d'utilisateurs ayant fait une action
*/
private function getUtilisateursPf() {
return array(
'nbUtilisateursAFTag' => $this->getNbUtilisateursPfAFTag(),
'nbUtilisateursAFTagMPM' => $this->getNbUtilisateursPfAFTag(true),
'nbUtilisateursAFVote' => $this->getNbUtilisateursPfAFVote(),
'nbUtilisateursAFVoteMPM' => $this->getNbUtilisateursPfAFVote(true),
'nbUtilisateursAFAction' => $this->getNbUtilisateursPfAFAction(),
'nbUtilisateursAFActionMPM' => $this->getNbUtilisateursPfAFActionMPM()
);
}
 
/**
* Nombre d'utilisateurs ayant ajouté un mot-clé
*/
private function getNbUtilisateursPfAFTag($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_utilisateur) AS nb_total FROM del_image_tag";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs ayant fait un vote
*/
private function getNbUtilisateursPfAFVote($mpm=false) {
$requete = "SELECT COUNT(DISTINCT ce_utilisateur) AS nb_total FROM del_image_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
if ($mpm) {
$requete = $this->encapsulerMPM($requete);
}
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Nombre d'utilisateurs ayant fait une action
*/
private function getNbUtilisateursPfAFAction() {
$requete = "SELECT COUNT(*) AS nb_total FROM (SELECT ce_utilisateur FROM del_image_tag";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " UNION SELECT ce_utilisateur FROM del_image_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= ") AS action";
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Moyenne par mois du nombre d'utilisateurs ayant fait une action
* -> n'est pas encapsulable par encapsulerMPM()
*/
private function getNbUtilisateursPfAFActionMPM() {
$requete = "SELECT avg(nb_total) FROM (SELECT COUNT(*) AS nb_total FROM (SELECT * FROM (SELECT ce_utilisateur, date FROM del_image_tag";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= " UNION SELECT ce_utilisateur, date FROM del_image_vote";
if ($this->annee != null) {
$requete .= ' WHERE YEAR(date) = ' . $this->annee;
}
$requete .= ") AS action GROUP BY ce_utilisateur) AS utildate GROUP BY CONCAT(year(date),month(date))) as truc";
$resultat = $this->bdd->recupererTous($requete);
return intval(array_pop($resultat[0]));
}
 
/**
* Liste des mots-clés les plus fréquents
*/
private function getListeMeilleursTags() {
$liste = array();
$requete = "SELECT count(*) as occurrences, tag FROM del_image_tag";
if ($this->annee != null) {
$requete .= " WHERE YEAR(date) = " . $this->annee;
}
$requete .= " GROUP BY tag ORDER BY occurrences DESC LIMIT 20";
$resultat = $this->bdd->recupererTous($requete);
return array(
'liste' => $resultat
);
}
 
/**
* Liste des utilisateurs ayant ajouté le plus de mots-clés
*/
private function getListeMeilleursTagueurs() {
$resultat = array();
$requete = "SELECT count(*) as nombre, IF (ce_utilisateur REGEXP '^-?[0-9]+$' OR ce_utilisateur REGEXP '^.+@.+$', ce_utilisateur, null) as ce_util FROM del_image_tag";
if ($this->annee != null) {
$requete .= " WHERE year(date) = " . $this->annee;
}
$requete .= " GROUP BY ce_util ORDER BY nombre DESC LIMIT 20";
$resultat = $this->bdd->recupererTous($requete);
 
// Formatage de la liste avec les intitulés des utilisateurs
$ids = array_column($resultat, 'ce_util');
$ids = array_filter($ids, 'is_numeric'); // on oublie les ids de session et autres facéties
$infosUtilisateurs = $this->recupererIntitulesUtilisateursParIds($ids, true);
foreach ($resultat as &$util) {
$ce = $util['ce_util'];
if (isset($infosUtilisateurs[$ce]['intitule'])) {
$util['intitule'] = $infosUtilisateurs[$ce]['intitule'];
} else {
$posa = strpos($ce, '@');
if ($posa !== false) {
$util['intitule'] = substr($ce, 0, $posa+1) . '...';
} else {
$util['intitule'] = null;
}
}
}
 
return array(
'liste' => $resultat
);
}
 
/**
* Encapsule une reqûete de comptage dans un autre morceau de requête
* afin de calculer la moyenne par mois
* @param string $requete count() qui doit renvoyer une colonne 'nb_total'
*/
protected function encapsulerMPM($requete, $colonne="date") {
$requeteEncapsulee = "SELECT AVG(nb_total) as moyenne FROM ("
. $requete
. " GROUP BY CONCAT(year($colonne),month($colonne)) ) AS nombre";
return $requeteEncapsulee;
}
 
/**
* Prend en paramêtre un tableau d'identifiants utilisateurs et retourne après avoir interrogé un
* webservice de l'annuaire un tableau avec en clé l'id et en valeur l'intitulé
* @param array $ids un tableau d'ids
*/
protected function recupererIntitulesUtilisateursParIds(array $ids) {
$service = "utilisateur/infos-par-id/" . implode(',', $ids);
$url = $this->conteneur->getParametre('urlServiceBaseAnnuaire') . $service;
//echo "URL: $url\n\n";
$json = $this->conteneur->getRestClient()->consulter($url);
$resultat = json_decode($json, true);
return $resultat;
}
}
/branches/v1.11-magnesium/services/modules/0.1/Observations.php
New file
0,0 → 1,227
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des services Observations.
*
* URLs possibles :
* GET :
* http://localhost/service:del:0.1/observations
* toutes les observations (infos obs, infos images, infos propositions, infos nb commentaires)
*
* http://localhost/service:del:0.1/observations?retour.format=widget
* toutes les infos des observations pour le Widget DEL
*
* http://localhost/service:del:0.1/observations/#idObs
* une observation donnée et ses images, SANS LES propositions & nombre de commentaire*
*
* http://localhost/service:del:0.1/observations/#idObs/#idVote/vote
* toutes les infos sur les votes d'une proposition
*
* PUT :
* http://localhost/service:del:0.1/observations/#idObs/#idCommentaire/vote
* ajoute un vote (+ ou -) pour une obs et une proposition donnée
*
* POST :
* http://localhost/service:del:0.1/observations/#idObs
* utilisé seulement par les admins pour modifier une obs depuis DEL (dépublication des obs)
*
* http://localhost/service:del:0.1/observations/#idObs/#idCommentaire/vote
* modifie un vote (+ ou -) pour une obs et une proposition donnée
*
* @category DEL
* @package Services
* @subpackage Observations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class Observations extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'observations';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
$this->analyserRessoucesConsultation();
} else if ($this->methode == 'modifier' || $this->methode == 'ajouter') {
$this->analyserRessoucesModification();
}
}
 
private function analyserRessoucesConsultation() {
if (count($this->ressources) == 0) {
// http://localhost/service:del:0.1/observations
$this->sousServiceNom = 'liste-observations';
} else if (count($this->ressources) == 1) {
if ($this->etreRessourceIdentifiant(0)) {
// http://localhost/service:del:0.1/observations/#idObs
$this->sousServiceNom = 'observation-details';
}
} else if (count($this->ressources) == 3) {
if ($this->etreRessourceIdentifiant(0) && $this->etreRessourceIdentifiant(1) && $this->verifierRessourceValeur(2, 'vote')) {
// http://localhost/service:del:0.1/observations/#idObs/#idProposition/vote/
$this->sousServiceNom = 'vote-observation';
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function analyserRessoucesModification() {
if (count($this->ressources) == 1) {
if ($this->methode == 'modifier' && $this->etreRessourceIdentifiant(0)) {
// http://localhost/service:del:0.1/observations/#idObs
$this->sousServiceNom = 'observation-details';
}
} else if (count($this->ressources) == 3) {
if ($this->etreRessourceIdentifiant(0) && $this->etreRessourceIdentifiant(1) && $this->verifierRessourceValeur(2, 'vote')) {
// http://localhost/service:del:0.1/observations/#idObs/#idProposition/vote/
$this->sousServiceNom = 'vote-observation';
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function etreRessourceIdentifiant($num) {
$presenceId = false;
if (isset($this->ressources[$num]) && is_numeric($this->ressources[$num])) {
$presenceId = true;
}
return $presenceId;
}
 
private function verifierRessourceValeur($num, $valeur) {
$ok = false;
if (isset($this->ressources[$num]) && $this->ressources[$num] == $valeur) {
$ok = true;
}
return $ok;
}
 
private function verifierParametreValeur($cle, $valeur) {
$ok = false;
if (isset($this->parametres[$cle]) && $this->ressources[$cle] == $valeur) {
$ok = true;
}
return $ok;
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : observations, observations/#idObs/#idProposition/vote \n".
" - en POST : observations/#id, observations/#idObs/#idProposition/vote \n".
" - en PUT : observations/#idObs/#idProposition/vote \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
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);
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/images/VotesImage.php
New file
0,0 → 1,307
<?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 DEL
* @package Services
* @subpackage Images
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class VotesImage {
 
private $conteneur;
private $navigation;
private $bdd;
private $mapping = array();
private $ressources;
private $parametres;
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->navigation = $conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
 
$this->mapping = $this->conteneur->getParametreTableau('votes.mapping');
}
 
/**
* 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->ressources = $ressources;
$this->parametres = $parametres;
 
// 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->navigation->getEntete(), 'resultats' => $votes);
return $resultat;
}
 
private function chargerVotes() {
$idImgP = $this->bdd->proteger($this->ressources[0]);
$idProtocoleP = isset($this->parametres['protocole']) ? $this->bdd->proteger($this->parametres['protocole']) : null;
$requete = 'SELECT * FROM del_image_vote '.
"WHERE ce_image = $idImgP ".
($idProtocoleP != null ? "AND ce_protocole = $idProtocoleP " : '').
' -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
return $this->formaterVotes($resultats);
}
 
private function compterVotes() {
$requete = 'SELECT FOUND_ROWS() AS nbre -- '.__FILE__.' : '.__LINE__;
$resultats = $this->bdd->recuperer($requete);
return (int) $resultats['nbre'];
}
 
private function formaterVotes($votes) {
$retour = array();
foreach ($votes as $vote) {
foreach ($vote as $champ => $valeur) {
$attribut = $this->mapping[$champ];
$retour[$vote['id_vote']][$attribut] = $valeur;
}
}
return $retour;
}
 
public function ajouter($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
 
$this->verifierParametresAjoutModif();
$idVote = $this->ajouterVote();
if ($idVote) {
self::updateStats($this->bdd, $this->ressources[0], $this->parametres['protocole']);
 
$resultat = new ResultatService();
$resultat->corps = array('id_vote' => $idVote);
return $resultat;
}
return false;
}
 
private function ajouterVote() {
$idImgP = $this->bdd->proteger($this->ressources[0]);
$idProtocoleP = $this->bdd->proteger($this->parametres['protocole']);
$idUtilisateurP = $this->bdd->proteger($this->parametres['utilisateur']);
$valeurP = $this->bdd->proteger($this->parametres['valeur']);
$this->verifierAutorisationProtocoleIdentifie($this->parametres['protocole'], $this->parametres['utilisateur']);
$requete = 'INSERT INTO del_image_vote (ce_image, ce_protocole, ce_utilisateur, valeur, date) '.
"VALUES ( $idImgP, $idProtocoleP, $idUtilisateurP, $valeurP, NOW()) ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultat = $this->bdd->executer($requete);
if ($resultat === false) {
$msg = "Un problème est survenu lors de l'ajout d'un vote.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
} else if ($resultat === 0) {
$msg = "Aucun vote ne correspond au critères fournis : ".
"idImg -> $idImgP, id_protocole -> $idProtocoleP et id_utilisateur -> $idUtilisateurP.";
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
// ATTENTION : idVote doit être récupéré avant toute nouvelle requete !
$idVote = $this->bdd->recupererIdDernierAjout();
return $idVote;
}
 
public function verifierParametresAjoutModif() {
$erreurs = array();
if (!is_numeric($this->ressources[0])) {
$erreurs[] = "Le paramètre indiquant l'identifiant de l'image doit être numérique.";
}
 
if (!isset($this->parametres['utilisateur'])) {
$erreurs[] = "Paramètre 'utilisateur' manquant.";
}
 
if (!isset($this->parametres['protocole'])) {
$erreurs[] = "Paramètre 'id_protocole' manquant.";
} else {
if (!is_numeric($this->parametres['protocole'])) {
$erreurs[] = "Le paramètre 'protocole' doit être numérique.";
}
}
 
if (!isset($this->parametres['valeur'])) {
$erreurs[] = "Paramètre 'valeur' manquant.";
} else {
if (!is_numeric($this->parametres['valeur'])) {
$erreurs[] = "Le paramètre 'valeur' doit être numérique.";
}
}
 
if (!empty($erreurs)) {
$msg = "Erreur lors de la configuration : \n".implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
public function modifier($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->verifierParametresAjoutModif();
$resultat = $this->modifierVote();
if ($resultat > 0) {
self::updateStats($this->bdd, $this->ressources[0],$this->parametres['protocole']);
return 'ok';
}
}
 
public function modifierVote() {
$valeurP = $this->bdd->proteger($this->parametres['valeur']);
$idImgP = $this->bdd->proteger($this->ressources[0]);
$idProtocoleP = $this->bdd->proteger($this->parametres['protocole']);
$idUtilisateurP = $this->bdd->proteger($this->parametres['utilisateur']);
 
$this->verifierAutorisationProtocoleIdentifie($this->parametres['protocole'], $this->parametres['utilisateur']);
$requete = 'UPDATE del_image_vote '.
"SET valeur = $valeurP, date = NOW() ".
"WHERE ce_image = $idImgP AND ce_protocole = $idProtocoleP AND ce_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultat = $this->bdd->executer($requete);
if ($resultat === false) {
$msg = "Un erreur est survenu lors de la tentative de modification du vote.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
} else if ($resultat === 0) {
$msg = "Aucun vote ne correspond au critères fournis : ".
"idImg -> $idImgP, id_protocole -> $idProtocoleP et id_utilisateur -> $idUtilisateurP.";
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
return $resultat;
}
 
public function supprimer($ressources) {
$this->ressources = $ressources;
$this->verifierParametresSuppression();
$idVoteP = $this->bdd->proteger($this->ressources[2]);
 
$voteInfos = $this->recupererInfosVote($idVoteP);
$this->verifierAutorisationSuppression($voteInfos);
 
$resultat = $this->supprimerVote($idVoteP);
if ($resultat > 0) {
$idImg = $this->ressources[0];
self::updateStats($this->bdd, $idImg, $voteInfos['id_protocole']);
return 'ok';
}
}
 
public function verifierParametresSuppression() {
$erreurs = array();
if (!isset($this->ressources[0])) {
$erreurs[] = "Le paramètre indiquant l'identifiant de l'image est obligatoire.";
} else {
if (!is_numeric($this->ressources[0])) {
$erreurs[] = "Le paramètre indiquant l'identifiant de l'image doit être numérique.";
}
}
if (!isset($this->ressources[2])) {
$erreurs[] = "Le paramètre indiquant l'identifiant du vote est obligatoire.";
} else {
if (!is_numeric($this->ressources[2])) {
$erreurs[] = "Le paramètre indiquant l'identifiant du vote doit être numérique.";
}
}
 
if (!empty($erreurs)) {
$msg = 'Erreur lors de la configuration : '."\n".implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
 
private function recupererInfosVote($idVoteP) {
$requete = 'SELECT id_vote, ce_protocole AS id_protocole, ce_utilisateur AS id_utilisateur '.
'FROM del_image_vote '.
"WHERE id_vote = $idVoteP ".
' -- '.__FILE__.' : '.__LINE__;
 
$infos = $this->bdd->recuperer($requete);
if ($infos === false) {
$msg = "Aucun vote ne correspond à l'identifiant $idVoteP.";
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
return $infos;
}
private function verifierAutorisationProtocoleIdentifie($idProtocole, $idUtilisateur) {
$idProtocoleP = $this->bdd->proteger($idProtocole);
$gestion_utilisateur = $this->conteneur->getUtilisateur();
$utilisateur = $gestion_utilisateur->getUtilisateur();
$utilisateurAComparer = $gestion_utilisateur->getIdAnonymeTemporaire();
// Si l'utilisateur n'est pas identifié on vérifie que le protocole n'impose
// pas d'être connecté pour voter, et le cas échéant, on refuse le vote
if(!$utilisateur['connecte']) {
$requete_proto_identifie = 'SELECT identifie FROM del_image_protocole '.
'WHERE id_protocole = '.$idProtocoleP;
$proto_identifie = $this->bdd->recuperer($requete_proto_identifie);
if($proto_identifie['identifie'] == 1) {
$msg = "Ce protocole nécéssite d'être identifié pour voter.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
} else {
$utilisateurAComparer = $utilisateur['id_utilisateur'];
}
// Sinon on vérifie tout de même que la personne identifiée est bien celle
// associée aux votes dans les paramètres
if($utilisateurAComparer != $idUtilisateur) {
$msg = "L'utilisateur identifié et l'utilisateur du vote ne correspondent pas.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
private function verifierAutorisationSuppression($voteInfos) {
$gestion_utilisateur = $this->conteneur->getUtilisateur();
$utilisateur = $gestion_utilisateur->getUtilisateur();
// @SECURE vérifier que l'utilisateur qui modifie son vote est bien celui qui est connecté
if (isset($utilisateur['id_utilisateur']) &&
$utilisateur['id_utilisateur'] != $voteInfos['id_utilisateur'] &&
$gestion_utilisateur->getIdAnonymeTemporaire() != $voteInfos['id_utilisateur']) {
$message = "Vous n'êtes pas autorisé à supprimer le vote : {$voteInfos['id_vote']}";
throw new Exception($message, RestServeur::HTTP_CODE_ACCES_NON_AUTORISE);
}
}
 
private function supprimerVote($idVoteP) {
$requete = "DELETE FROM del_image_vote WHERE id_vote = $idVoteP ".' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->executer($requete);
if ($resultat === false) {
$msg = "Impossible de supprimer le vote $idVoteP.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
return $resultat;
}
 
// intermédiaire pour la méthode contenue dans "Commun"
static function updateStats($db, $id_image, $id_protocole) {
return TelaBotanica\Del\Commun\Stats::updateStats($db, $id_image, $id_protocole);
}
}
/branches/v1.11-magnesium/services/modules/0.1/images/ListeImages.php
New file
0,0 → 1,168
<?php
// declare(encoding='UTF-8');
/**
* Listes des images avec leurs infos liées.
*
* 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
*
* @category DEL
* @package Services
* @subpackage Images
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
//restore_error_handler();
//restore_exception_handler();
//error_reporting(E_ALL);
class ListeImages {
 
private $conteneur;
private $bdd;
private $filtrage;
private $sql;
private $navigation;
private $paramsFiltres = array();
private $mappings = array();
private $idsImagesOrdonnees = array();
private $infosImages = array();
private $infosImagesOrdonnees = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->filtrage = $this->conteneur->getParametresFiltrage();
$this->sql = $this->conteneur->getSql();
$this->navigation = $this->conteneur->getNavigation();
 
$this->mappings['observations'] = $this->conteneur->getParametreTableau('observations.mapping');
$this->mappings['images'] = $this->conteneur->getParametreTableau('images.mapping');
}
 
public function consulter($ressources, $parametres) {
$this->paramsFiltres = $this->filtrage->filtrerUrlParamsAppliImg();
$this->sql->setAppli(Sql::APPLI_IMG);
$this->sql->setParametres($this->paramsFiltres);
$this->sql->ajouterContraintes();
$this->sql->ajouterConstrainteAppliImg();
$this->sql->definirOrdreSqlAppliImg();
 
$this->idsImagesOrdonnees = $this->getIdImages();
$this->navigation->setTotal($this->sql->getTotalLignesTrouvees());
 
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => array());
if (count($this->idsImagesOrdonnees) > 0) {
$this->infosImages = $this->getInfosImages();
$this->infosImagesOrdonnees = $this->formaterImages();
 
// Chargement des votes pour ces images et pour *tous* les protocoles
$votes = $this->sql->getVotesDesImages($this->idsImagesOrdonnees);
if ($votes) {
// ATTENTION : $images est récupéré par référence !
$this->sql->ajouterInfosVotesProtocoles($votes, $this->infosImagesOrdonnees);
}
 
$resultat->corps = array(
'entete' => $this->navigation->getEntete(),
'resultats' => array_values($this->infosImagesOrdonnees));
}
return $resultat;
}
 
private function getIdImages() {
$requete = 'SELECT SQL_CALC_FOUND_ROWS DISTINCT di.id_image '.
'FROM del_image AS di '.
$this->sql->getJoin().
'WHERE '.$this->sql->getWhere().
$this->sql->getGroupBy().
$this->sql->getOrderBy().
$this->sql->getLimit().
' -- '.__FILE__.':'.__LINE__;
// TODO : si le DISCTINCT dans la requête pose des pb de perf, supprimer les doublons d'id à l'aide de PHP
//Debug::printr($requete);exit();
$resultats = $this->bdd->recupererTous($requete);
$idImgs = array();
if ($resultats !== false ) {
foreach ($resultats as $resultat) {
$idImgs[] = $resultat['id_image'];
}
}
return $idImgs;
}
 
private function getInfosImages() {
// Traitement du champ mots clés text à part pour éviter un écrasement des mots
// clés img par ceux de l'obs
// TODO: gérer ce problème pour tous les champs où ça peut arriver
$mot_cles_texte_concat = "di.mots_cles_texte as mots_cles_texte_img";
$obsChamps = $this->sql->getAliasDesChamps($this->mappings['observations'], null, 'do');
$imgChamps = $this->sql->getAliasDesChamps($this->mappings['images'], null, 'di');
$idImgsConcat = implode(',', $this->idsImagesOrdonnees);
 
$requete = "SELECT $obsChamps, $imgChamps, $mot_cles_texte_concat ".
'FROM del_image AS di '.
' LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) '.
"WHERE di.id_image IN ($idImgsConcat) ".
'-- '.__FILE__.':'.__LINE__;
//Debug::printr($requete);
return $this->bdd->recupererTous($requete);
}
 
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
private function formaterImages() {
$urlImgTpl = $this->conteneur->getParametre('cel_img_url_tpl');
$imageFormat = isset($this->paramsFiltres['format']) ? $this->paramsFiltres['format'] : 'XL';
$obsFormatees = array_flip($this->idsImagesOrdonnees);// Permet de garder l'ordre de sortie !
foreach ($this->infosImages as $infos) {
$id = $infos['id_image'];
// 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' => $id,
'binaire.href' => sprintf($urlImgTpl, $infos['id_image'], $imageFormat),
'mots_cles_texte' => isset($infos['mots_cles_texte_img']) ? $infos['mots_cles_texte_img'] : null,
);
unset($infos['id_image'], $infos['mots_cles_texte'], $infos['mots_cles_texte_img']);
 
$obsFormatees[$id] = $image;
$obsFormatees[$id]['observation'] = $infos;
$obsFormatees[$id]['protocoles_votes'] = array();
}
return $obsFormatees;
}
 
/**
* Supprime une image directement dans le CEL en faisant un appel à un web service du CEL.
* Utilisé uniquement par les admins.
*
* @param array $ressources tableau des informations contenues dans l'url après le nom du service
* @param array $parametres contenu du post
* @return mixed Chaine "OK" (en majuscule) en cas de succès, booléen "false" en cas d'échec
*/
public function supprimer($ressources) {
$gestionUtilisateurs = $this->conteneur->getUtilisateur();
$gestionUtilisateurs->etreUtilisateurAvecDroitAdmin();
 
$urlServiceBase = $this->conteneur->getParametre('urlServiceCelImage');
$idImage = $ressources[0];
$url = $urlServiceBase.$idImage;
 
$clientHttp = $this->conteneur->getRestClient();
$retourCel = $clientHttp->supprimer($url);
$retour = preg_match('/^OK$/i', $retourCel) ? 'OK' : false;
return $retour;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+d84f6381a2c4b4dcdd4e3de6e7b10fb8ad8e334d
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/nomstaxons/ListeTaxons.php
New file
0,0 → 1,72
<?php
// declare(encoding='UTF-8');
/**
* Web service récupèrant une liste de noms de taxons suivant un référentiel et un masque donné.
*
* @category DEL
* @package Services
* @subpackage NomsTaxons
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class ListeTaxons {
 
private $conteneur;
private $navigation;
private $bdd;
 
private $resultatsBruts = array();
private $resultats = array();
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->navigation = $this->conteneur->getNavigation();
$this->bdd = $this->conteneur->getBdd();
}
 
public function consulter() {
$this->chargerNoms();
$this->formaterResultats();
$this->mettreAJourEnteteResultats();
 
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => $this->resultats);
return $resultat;
}
 
private function chargerNoms() {
$referentiel = $this->navigation->getFiltre('masque.referentiel');
 
if ($referentiel != 'tous') {
$requete = urlencode($this->navigation->getFiltre('masque.nom'));
$url = sprintf($this->conteneur->getParametre('nomstaxons.url_autocompletion_tpl'), $referentiel, $requete);
$restClient = $this->conteneur->getRestClient();
$resultatJson = $restClient->consulter($url);
$this->resultatsBruts =(array) json_decode($resultatJson, true);
}
}
 
private function formaterResultats() {
if (isset($this->resultatsBruts['resultat'])) {
foreach ($this->resultatsBruts['resultat'] as $info) {
$this->resultats[] = array(
"nn" => $info['num_nom'],
"ns" => $info['nom_sci_complet'],
"retenu" => ($info['retenu'] === "true" ? true : false)
);
}
}
}
 
private function mettreAJourEnteteResultats() {
$total = count($this->resultats);
$this->navigation->setTotal($total);
$this->navigation->setSansLimite();
}
}
/branches/v1.11-magnesium/services/modules/0.1/Syndication.php
New file
0,0 → 1,178
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services de syndication.
*
* @category DEL
* @package Services
* @subpackage Syndication
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Syndication extends RestService {
 
private $parametres = array();
private $ressources = array();
private $conteneur;
private $methode = null;
private $sousServiceNom = null;
private $cheminCourant = null;
 
private $serviceNom = 'syndication';
private $format = 'atom';
private $squelette_dossier = null;
 
/** Indique si oui (true) ou non (false), on veut utiliser les paramètres bruts. */
protected $utilisationParametresBruts = true;
 
public function __construct() {
$this->conteneur = new Conteneur();
$this->cheminCourant = dirname(__FILE__).DS;
$this->squelette_dossier = dirname(__FILE__).DS.$this->serviceNom.DS.'squelettes'.DS;
$this->formats_autorises = $this->conteneur->getParametreTableau('syndication.formats');
}
 
public function consulter($ressources, $parametres) {
$this->methode = 'consulter';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
$this->verifierRessourcesEtParametres();
$this->format = isset($this->parametres['format']) ? $this->parametres['format'] : $this->format;
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function verifierRessourcesEtParametres() {
if (isset($this->parametres['format']) && !in_array($this->parametres['format'], $this->formats_autorises)) {
$msg = "Vous devez indiquer un format de flux valide.\n".$this->getDoc();
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
}
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$donnees = $this->traiterRessources();
$resultat = $this->creerResultatService($donnees);
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
$corps = $reponseHttp->getCorps();
return $corps;
}
 
private function traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter' && isset($this->ressources[0])) {
if (preg_match('/^tags|votes-?-Par-?Tag|tags-?Par-?Protocole$/i', $this->ressources[0])) {
$this->sousServiceNom = 'tags';
} else if (preg_match('/^votes|votes-?Par-?Protocole$/i', $this->ressources[0])){
$this->sousServiceNom = 'votes';
} else if ($this->ressources[0] == 'commentaires') {
$this->sousServiceNom = 'commentaires';
}
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service {$this->serviceNom} !\n".
$this->getDoc();
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
public function getDoc() {
$formatsAutorises = implode(', ', $this->formats_autorises);
return "Les URLs disponibles pour ce service sont :\n".
" * en GET :\n".
" - syndication/commentaires\n".
" - syndication/tags\n".
" - syndication/votes-par-protocole\n".
" Paramètres : \n".
" - format : $formatsAutorises";
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService();
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
 
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter();
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->serviceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"est introuvable par le service '{$this->serviceNom}' !\n".
$this->getDoc();
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
return $retour;
}
 
private function obtenirNomClasseService() {
return str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $this->sousServiceNom))));
}
 
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;
}
 
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;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+40211bf16084bd2d96250989c0bb58b4fa8dbe36
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/Determinations.php
New file
0,0 → 1,143
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services concernant les "déterminations" dans DEL.
*
* URLs possibles :
* GET :
* http://localhost/del/services/0.1/determinations/images-determinations-probables =>
*
* POST :
* http://localhost/del/services/0.1/determinations/valider-determination/#idProposition => Permet d'accepter une proposition donnée
*
* @category DEL
* @package Services
* @subpackage Determinations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Determinations extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'determinations';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function modifier($ressources, $requeteDonnees) {
$this->methode = 'modifier';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if ($this->ressources[0] == 'images-determinations-probables') {
$this->sousServiceNom = 'liste-images-determinations-probables';
}
} else if ($this->methode == 'modifier') {
if ($this->ressources[0] == 'valider-determination') {
$this->sousServiceNom = 'valider-determination';
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : determinations/images-determinations-probables \n".
" - en POST : determinations/valider-determination";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($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)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/Plantnet.php
New file
0,0 → 1,83
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services de Plantnet.
*
* @category DEL
* @package Services
* @subpackage Plantnet
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Samuel DUFOUR-KOWALSKI <samuel.dufour@cirad.fr>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class PlantNet extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $projetNom = 'plantnet';
private $serviceNom = 'changements';
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';
$reponseHttp = new ReponseHttp();
try {
$this->ressources = $ressources;
$this->parametres = $parametres;
 
$this->conteneur = new Conteneur($this->parametres);
 
$resultat = $this->initialiserService();
$reponseHttp->setResultatService($resultat);
} catch (Exception $e) {
$reponseHttp->ajouterErreur($e);
}
$reponseHttp->emettreLesEntetes();
return $reponseHttp->getCorps();
}
 
/*------------------------------------------------------------------------------------------------------------------
CONFIGURATION DU SERVICE
------------------------------------------------------------------------------------------------------------------*/
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->serviceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->projetNom.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
$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 obtenirNomClasseService($mot) {
$classeNom = str_replace(' ', '', ucwords(strtolower(str_replace('-', ' ', $mot))));
return $classeNom;
}
}
/branches/v1.11-magnesium/services/modules/0.1/Commentaires.php
New file
0,0 → 1,168
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services concernant les commentaires.
*
* URLs possibles :
*
* GET :
* http://localhost/del/services/0.1/commentaires => liste tous les commentaires
* http://localhost/del/services/0.1/commentaires/#id => retourne le contenu d'un commentaire d'id #id
*
* PUT :
* http://localhost/del/services/0.1/commentaires => Ajoute un nouveau commentaire
*
* DELETE :
* http://localhost/del/services/0.1/commentaires/#id => supprime le commentaire d'id #id
*
* @category DEL
* @package Services
* @subpackage Commentaires
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Commentaires extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'commentaires';
private $sousServiceNom = null;
private $cheminCourant = null;
private $erreur = 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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
public function ajouter($ressources, $requeteDonnees) {
$this->methode = 'ajouter';
$this->initialiserRessourcesEtParametres($ressources, $requeteDonnees);
return $this->executerService();
}
 
public function supprimer($ressources) {
$this->methode = 'supprimer';
$this->initialiserRessourcesEtParametres($ressources);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if (!isset($this->ressources) || empty($this->ressources)) {
$this->sousServiceNom = 'liste-commentaires';
} else if (isset($this->ressources[0]) && count($this->ressources) == 1 && is_numeric($this->ressources[0])) {
$this->sousServiceNom = 'commentaire-details';
}
} else if ($this->methode == 'ajouter') {
$this->sousServiceNom = 'ajouter-commentaire';
} else if ($this->methode == 'supprimer') {
if (isset($this->ressources[0]) && count($this->ressources) == 1 && is_numeric($this->ressources[0])) {
$this->sousServiceNom = 'supprimer-commentaire';
} else {
$this->erreur = "L'identifiant du commentaire est obligatoire et doit être un entier.";
}
}
 
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
if ($this->erreur != null) {
$message = $this->erreur;
} else {
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : commentaires, commentaires/#id \n".
" - en PUT : commentaires".
" - en DELETE : commentaires/#id\n".
"#id représente un nombre entier identifiant un commentaire.";
}
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
//echo $this->sousServiceNom.':'.$classe."\n";
//echo 'Ressources :'.print_r($this->ressources, true);
//echo 'Parametres :'.print_r($this->parametres, true);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter($this->ressources);
} elseif ($this->methode == 'ajouter') {
$retour = $service->ajouter($this->parametres);
} elseif ($this->methode == 'supprimer') {
$retour = $service->supprimer($this->ressources);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"n'existe pas dans le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/Communes.php
New file
0,0 → 1,133
<?php
// declare(encoding='UTF-8');
/**
* Classe principale de chargement des sous-services d'accès aux infos sur les communes.
*
* @category DEL
* @package Services
* @subpackage Communes
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class Communes extends RestService {
 
private $parametres = array();
private $ressources = array();
private $methode = null;
private $serviceNom = 'communes';
private $sousServiceNom = null;
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';
$this->initialiserRessourcesEtParametres($ressources, $parametres);
return $this->executerService();
}
 
private function initialiserRessourcesEtParametres($ressources, $parametres = array()) {
$this->ressources = $ressources;
$this->parametres = $parametres;
}
 
private function executerService() {
$reponseHttp = new ReponseHttp();
try {
$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 traiterRessources() {
$this->analyserRessources();
$retour = $this->initialiserService();
return $retour;
}
 
private function analyserRessources() {
if ($this->methode == 'consulter') {
if (count($this->ressources) == 0 && $this->verifierPresenceParametre('masque.nom')) {
$this->sousServiceNom = 'liste-communes';
}
}
if ($this->sousServiceNom == null) {
$this->lancerMessageErreurRessource();
}
}
 
private function verifierPresenceParametre($cle) {
if (isset($this->parametres[$cle]) && trim($this->parametres[$cle]) == '') {
$message = "Le service demandé '{$this->serviceNom}' ".
"nécessite l'utilisation d'un paramètre (non vide) : masque.nom \n";
throw new Exception($message, RestServeur::HTTP_CODE_ECHEC_CONDITION);
}
return true;
}
 
private function lancerMessageErreurRessource() {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "La ressource demandée '$ressource' ".
"n'est pas disponible pour le service ".$this->serviceNom." !\n".
"Les URLs disponibles sont : \n".
" - en GET : communes (paramètres : masque.nom) \n";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
 
private function initialiserService() {
$classe = $this->obtenirNomClasseService($this->sousServiceNom);
$chemins = array();
$chemins[] = $this->cheminCourant.$this->serviceNom.DS.$classe.'.php';
$chemins[] = $this->cheminCourant.'commun'.DS.$classe.'.php';
$retour = '';
$service = null;
 
foreach ($chemins as $chemin) {
if (file_exists($chemin)) {
require_once $chemin;
$service = new $classe($this->conteneur);
if ($this->methode == 'consulter') {
$retour = $service->consulter();
} else {
$message = "Le sous-service '{$this->sousServiceNom}' du service '{$this->serviceNom}' ".
"ne possède pas de méthode '{$this->methode}' !";
$code = RestServeur::HTTP_NON_IMPLEMENTE;
throw new Exception($message, $code);
}
}
}
 
if (is_null($service)) {
$ressource = $this->sousServiceNom.'/'.implode('/', $this->ressources);
$message = "Le classe '$classe' correspondant à la ressource '$ressource' ".
"est introuvable par le service '{$this->serviceNom}' !";
$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.11-magnesium/services/modules/0.1/observations/VoteObservation.php
New file
0,0 → 1,249
<?php
// declare(encoding='UTF-8');
/**
* Web service permetant d'ajouter ou de modifier les votes associés aux propositions d'une observation.
*
* @category DEL
* @package Services
* @subpackage Observations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
 
class VoteObservation {
private $conteneur;
private $bdd;
private $navigation;
private $mapping;
private $ressources;
private $parametres;
 
public function __construct(Conteneur $conteneur = null) {
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->navigation = $conteneur->getNavigation();
 
$this->mapping = $this->conteneur->getParametreTableau('votes.mapping');
}
 
public function consulter($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
 
$votes = $this->chargerVotes();
$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->navigation->getEntete(), 'resultats' => $votes);
return $resultat;
}
 
private function chargerVotes() {
$idCommentaireP = $this->bdd->proteger($this->ressources[1]);
$requete = 'SELECT * '.
'FROM del_commentaire_vote '.
"WHERE ce_proposition = $idCommentaireP ";
' -- '.__FILE__.':'.__LINE__;
$resultats = $this->bdd->recupererTous($requete);
$votes = array();
foreach ($resultats as $vote) {
$votes[$vote['id_vote']] = $this->formaterVotes($vote);
$utilisateur = $this->chercherUtilisateur($vote['ce_utilisateur']);
if ($utilisateur && count($utilisateur) > 0) {
$votes[$vote['id_vote']] = array_merge($votes[$vote['id_vote']], $utilisateur);
}
}
return $votes;
}
 
private function formaterVotes($vote) {
$retour = array();
foreach ($vote as $param => $valeur) {
$retour[$this->mapping[$param]] = $valeur;
}
return $retour;
}
 
/**
* Recherche les coordonnées d'un utilisateur en fonction de son ID;
* puisqu'il a un ID c'est qu'il est connecté, on suppose donc qu'il
* a un tuple dans del_utilisateur_infos
*
* @TODO vérifier cette hypothèse
*/
private function chercherUtilisateur($id) {
// par défaut, pas d'info
$utilisateur = array();
// Si l'id utilisateur est un hash de session, on ne cherche rien
if (is_numeric($id)) {
$idUtilisateurP = $this->bdd->proteger($id);
$requete = "SELECT id_utilisateur AS 'auteur.id', nom AS 'auteur.nom', prenom AS 'auteur.prenom', ".
"courriel AS 'auteur.courriel' ".
'FROM del_utilisateur_infos '.
"WHERE id_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.':'.__LINE__;
$utilisateur = $this->bdd->recuperer($requete);
}
return $utilisateur;
}
 
public function ajouter($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->verifierParametresAjoutModif();
$idVote = $this->ajouterVote();
if ($idVote) {
$resultat = new ResultatService();
$resultat->corps = array('id_vote' => $idVote);
return $resultat;
}
return false;
}
 
private function ajouterVote() {
$idProposition = $this->creerPropositionDeterminationInitiale();
 
$idObsP = $this->bdd->proteger($this->ressources[0]);
$idPropositionP = $this->bdd->proteger($idProposition);
$idUtilisateurP = $this->bdd->proteger($this->parametres['utilisateur']);
$valeurP = $this->bdd->proteger($this->parametres['valeur']);
$requete = 'INSERT INTO del_commentaire_vote (ce_proposition , ce_utilisateur , valeur , date) '.
"VALUES ($idPropositionP, $idUtilisateurP, $valeurP, NOW()) ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultat = $this->bdd->executer($requete);
if ($resultat === false) {
$msg = "Un problème est survenu lors de l'ajout d'un vote.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
} else if ($resultat === 0) {
$msg = "Aucun vote ne correspond au critères fournis : ".
"idObs -> $idObsP, idProposition -> $idPropositionP et id_utilisateur -> $idUtilisateurP.";
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
// ATTENTION : idVote doit être récupéré avant toute nouvelle requete !
$idVote = $this->bdd->recupererIdDernierAjout();
return $idVote;
}
 
/** 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)
*/
private function creerPropositionDeterminationInitiale() {
$idProposition = $this->ressources[1];
if ($idProposition == 0) {
$propositionExiste = $this->verifierExistencePropositionInitiale();
if ($propositionExiste === false) {
$idProposition = $this->creerPropositionInitiale();
} else {
$idProposition = $this->getIdPropositionInitiale();
}
}
return $idProposition;
}
 
private function verifierExistencePropositionInitiale() {
$idObservationP = $this->bdd->proteger($this->ressources[0]);
$requete = 'SELECT COUNT(*) >= 1 AS existe '.
'FROM del_commentaire '.
"WHERE ce_observation = $idObservationP ".
'AND proposition_initiale = 1 '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recuperer($requete);
return $resultat['existe'] == 1;
}
 
private function creerPropositionInitiale() {
$idObservationP = $this->bdd->proteger($this->ressources[0]);
$requete = '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_utilisateur, nom_utilisateur, '.
' courriel_utilisateur, nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, '.
" nt, famille, nom_referentiel, NOW(), '1' ".
'FROM del_observation do '.
"WHERE id_observation = $idObservationP ".
' -- '.__FILE__.' : '.__LINE__;
 
$this->bdd->executer($requete);
$id = $this->bdd->recupererIdDernierAjout();
return $id;
}
 
private function getIdPropositionInitiale() {
$idObservationP = $this->bdd->proteger($this->ressources[0]);
$requete = 'SELECT id_commentaire '.
'FROM del_commentaire '.
"WHERE ce_observation = $idObservationP ".
'AND proposition_initiale = 1 '.
' -- '.__FILE__.' : '.__LINE__;
$resultat = $this->bdd->recuperer($requete);
return $resultat['id_commentaire'];
}
 
public function modifier($ressources, $parametres) {
$this->ressources = $ressources;
$this->parametres = $parametres;
$this->verifierParametresAjoutModif();
$resultat = $this->modifierVote();
if ($resultat > 0) {
return 'ok';
}
}
 
private function modifierVote() {
// protection anti-usurpateurs @TODO faudrait pt-être la faire !
$idUtilisateur = $this->parametres['utilisateur'];
/*$gestionUtilisateurs = $this->conteneur->getUtilisateur();
$gestionUtilisateurs->controleUtilisateurIdentifie($idUtilisateur);*/
 
$idObsP = $this->bdd->proteger($this->ressources[0]);
$idPropositionP = $this->bdd->proteger($this->ressources[1]);
$idUtilisateurP = $this->bdd->proteger($idUtilisateur);
$valeurP = $this->bdd->proteger($this->parametres['valeur']);
$requete = 'UPDATE del_commentaire_vote '.
"SET valeur = $valeurP, date = NOW() ".
"WHERE ce_proposition = $idPropositionP AND ce_utilisateur = $idUtilisateurP ".
' -- '.__FILE__.' : '.__LINE__;
 
$resultat = $this->bdd->executer($requete);
if ($resultat === false) {
$msg = "Une erreur est survenue lors de la tentative de modification du vote.";
throw new Exception($msg, RestServeur::HTTP_CODE_ERREUR);
} else if ($resultat === 0) {
$msg = "Aucun vote ne correspond au critères fournis : ".
"idObs -> $idObsP, idProposition -> $idPropositionP et id_utilisateur -> $idUtilisateurP.";
throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
return $resultat;
}
 
private function verifierParametresAjoutModif() {
$erreurs = array();
 
if (!isset($this->parametres['utilisateur'])) {
$erreurs[] = 'Paramètre "utilisateur" manquant.';
}
 
if (!isset($this->parametres['valeur'])) {
$erreurs[] = 'Paramètre "valeur" manquant.';
} else {
if (!is_numeric($this->parametres['valeur'])) {
$erreurs[] = 'Le paramètre "valeur" doit être numérique.';
} elseif($this->parametres['valeur'] != 0 && $this->parametres['valeur'] != 1) {
$erreurs[] = 'Le paramètre "valeur" ne peut prendre que la valeur 0 ou 1.';
}
}
 
if (!empty($erreurs)) {
$msg = "Erreur lors de la configuration : \n".implode("\n", $erreurs);
throw new Exception($msg, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
}
}
}
/branches/v1.11-magnesium/services/modules/0.1/observations/ListeObservations.php
New file
0,0 → 1,386
<?php
// declare(encoding='UTF-8');
/**
* Web service récupèrant toutes les observations et, pour chacune d'elle, les images qui lui sont associées.
*
* ATTENTION : le web service commence par récupérer seulement les id des obs (1er requete SQL), puis dans une
* deuxième requête SQL récupère les informations complémentaires. Il s'avère qu'en procédant ainsi le web service
* est 3 fois plus rapide !
*
* @category DEL
* @package Services
* @subpackage Observations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ListeObservations {
 
private $conteneur;
private $bdd;
private $navigation;
private $filtrage;
private $sql;
 
private $mappings = array();
private $paramsFiltres = array();
 
private $idsObsOrdonnees = array();
private $infosObs = array();
private $infosObsOrdonnee = array();
private $evenementsObs = array();
 
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
 
$this->bdd = $this->conteneur->getBdd();
$this->filtrage = $this->conteneur->getParametresFiltrage();
$this->sql = $this->conteneur->getSql();
$this->navigation = $this->conteneur->getNavigation();
 
$this->mappings['votes'] = $this->conteneur->getParametreTableau('votes.mapping');
$this->mappings['commentaires'] = $this->conteneur->getParametreTableau('commentaires.mapping');
}
 
public function consulter($ressources, $parametres) {
$this->paramsFiltres = $this->filtrage->filtrerUrlParamsAppliObs();
$this->sql->setAppli(Sql::APPLI_OBS);
$this->sql->setParametres($this->paramsFiltres);
$this->sql->ajouterContraintes();
$this->sql->ajouterConstrainteAppliObs();
$this->sql->definirOrdreSqlAppliObs();
 
$this->idsObsOrdonnees = $this->getIdObs();
$this->navigation->setTotal($this->sql->getTotalLignesTrouvees());
 
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats
$resultat = new ResultatService();
$resultat->corps = array('entete' => $this->navigation->getEntete(), 'resultats' => array());
if (count($this->idsObsOrdonnees) > 0) {
 
// 2) récupération des données nécessaires pour ces observations (obs + images)
$this->infosObs = $this->getInfosObs();
// 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
$this->infosObsOrdonnees = $this->formaterObservations();
// 4) Ajouter commentaires + votes à $this->infosObsOrdonnees
$this->chargerDeterminations();
 
$resultat->corps = array(
'entete' => $this->navigation->getEntete(),
// 5) Applatissage du tableau afin de garder l'ordre de tri
// (qui n'est pas garanti dans un objet json)
'resultats' => array_values($this->infosObsOrdonnees));
}
return $resultat;
}
 
// 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
*/
private function getIdObs() {
$requete = $this->renvoyerRequeteSelonType();
//Debug::printr($requete);
$resultats = $this->bdd->recupererTous($requete);
 
$idObs = array();
if ($resultats !== false ) {
foreach ($resultats as $resultat) {
$idObs[] = $resultat['id_observation'];
}
}
return $idObs;
}
private function renvoyerRequeteSelonType() {
if($this->monActiviteEstDemandee()) {
$gestion_utilisateur = new GestionUtilisateur($this->conteneur);
$utilisateur = $gestion_utilisateur->getUtilisateur();
if ($utilisateur['connecte'] === true) {
$id_utilisateur = $utilisateur['id_utilisateur'];
$requete = $this->sql->getRequeteIdObsMonactiviteTout($id_utilisateur, $this->sql->getLimit()).' -- '.__FILE__.':'.__LINE__;
// Enregistrement de la date de consultation pour ne pas réafficher des événements déjà consultés
$gestion_utilisateur->setDerniereDateConsultationEvenements($id_utilisateur, date('Y-m-d H:i:s'));
} else {
//TODO: que faire si l'on n'est pas connecté ?
}
} else {
$requete = 'SELECT SQL_CALC_FOUND_ROWS id_observation '.
'FROM del_observation AS do '.
$this->sql->getJoin().
'WHERE '.$this->sql->getWhere().
$this->sql->getGroupBy().
$this->sql->getOrderBy().
$this->sql->getLimit().
' -- '.__FILE__.':'.__LINE__;
}
/*echo "REQ: "; var_dump($requete);
exit;*/
return $requete;
}
private function monActiviteEstDemandee() {
return isset($this->paramsFiltres['masque.type']) && in_array('monactivite',array_keys($this->paramsFiltres['masque.type']));
}
 
/**
* Après avoir récupérer seulement les ids dans une première requête, nous récupérons maintenant les infos.
* Le web service est ainsi 3 fois plus rapide.
*/
private function getInfosObs() {
$idsObsConcat = implode(',', $this->idsObsOrdonnees);
$requete = "SELECT 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, pays, '.
'zone_geo, lieudit, station, milieu, date_observation, do.mots_cles_texte, '.
'do.date_transmission, do.commentaire, '.
'do.ce_utilisateur AS `auteur.id`, do.prenom_utilisateur AS `auteur.prenom`, '.
'do.nom_utilisateur AS `auteur.nom`, do.courriel_utilisateur AS `auteur.courriel`, '.
'id_image, date_prise_de_vue AS `date`, hauteur, largeur, nom_original '.
'FROM del_observation AS do '.
' LEFT JOIN del_image AS di ON (do.id_observation = di.ce_observation) '.
"WHERE id_observation IN ($idsObsConcat) ".
' -- '.__FILE__.':'.__LINE__;
if ($this->monActiviteEstDemandee()) {
$this->stockerEvenementsObs($idsObsConcat);
}
//Debug::printr($requete);
return $this->bdd->recupererTous($requete);
}
private function stockerEvenementsObs($idsObsConcat) {
$gestion_utilisateur = new GestionUtilisateur($this->conteneur);
$utilisateur = $gestion_utilisateur->getUtilisateur();
$id_utilisateur = $utilisateur['id_utilisateur'];
$evenements = $this->sql->getEvenementsObs($idsObsConcat, $id_utilisateur);
$this->evenements_obs = array();
foreach($evenements as &$evenement) {
$this->affecterTypeEvenement($evenement, $id_utilisateur, $evenement['id_observation']);
}
}
 
private function affecterTypeEvenement(&$evenement, $id_utilisateur, $id_observation) {
 
$type = "";
$infos = "";
// La date maximale détermine le type d'évènement
switch($evenement['date_max']) {
// Quelqu'un a fait un nouveau commentaire ou proposition
case $evenement['date_com']:
if(!empty($evenement['nom_sel_com'])) {
$type = 'nouvelle_proposition';
$infos = $evenement['proposition_commentaire_nom_sel'];
} else {
$type = 'nouveau_commentaire';
$infos = $evenement['proposition_commentaire_texte'];
}
// J'ai commenté ou fait une proposition
if($evenement['utilisateur_commentaire'] == $id_utilisateur) {
$type .= "_vous_a_obs_autre";
} else {
$type .= "_autre_sur_obs_vous";
}
break;
 
// Quelqu'un a répondu à un de mes commentaires ou une de mes propositions
case $evenement['date_com_reponse']:
if(!empty($evenement['nom_sel_com_parent'])) {
$type = 'nouvelle_reponse_autre_sur_proposition_vous';
} else {
$type = 'nouvelle_reponse_autre_sur_commentaire_vous';
}
$infos = $evenement['proposition_commentaire_texte_commente'];
break;
// Quelqu'un a fait un nouveau vote
case $evenement['date_vote']:
$type = 'nouveau_vote';
// Sur une proposition qui n'est pas à moi sur une observation à moi
if($evenement['utilisateur_commentaire_vote'] != $evenement['utilisateur_observation'] && $evenement['utilisateur_commentaire_vote'] != $id_utilisateur) {
$type .= "_autre_sur_com_autre_obs_vous";
} else {
// Sur une proposition qui est à moi sur une observation (à moi ou non)
$type .= "_autre_sur_com_vous";
}
$infos = $evenement['proposition_commentaire_nom_sel_votee'];
break;
// Quelqu'un a validé une proposition
case $evenement['date_validation']:
$type = "nouvelle_validation_autre_sur_prop_vous";
$infos = $evenement['proposition_validee_nom_sel'];
// $type = "nouvelle_validation_vous_a_prop_autre";
break;
// Cas qui ne devrait jamais arriver
default:
$type = 'inconnu';
$infos = "";
}
$infos_evts = array('type' => $type, 'infos_complementaires' => $infos);
// La requête est un peu trop complexe et certains évènements sortent en doublons
// donc on dédoublonne ici (mais ça n'est pas une solution pérenne)
// TODO: optimiser et simplifier ceci
if(empty($this->evenementsObs[$id_observation])) {
$this->evenementsObs[$id_observation] = array();
}
if(array_search($infos_evts, $this->evenementsObs[$id_observation]) === false) {
$this->evenementsObs[$id_observation][] = $infos_evts;
}
}
 
/**
* Les informations étant extraites d'une vue dont les infos des obs sont dupliquées pour chaque image,
* il nous faut maintenant récupérer qu'une seule fois les données d'observations et y intégrer les données
* des images.
*/
private function formaterObservations() {
$observations = array_map('array_filter', $this->infosObs);
$obsFormatees = array_flip($this->idsObsOrdonnees);// Permet de garder l'ordre de sortie !
foreach ($observations as &$obs) {
$this->nettoyerAuteur($obs);
 
$id = $obs['id_observation'];
// ATTENTION : la requête retourne de nombreuses lignes avec les mêmes données (test de l'existence nécessaire)
if (is_array($obsFormatees[$id]) === false) {
$obsFormatees[$id] = $obs;
}
$obsFormatees[$id]['images'][] = $this->extraireInfosImage($obs);
if(isset($this->evenementsObs[$id])) {
$obsFormatees[$id]['evenements'] = $this->evenementsObs[$id];
}
}
return $obsFormatees;
}
 
private function nettoyerAuteur(&$obs) {
// 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($obs['auteur.id']) || !is_numeric($obs['auteur.id'])) {
$obs['auteur.id'] = "0";
}
if (!isset($obs['auteur.nom'])) {
$obs['auteur.nom'] = '[inconnu]';
}
}
 
private function extraireInfosImage(&$obs) {
$champsImageAffichables = array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original');
$image = array_intersect_key($obs, array_flip($champsImageAffichables));
$urlImgTpl = $this->conteneur->getParametre('cel_img_url_tpl');
$image['binaire.href'] = sprintf($urlImgTpl, $image['id_image'], 'XL');
 
unset($obs['id_image'], $obs['date'], $obs['hauteur'], $obs['largeur'], $obs['nom_original']);
return $image;
}
 
/**
* 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() {
$idObsConcat = implode(',', $this->idsObsOrdonnees);
$requete = 'SELECT * '.
'FROM del_commentaire AS dc '.
'WHERE dc.nom_sel IS NOT NULL '.
"AND ce_observation IN ($idObsConcat) ".
'-- '.__FILE__.':'.__LINE__;
$commentaires = $this->chargerNombreCommentaireObs();
 
$propositions = $this->bdd->recupererTous($requete);
if ($propositions) {
foreach ($propositions as $proposition) {
$idObs = $proposition['ce_observation'];
$idComment = $proposition['id_commentaire'];
$comment = $this->formaterDetermination($idComment, $proposition);
if ($comment) {
$this->infosObsOrdonnees[$idObs]['commentaires'][$idComment] = $comment;
}
$this->infosObsOrdonnees[$idObs]['nb_commentaires'] = isset($commentaires[$idObs]) ? $commentaires[$idObs] : 0;
}
}
}
 
private function formaterDetermination($propositionId, $propositionInfos) {
if (!$propositionInfos) return NULL;
 
$propositionFormatee = array();
foreach ($this->mappings['commentaires'] as $nomChamp => $nomAttributJson) {
if (isset($propositionInfos[$nomChamp])) {
$propositionFormatee[$nomAttributJson] = $propositionInfos[$nomChamp];
}
}
 
// Charger les votes sur les déterminations
$requete = "SELECT * FROM del_commentaire_vote WHERE ce_proposition = $propositionId".
'-- '.__FILE__.':'.__LINE__;
$resultatsVotes = $this->bdd->recupererTous($requete);
foreach ($resultatsVotes as $vote) {
$propositionFormatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
}
 
$propositionFormatee['nb_commentaires'] = $this->chargerNombreCommentaire($propositionId);
 
return $propositionFormatee;
}
 
/**
* Formater un vote en fonction du fichier de configuration config_votes.ini
* @param $votes array()
*/
private function formaterVote($vote) {
$voteFormate = array();
foreach ($vote as $nomChamp => $valeur) {
$voteFormate[$this->mappings['votes'][$nomChamp]] = $valeur;
}
return $voteFormate;
}
private function chargerNombreCommentaireObs() {
$idObsConcat = implode(',', $this->idsObsOrdonnees);
$requete = 'SELECT ce_observation, COUNT( id_commentaire ) AS nb '.
'FROM del_commentaire '.
"WHERE ce_observation IN ($idObsConcat) ".
'GROUP BY ce_observation '.
'-- '.__FILE__.':'.__LINE__;
$commentaires = $this->bdd->recupererTous($requete);
$commentaires_par_obs = array();
foreach($commentaires as $commentaire) {
$commentaires_par_obs[$commentaire['ce_observation']] = $commentaire['nb'];
}
return $commentaires_par_obs;
}
 
private function chargerNombreCommentaire($propositionId) {
$requete = 'SELECT COUNT( id_commentaire ) AS nb '.
'FROM del_commentaire '.
"WHERE ce_proposition = $propositionId ".
'GROUP BY ce_proposition '.
'-- '.__FILE__.':'.__LINE__;
$commentaires = $this->bdd->recuperer($requete);
return $commentaires ? $commentaires['nb'] : 0;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+a907dccbc2c4971fed812325a132c6ba9d675b72
\ No newline at end of property
/branches/v1.11-magnesium/services/modules/0.1/observations/ObservationDetails.php
New file
0,0 → 1,210
<?php
// declare(encoding='UTF-8');
/**
* Web service retournant toutes les infos d'une observation donnée :
* images, votes sur image et protocole, commentaires, votes sur commentaires, ...
*
* @category DEL
* @package Services
* @subpackage Observations
* @version 0.1
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
*/
class ObservationDetails {
 
private $conteneur;
private $bdd;
private $sql;
private $idObs;
private $protocole;
private $observation;
private $mappings = array();
 
public function __construct(Conteneur $conteneur) {
$this->conteneur = $conteneur;
$this->bdd = $this->conteneur->getBdd();
$this->sql = $this->conteneur->getSql();
 
$this->mappings['observations'] = $this->conteneur->getParametreTableau('observations.mapping');
$this->mappings['images'] = $this->conteneur->getParametreTableau('images.mapping');
$this->mappings['votes'] = $this->conteneur->getParametreTableau('votes.mapping');
$this->mappings['commentaires'] = $this->conteneur->getParametreTableau('commentaires.mapping');
// les deux alias suivants sont particuliers afin d'éviter un conflit d'alias lors des jointures avec del_commentaire_vote
$this->mappings['commentaires']['ce_utilisateur'] = '__auteur_com';
$this->mappings['commentaires']['date'] = '__date_com';
}
 
public function consulter($ressources, $parametres) {
$this->idObs = $ressources[0];
$this->protocole = isset($parametres['protocole']) && is_numeric($parametres['protocole']) ? intval($parametres['protocole']) : null;
 
$infos = $this->getInfosObservationEtImages();
if (! $infos) {
$message = "Aucune observation ne possède d'identifiant '{$this->idObs}'.";
throw new Exception($message, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
}
$this->formaterObservation($infos);
 
// 3) charge les données de votes et protocoles associés aux images
 
if ($this->observation['images']) {
$idsImages = array_keys($this->observation['images']);
$votes = $this->sql->getVotesDesImages($idsImages, $this->protocole);
$this->sql->ajouterInfosVotesProtocoles($votes, $this->observation['images']);
}
 
// 4) charge les commentaires et les votes associés -> modifie/créé $observation['commentaires']
$commentaires = $this->getCommentaires();
$this->ajouterCommentaires($commentaires);
 
// désindexe le tableau (tel qu'apparement attendu par les applis), c'est une exception
// TODO : corriger l'appli cliente pour utiliser les index puis supprimer cette ligne
$this->observation['images'] = array_values($this->observation['images']);
 
// autre élément de post-processing: le ce_utilisateur de l'observation non-numeric...
$this->nettoyerAuteur();
 
// Mettre en forme le résultat et l'envoyer pour affichage
$resultat = new ResultatService();
$resultat->corps = $this->observation;
return $resultat;
}
 
private function getInfosObservationEtImages() {
$obsChamps = $this->sql->getAliasDesChamps($this->mappings['observations'], null, 'do');
$imgChamps = $this->sql->getAliasDesChamps($this->mappings['images'], null, 'di');
 
// rétrocompatibilité champs de l'annuaire après suppression de del_utilisateur
$annuaireChamps = implode(', ', array(
"do.prenom_utilisateur AS `auteur.prenom`",
"do.nom_utilisateur AS `auteur.nom`",
"do.courriel_utilisateur AS `auteur.courriel`"));
 
$requete = "SELECT $obsChamps, $imgChamps, $annuaireChamps ".
"FROM del_observation AS do ".
" LEFT JOIN del_image AS di ON (do.id_observation = di.ce_observation) ".
"WHERE do.id_observation = {$this->idObs} ".
'-- '.__FILE__.':'.__LINE__;
//Debug::printr($requete);
return $this->bdd->recupererTous($requete);
}
 
private function formaterObservation($infos) {
$infos = array_filter($infos);
foreach ($infos as $info) {
$image = array_intersect_key($info, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
$urlImgTpl = $this->conteneur->getParametre('cel_img_url_tpl');
$imageFormat = 'XL';
$image['binaire.href'] = sprintf($urlImgTpl, $image['id_image'], $imageFormat);
unset($info['id_image'], $info['date'], $info['hauteur'], $info['largeur'], $info['nom_original']);
 
// ATTENTION : la requête retourne de nombreuses lignes avec les mêmes données (test de l'existence nécessaire)
if (!isset($this->observation)) {
$this->observation = $info;
$this->observation['images'] = array();
}
if (!isset($this->observation['images'][$image['id_image']])) {
$this->observation['images'][$image['id_image']] = $image;
}
}
}
 
private function getCommentaires() {
$selectVotes = array('id_vote', 'ce_proposition', 'ce_utilisateur', 'valeur', 'date');
$selectCommentaires = 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','proposition_retenue');
 
$voteChamps = $this->sql->getAliasDesChamps($this->mappings['votes'], $selectVotes, 'cv');
$commentaireChamps = $this->sql->getAliasDesChamps($this->mappings['commentaires'], $selectCommentaires, 'dc');
 
// LEFT JOIN optionnel, mais explicatif : récupèration des infos de vote que pour les commentaires comportant un nom_sel "valide"
$requete = "SELECT $commentaireChamps, $voteChamps ".
"FROM del_commentaire AS dc ".
" LEFT JOIN del_commentaire_vote AS cv ".
" ON (cv.ce_proposition = dc.id_commentaire AND dc.nom_sel != '' AND dc.nom_sel IS NOT NULL) ".
"WHERE ce_observation = {$this->idObs} ".
'-- '.__FILE__.':'.__LINE__;
 
$commentaires = $this->bdd->recupererTous($requete);
return $commentaires;
 
}
 
private function ajouterCommentaires($commentaires) {
if (!$commentaires) return;
 
$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($this->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($this->mappings['votes']));
$ret[$commentId]['votes'][$voteId] = $vote;
}
$this->observation['commentaires'] = $ret;
}
 
private function nettoyerAuteur() {
if (!isset($this->observation['auteur.id']) || !is_numeric($this->observation['auteur.id'])) {
$this->observation['auteur.id'] = '0';
}
if (!isset($this->observation['auteur.nom'])) {
$this->observation['auteur.nom'] = '[inconnu]';
}
}
 
/**
* Modifie une observation directement dans le CEL en faisant un appel à un web service du CEL.
* Utilisé uniquement par les admins.
* Permet de dépublier une observation.
*
* @param array $ressources tableau des informations contenues dans l'url après le nom du service
* @param array $parametres contenu du post
* @return mixed Chaine "OK" (en majuscule) en cas de succès, booléen "false" en cas d'échec
*/
public function modifier($ressources, $parametres) {
$gestionUtilisateurs = $this->conteneur->getUtilisateur();
$gestionUtilisateurs->etreUtilisateurAvecDroitAdmin();
 
$retour = false;
if (isset($parametres['transmission'])) {
$idObs = $ressources[0];
$clientRest = $this->conteneur->getRestClient();
$urlTpl = $this->conteneur->getParametre('urlServiceCelObs');
$url = $urlTpl.$idObs;
$retourCel = $clientRest->modifier($url, $parametres);
$retour = preg_match('/^OK$/i', $retourCel) ? 'OK' : false;
if ($retour === false) {
$message = "Erreur du web service CEL : ".$retourCel;
$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
throw new Exception($message, $code);
}
} else {
$message = "Ce web service doit contenir un paramètre 'transmission'.";
$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
throw new Exception($message, $code);
}
return $retour;
}
}
Property changes:
Added: svnkit:entry:sha1-checksum
+4089895a5dcb7422fbe6b5f30ea02926993bd44b
\ No newline at end of property
/branches/v1.11-magnesium/services/configurations/config.defaut.ini
New file
0,0 → 1,347
; 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 et PictoFlora
; Abréviation de l'application
info.abr = del-services
; Version du Framework nécessaire au fonctionnement de cette application
info.framework.version = 0.4
;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:3306"
; 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 = "tb_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 = /services/del/
; URL à rediriger
serveur.baseAlternativeURL = /service:del:0.1/
 
; +------------------------------------------------------------------------------------------------------+
; CONFIG GÉNÉRALE de DEL
 
; URL de base des services
url_base = "http://localhost/"
; URL de base des services de DEL
url_service_base = "{ref:url_base}service:del:0.1/"
 
; Chemin vers les fichiers PHP communs aux scripts et services (au format relatif par rapport au fichier index.php)
chemin_del_commun = "../../commun"
 
; Droits des utilisateurs
droit_coordinateur = "1"
droit_superadmin = "2"
; Liste des ips (nom de domaine) autorisés à accéder aux services de DEL
ip_autorisees = "127.0.0.1, 193.54.123.169, 193.54.123.216"
 
; Lien de base vers l'appli DEL
obs_appli_lien = "http://www.tela-botanica.org/appli:identiplante"
img_appli_lien = "http://www.tela-botanica.org/appli:pictoflora"
 
; Lien de base vers la fiche de l'observation dans DEL
obs_fiche_tpl = "{ref:obs_appli_lien}/obs%s"
img_fiche_tpl = "{ref:img_appli_lien}#img~%s"
 
; Liste des valeurs autorisés pour certains paramètres d'URL :
valeurs_ordre = "asc, desc"
valeurs_referentiel = "bdtfx, bdtxa, isfan, apd, lbf, bdtre, aublet, florical"
valeurs_type = "adeterminer, aconfirmer, endiscussion, validees, monactivite"
 
; Liste des mots-clés CEL utilisés dans IdentiPlante/PictoFlora
mots_cles_cel_affiches = "fleur,fleurs,feuille,feuilles,ecorce,fruit,fruits,port,defiphoto,plantnet"
 
; +------------------------------------------------------------------------------------------------------+
; SERVICES du CEL
; URL de base des services du CEL
urlServiceBaseCel = "{ref:url_base}service:cel:"
; Service du CEL pour manipuler une image à distance
urlServiceCelImage = "{ref:urlServiceBaseCel}CelImage/";
; Service du CEL pour manipuler une observation à distance
urlServiceCelObs = "{ref:urlServiceBaseCel}CelObs/";
; Service du CEL permetant d'obtenir la liste des communes pour l'auto-complétion
urlServiceCelCommune = "{ref:urlServiceBaseCel}LocationSearch/";
; Squelette d'Url permettant d'afficher une image du CEL (remplace %s par l'id de l'image sans underscore)
cel_img_url_tpl = "http://api.tela-botanica.org/img:%09d%s.jpg"
 
; +------------------------------------------------------------------------------------------------------+
; SERVICES d'eFlore
; URL de base des services d'eFlore
url_service_base_eflore = "{ref:url_base}service:eflore:0.1/"
 
; +------------------------------------------------------------------------------------------------------+
; AUTRES SERVICES
; URL de base des services de l'annuaire
urlServiceBaseAnnuaire = "http://localhost/service:annuaire:"
urlServiceBaseAuth = "https://localhost/service:annuaire:auth/"
 
; +------------------------------------------------------------------------------------------------------+
; APPLI OBS = PictoFlora
[appli_img]
; Filtres de l'url (=paramètres) pour lesquel un tri est possible
tris_possibles = "date_transmission, date_observation, moyenne-arithmetique, tags, points"
; Identifiant du protocole par défaut (3 = Capitalisation d'image)
protocole_defaut = "3"
; Formats disponibles pour les images :
img_formats_possibles = "O, CRX2S, CRS, CXS, CS, XS, S, M, L, CRL, XL, X2L, X3L"
 
; +------------------------------------------------------------------------------------------------------+
; APPLI OBS = IdentiPlante
[appli_obs]
; Filtres de l'url (=paramètres) pour lesquel un tri est possible
tris_possibles = "date_transmission, date_observation, nb_commentaires"
 
; +------------------------------------------------------------------------------------------------------+
; CONFIGURATIONS des SERVICES
[mots-cles]
; Masque de filtrage possible pour la consultation :
masques_possibles = "image,auteur.id"
; Mapping champs JSON / champs base de données :
mapping = "
id_tag = id_mot_cle,
ce_image = image,
ce_utilisateur = auteur.id,
date = date,
tag = mot_cle"
 
[commentaires]
; Masque de filtrage possible pour la consultation :
masques_possibles = "proposition,observation"
; Mapping champs JSON / champs base de données :
mapping = "
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,
proposition_retenue = proposition_retenue"
 
[communes]
; Masque de filtrage possible pour la consultation :
masques_possibles = "masque.nom"
 
[determinations]
; Masque de filtrage possible pour la consultation :
masques_possibles = "masque.protocole,masque.valeur_vote_min";
; Mapping champs JSON / champs base de données :
mapping = "
famille = famille,
ns = nom_sel,
nn = nom_sel_nn,
date = date_observation,
tag = mots_cles_texte,
commune = zone_geo"
; Mots-clés du CEL à prendre en compte dans DEL
mots_cles_cel_affiches = "fleur,fleurs,feuille,feuilles,ecorce,fruit,fruits,port,plantnet,plantscan_new";
; Format d'image pour les liens du web service ListeImagesDeterminationsProbables
format_image = "L"
; Template d'URL pour la fiche eFlore d'un nom
url_fiche_eflore_tpl = "http://www.tela-botanica.org/%s-nn-%s";
 
[nomstaxons]
; Masque de filtrage possible pour la consultation :
masques_possibles = "masque.nom,masque.referentiel"
; URL de base du service appelé pour autocompléter les noms de taxons
url_autocompletion_tpl = "{ref:url_service_base_eflore}%s/noms?masque=%s&recherche=etendue&navigation.limite=50&ns.structure=au&retour.format=min&retour.tri=alpharet&retour.structure=liste";
 
[protocoles]
; Mapping champs JSON / champs base de données :
mapping = "
id_protocole = protocole.id,
intitule = protocole.intitule,
descriptif = protocole.descriptif,
tag = protocole.tag,
mots_cles = protocole.mots_cles,
identifie = protocole.identifie"
 
[syndication]
; Masque de filtrage possible pour la consultation, tout type de syndication confondus :
masques_possibles = "auteur,espece,observation,image,protocole"
; Mapping champs JSON / champs BDD pour tous les sous-services
;TODO : si problématique séparer en mapping spécifique à chaque sous-service et fusionner avec les params ..._filtres
mapping = "
espece = nom_sel,
observation = ce_observation,
image = id_image,
protocole = ce_protocole"
 
; Filtres disponibles pour chaque type de syndication
commentaire_filtres = "auteur,espece,observation"
vote_filtres = "protocole"
tag_filtres = ""
 
; Liste des formats de flux disponibles
formats = "rss1,rss2,atom"
 
; Editeur du flux
editeur = "Tela Botanica"
 
; Infos sur le générateur
generateur_nom = "DEL - Syndication"
generateur_version = "1.0"
 
; Format du Guid de DEL pour le flux de syndication
commentaire_guid_tpl = "urn:lsid:tela-botanica.org:del:commentaire%s"
vote_guid_tpl = "urn:lsid:tela-botanica.org:del:vote%s"
tag_guid_tpl = "urn:lsid:tela-botanica.org:del:tag%s"
 
; Titre
commentaire_titre = "identiPlante : commentaires et propositions"
vote_titre = "pictoFlora : votes"
tag_titre = "pictoFlora : tags"
 
; Descriptions des flux
commentaire_dsc = "Ce flux regroupe les dernières déterminations et commentaires rédigés dans l'application identiPlante"
vote_dsc = "Ce flux regroupe les derniers votes sur les images de pictoFlora"
tag_dsc = "Ce flux regroupe les derniers tags des images de pictoFlora"
 
[observations]
; Masque de filtrage possible pour la consultation :
masques_possibles = "masque,masque.famille,masque.genre,
masque.referentiel, masque.ns, masque.nn, masque.auteur, masque.date,
masque.commune, masque.departement, masque.tag_cel, masque.espece,
masque.pninscritsseulement,
navigation.depart, navigation.limite, tri, ordre, masque.type, masque.pays"
; Valeurs par défaut pour les paramètres de l'url :
parametres_valeurs_defaut = "
navigation.depart = 0,
navigation.limite = 10,
tri = date_transmission,
ordre = desc,
masque.type = null"
; Mapping champs JSON / champs base de données :
mapping = "
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,
pays = pays,
ce_zone_geo = id_zone_geo,
zone_geo = zone_geo,
lieudit = lieudit,
station = station,
milieu = milieu,
mots_cles_texte = mots_cles_texte,
commentaire = commentaire,
ce_utilisateur = auteur.id,
nom_utilisateur = auteur.nom,
prenom_utilisateur = auteur.prenom,
courriel_utilisateur = auteur.courriel"
; Texte du tag "à déterminer"
tag_adeterminer = aDeterminer
; Permet d'indiquer le nombre de commentaire nécessaire pour que l'observation apparaisse dans l'onglet "En discussion" d'IdentiPlante.
nb_commentaires_discussion = 1
 
[images]
; Masque de filtrage possible pour la consultation :
; Dans fichier config : "auteur,date,"
masques_possibles = "protocole,
masque, masque.famille, masque.genre, masque.espece,
masque.referentiel, masque.ns, masque.nn, masque.nt,
masque.commune, masque.departement, masque.id_zone_geo,
masque.auteur, masque.date, masque.type, masque.milieu,
masque.tag, masque.tag_cel, masque.tag_del,
masque.pninscritsseulement,
navigation.depart, navigation.limite, tri, ordre, format, masque.pays"
; Valeurs par défaut pour les paramètres de l'url :
parametres_valeurs_defaut = "
navigation.depart = 0,
navigation.limite = 10,
tri = date_transmission,
ordre = desc,
format = XL"
; Mapping champs JSON / champs base de données
mapping = "
id_image = id_image,
hauteur = hauteur,
date_prise_de_vue = date,
nom_original = nom_original,
mots_cles_texte = mots_cles_texte_img"
 
[votes]
; Mapping champs JSON / champs base de données :
mapping = "
id_vote = vote.id,
valeur = vote,
ce_protocole = protocole.id,
ce_proposition = proposition.id,
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"
 
[protocoles]
; Mapping champs JSON / champs base de données :
mapping = "
ce_protocole = protocole.id,
id_protocole = protocole.id,
intitule = protocole.intitule,
descriptif = protocole.descriptif,
tag = protocole.tag,
mots_cles = protocole.mots_cles"
; +------------------------------------------------------------------------------------------------------+
; Messages de validation
[message]
lien_profil = "http://www.tela-botanica.org/profil:%s"
; Titre du message de validation par un admin/validateur d'identiplante
titre_message_validation = "IdentiPlante : un telabotaniste vous a aidé";
; Page d'aide du wiki contenant les explications sur la validation
lien_wiki_validation = "http://www.tela-botanica.org/wikini/identiplante/wakka.php?wiki=TelabotanisteAvances"
Property changes:
Added: svnkit:entry:sha1-checksum
+43aaff7f302ddba1504b6409428b18f0d8b0dbb6
\ No newline at end of property
/branches/v1.11-magnesium/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.11-magnesium/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.11-magnesium/services/configurations
New file
Property changes:
Added: svn:ignore
+config.ini
/branches/v1.11-magnesium/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.11-magnesium/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.11-magnesium/services/.
New file
Property changes:
Added: svn:mergeinfo
Merged /branches/v1.3-beryllium/services:r1753,1758
Merged /branches/v1.6-azote/services:r1988
Merged /branches/v1.2-lithium/services:r1676
Merged /branches/v1.5-carbone/services:r1893-1921,1942
Merged /branches/v1.10-sodium/services:r2096,2116,2120,2138-2139,2143,2145
Merged /branches/v1.0-hydrogene/services:r1507
Merged /branches/v1.7-oxygene/services:r2003,2006-2007,2009-2010
Added: svn:ignore
+framework.php