* @author Jean-Pascal MILCENT * @author Aurelien PERONNET * @license GPL v3 * @license CECILL v2 * @copyright 1999-2014 Tela Botanica */ 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']); } }