Rev 2210 | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?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']);
}
}