Subversion Repositories eFlore/Applications.del

Compare Revisions

No changes between revisions

Ignore whitespace Rev 2094 → Rev 2095

/branches/v1.10-sodium/services/bibliotheque/GestionUtilisateur.php
New file
0,0 → 1,336
<?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;
 
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é
*
* @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
$donneesJeton = $this->decoderJeton($jeton);
if ($donneesJeton != null && $donneesJeton["sub"] != "") {
// récupération de l'utilisateur
$courriel = $donneesJeton["sub"];
$utilisateur = $this->recupererUtilisateurEnBdd($courriel);
}
}
}
if ($utilisateur != null) {
$utilisateur = $this->completerInfosUtilisateur($utilisateur);
}
return $utilisateur;
}
 
/**
* 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 = base64_decode($payload);
$payload = json_decode($payload, true);
 
return $payload;
}
 
/**
* 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 du.id_utilisateur, nom, prenom, courriel, dui.admin '.
'FROM del_utilisateur AS du '.
' LEFT JOIN del_utilisateur_infos AS dui ON (du.id_utilisateur = dui.id_utilisateur) '.
"WHERE courriel = $loginP ".
' -- '.__FILE__.' : '.__LINE__;
 
return $this->bdd->recuperer($requete);
}
 
protected function completerInfosUtilisateur($utilisateur) {
$utilisateur['session_id'] = session_id();
$utilisateur['connecte'] = true;
return $utilisateur;
}
 
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);
$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;
}
 
public function initialiserInfosUtilisateur($id_utilisateur) {
$preferences_defaut = $this->obtenirTableauPreferenceDefaut();
$prefsEncodeesP = $this->bdd->proteger(json_encode($preferences_defaut));
$idUtilisateurP = $this->bdd->proteger($id_utilisateur);
$requete = 'INSERT INTO del_utilisateur_infos '.
'(id_utilisateur, admin, preferences, date_premiere_utilisation )'.
"VALUES ($idUtilisateurP, 0, $prefsEncodeesP, 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.10-sodium/services/bibliotheque/ControleAcces.php
New file
0,0 → 1,234
<?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) {
$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.10-sodium/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.10-sodium/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.10-sodium/services/bibliotheque/SyndicationOutils.php
New file
0,0 → 1,77
<?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;
}
 
public function demanderAutorisationAdmin() {
$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.10-sodium/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.10-sodium/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.10-sodium/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.10-sodium/services/bibliotheque/Sql.php
New file
0,0 → 1,842
<?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éfix de del_image est "di"
* - le préfix de del_observation est "do"
* - le préfix 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' ? true : false;
}
 
private function etreAppliObs() {
return $this->appli === 'OBS' ? true : false;
}
 
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();
}
 
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();
}
}
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) ';
}
 
public function ajouterConstrainteAppliObs() {
$this->ajouterContrainteTagCel();
$this->ajouterContrainteType();
// TODO : ATTENTION -> vue 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();
}
 
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) {
$tagsMotif = $this->construireTagsMotif();
if (is_null($tagsMotif) === false) {
$sousRequete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'GROUP BY ce_image '.
"HAVING GROUP_CONCAT(DISTINCT tag_normalise ORDER BY tag_normalise) REGEXP $tagsMotif ";
}
} 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;
}
 
private function construireTagsMotif() {
$tagsMotif = null;
if (isset($this->parametres['masque.tag_del']['AND'])) {
$tags = $this->parametres['masque.tag_del']['AND'];
// ATTENTION -> optimisation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($tags);
$tagsMotif = $this->bdd->proteger(implode('.*', $tags));
} else if (isset($this->parametres['masque.tag_del']['OR'])) {
$tags = $this->parametres['masque.tag_del']['OR'];
$tagsMotif = $this->bdd->proteger(implode('|', $tags));
}
return $tagsMotif;
}
 
/**
* 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.10-sodium/services/bibliotheque/ParametresFiltrage.php
New file
0,0 → 1,475
<?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->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;
}
}
}
 
/** 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.10-sodium/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'];
}
}