Subversion Repositories eFlore/Applications.del

Rev

Rev 2157 | 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']);
        }
}