* @author Jean-Pascal MILCENT * @author Aurelien PERONNET * @license GPL v3 * @license CECILL v2 * @copyright 1999-2014 Tela Botanica */ class AlerteMailCommentaires { private $conteneur; private $testeurCourriel = null; private $recapitulatifAdmin = []; private $messageRecapitulatif = null; private $dateRenvoi = null; private $observations_concernees = array(); private $correspondance_id_obs_auteurs = array(); private $utilisateursInfos = array(); public function __construct($conteneur) { $this->conteneur = $conteneur; } public function executer() { try { $this->verifierModeUtilisation(); $this->verifierDateRenvoi(); $observations = $this->chargerObservationsCommentees(); if (!empty($observations)) { $this->formaterObservations($observations); $commentaires = $this->chargerCommentairesDuJour(); $this->formaterCommentaires($commentaires); $this->envoyerMessageAuxDestinataires(); } } catch (Exception $e) { $this->traiterErreur($e->getMessage()); } } private function verifierModeUtilisation() { $testeurCourriel = $this->conteneur->getParametre('t'); if ($testeurCourriel) { if (filter_var($testeurCourriel, FILTER_VALIDATE_EMAIL)) { $this->testeurCourriel = $testeurCourriel; } else { $msg = "Veuillez indiquer une adresse de courriel valide à la suite du paramètre «-t»."; new Exception($msg); } } } private function verifierDateRenvoi() { $dateRenvoi = $this->conteneur->getParametre('d'); if ($dateRenvoi) { if (preg_match('/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/', $dateRenvoi)) { $this->dateRenvoi = $dateRenvoi; } else { $msg = "Veuillez indiquer une date au format yyyy-mm-dd valide à la suite du paramètre «-d»."; new Exception($msg); } } } private function chargerObservationsCommentees() { // Seuls les utilisateurs ayant explicitement refusé le mail sont ignorés $preferences = $this->conteneur->getBdd()->proteger('%"mail_notification_mes_obs":"0"%'); $date = ($this->dateRenvoi == null) ? 'NOW()' : "'$this->dateRenvoi'"; //TODO: optimiser la requête si beaucoup de mails sont envoyés $requete = "SELECT * FROM del_observation AS do ". "WHERE do.id_observation IN ". "( ". //TODO: essayer de factoriser cette sous requete entre ici et la fonction du dessus "SELECT dc.ce_observation ". "FROM del_commentaire AS dc ". "WHERE dc.ce_observation != '0' ". "AND dc.id_commentaire NOT IN ". "( ". " SELECT dc.id_commentaire ". " FROM del_commentaire AS dc, del_observation AS dob". " WHERE dc.nom_sel = dob.nom_sel ". " AND dc.ce_utilisateur = dob.ce_utilisateur ". " AND dc.ce_observation = dob.id_observation ". ") ". "AND TO_DAYS($date) - TO_DAYS(dc.date) IN (0, 1) ". ") ". "AND do.ce_utilisateur NOT IN ". "( ". " SELECT id_utilisateur ". " FROM del_utilisateur_infos ". " WHERE preferences LIKE $preferences ". ") ". "ORDER BY do.ce_utilisateur, do.id_observation "; $observations = $this->conteneur->getBdd()->recupererTous($requete); return $observations; } // classe les observations concernées par id_utilisateur (ou courriel si non inscrit) // de l'auteur puis par id_observation private function formaterObservations($liste_obs) { foreach ($liste_obs as $obs) { $id_obs = $obs['id_observation']; $id_auteur_obs = $obs['ce_utilisateur']; if (($id_auteur_obs == 0) && ($obs['courriel_utilisateur'] != '')) { $id_auteur_obs = $obs['courriel_utilisateur']; } $infos = array(); $infos['id'] = $id_obs; $infos['nom_sci'] = $this->formaterNomSci($obs); $infos['date'] = $this->formaterDate($obs['date_observation']); $infos['lieu'] = $this->formaterLieu($obs); $infos['lien'] = $this->obtenirLienFiche($id_obs); $infos['commentaires'] = array(); $this->correspondance_id_obs_auteurs[$id_obs] = $id_auteur_obs; $this->observations_concernees[$id_auteur_obs][$id_obs] = $infos; } } private function formaterNomSci($obs) { $nom = 'Espèce indéterminée'; if ($obs['nom_ret'] != '') { $nom = $obs['nom_ret']; } else if($obs['nom_sel'] != '') { $nom = $obs['nom_sel']; } return $nom; } private function formaterDate($date) { $dateFmt = '(date inconnue)'; if ($date != '0000-00-00 00:00:00') { $time = strtotime($date); $dateFmt = date('d/m/Y', $time); } return $dateFmt; } private function formaterLieu($obs) { $lieuInfos = []; $champsLieu = ['zone_geo', 'lieudit', 'station']; foreach ($champsLieu as $champ) { if (trim($obs[$champ]) == '') { continue; } $lieuIntitule = $obs[$champ]; if ($champ == 'zone_geo') { $codeDept = $this->convertirCodeZoneGeoVersDepartement($obs['ce_zone_geo']); $lieuIntitule .= empty($codeDept) ? '' : " ($codeDept)"; } $lieuInfos[] = $lieuIntitule; } $lieu = (count($lieuInfos) > 0) ? implode(' > ', $lieuInfos) : '(lieu inconnu)'; return $lieu; } private function convertirCodeZoneGeoVersDepartement($code_zone_geo) { $code_departement = ''; if (preg_match('/^INSEE-C:([0-9]{2})[0-9]{3}$/', $code_zone_geo, $match)) { $code_departement = $match[1]; } return $code_departement; } private function obtenirLienFiche($id_obs) { return sprintf($this->conteneur->getParametre('url_fiche_observation'), $id_obs); } protected function chargerCommentairesDuJour() { $preferences = $this->conteneur->getBdd()->proteger('%"mail_notification_mes_obs":"0"%'); $date = ($this->dateRenvoi == null) ? 'NOW()' : "'$this->dateRenvoi'"; // TODO: optimiser la requête si beaucoup de mails sont envoyés $requete = "SELECT * ". "FROM del_commentaire AS dc ". "WHERE dc.ce_observation != '0' ". "AND dc.id_commentaire NOT IN ". "( ". " SELECT dc.id_commentaire ". " FROM del_commentaire AS dc, del_observation AS dob". " WHERE dc.nom_sel = dob.nom_sel ". " AND dc.ce_utilisateur = dob.ce_utilisateur ". " AND dc.ce_observation = dob.id_observation ". ") ". "AND dc.ce_utilisateur NOT IN ". "( ". " SELECT id_utilisateur ". " FROM del_utilisateur_infos ". " WHERE preferences LIKE $preferences ". ") ". "AND TO_DAYS($date) - TO_DAYS(dc.date) IN (0, 1) ". "ORDER BY dc.ce_observation"; $commentaires = $this->conteneur->getBdd()->recupererTous($requete); return $commentaires; } protected function formaterCommentaires($liste_commentaires) { foreach ($liste_commentaires as $commentaire) { $id_obs = $commentaire['ce_observation']; if (isset($this->correspondance_id_obs_auteurs[$id_obs])) { $id_auteur_obs = $this->correspondance_id_obs_auteurs[$id_obs]; $commentaire['auteur'] = $this->formaterAuteur($commentaire); $this->observations_concernees[$id_auteur_obs][$id_obs]['commentaires'][] = $commentaire; } } } private function formaterAuteur($commentaire) { return $commentaire['utilisateur_prenom'].' '.$commentaire['utilisateur_nom']; } protected function envoyerMessageAuxDestinataires() { foreach ($this->observations_concernees as $auteurId => $liste_obs) { $messageTxt = ''; $messageHtml = ''; $donnees = $this->formaterDonneesPourMessage($auteurId, $liste_obs); if (is_numeric($auteurId)) { // inscrits if ($auteurId != 0) { $messageTxt = $this->formaterMessageTxt($donnees, true); $messageHtml = $this->formaterMessageHtml($donnees, true); if ($this->testeurCourriel == null) { $this->envoyerMessage($messageHtml, $messageTxt, $auteurId); } } } else { // non-inscrits $messageTxt = $this->formaterMessageTxt($donnees, false); $messageHtml = $this->formaterMessageHtml($donnees, false); if ($this->testeurCourriel == null) { $this->envoyerMessageAdresseArbitraire($messageHtml, $messageTxt, $auteurId); } } $this->recapitulatifAdmin[$auteurId] = array('txt' => $messageTxt, 'html' => $messageHtml); } if ($this->testeurCourriel == null) { $this->envoyerMessageRecap(); } $this->envoyerMessagesTesteur(); } // Envoie un message sans passer par l'annuaire, pour les utilisateurs non inscrits private function envoyerMessageAdresseArbitraire($messageHtml, $messageTxt, $adresseAuteur) { $dateFmt = $this->formaterDateCourante(); $sujet = sprintf($this->conteneur->getParametre('titre_message_recapitulatif'), $dateFmt); $messagerie = $this->conteneur->getMessagerie(); // envoi mixte HTML + texte $envoieStatut = $messagerie->envoyerHtml($adresseAuteur, $sujet, $messageHtml, $messageTxt); return $envoieStatut; } private function formaterMessageHtml($donnees, $inscrit=true) { if ($inscrit) { $squelette = dirname(__FILE__).DS.'squelettes'.DS.'commentaires.tpl.html'; } else { $squelette = dirname(__FILE__).DS.'squelettes'.DS.'commentaires_non_inscrits.tpl.html'; } $squelettePhp = $this->conteneur->getSquelettePhp(); $msgHtml = $squelettePhp->analyser($squelette, $donnees); return $msgHtml; } private function formaterMessageTxt($donnees, $inscrit=true) { if ($inscrit) { $squelette = dirname(__FILE__).DS.'squelettes'.DS.'commentaires.tpl.txt'; } else { $squelette = dirname(__FILE__).DS.'squelettes'.DS.'commentaires_non_inscrits.tpl.txt'; } $squelettePhp = $this->conteneur->getSquelettePhp(); $msgTxt = $squelettePhp->analyser($squelette, $donnees); // Nettoyage des tabulations pour indentation du code PHP $msgTxt = str_replace("\t", '', $msgTxt); return $msgTxt; } private function formaterDonneesPourMessage($auteurId, $liste_obs) { $donnees = array(); $donnees['liste_observations'] = $liste_obs; if (is_numeric($auteurId)) { // inscrit $infosUtilisateur = $this->telechargerUtilisateurInfos($auteurId); $donnees['auteur_formate'] = $this->formaterIntituleUtilisateur($infosUtilisateur); } else { // non-inscrit $donnees['auteur_formate'] = $auteurId; } return $donnees; } private function formaterIntituleUtilisateur($infos) { $intituleUtilisateur = isset($infos->intitule) ? $infos->intitule : ''; return $intituleUtilisateur; } // envoie un message à un utilisateur inscrit, à l'aide de l'annuaire private function envoyerMessage($messageHtml, $messageTxt, $id_destinataire) { $url = sprintf($this->conteneur->getParametre('url_service_messagerie'), $id_destinataire); $dateFmt = $this->formaterDateCourante(); $sujet = sprintf($this->conteneur->getParametre('titre_message_recapitulatif'), $dateFmt); $donnees = array( 'utilisateur_courriel' => $this->conteneur->getParametre('adresse_expediteur_recapitulatif'), 'reponse_courriel' => $this->conteneur->getParametre('adresse_reponse'), // reply-to 'format' => 'html', 'sujet' => $sujet, 'message' => $messageHtml, 'message_txt' => $messageTxt ); $clientRest = $this->conteneur->getRestClient(); $clientRest->modifier($url, $donnees); } private function formaterDateCourante() { $date = ($this->dateRenvoi == null) ? time() : strtotime($this->dateRenvoi); $dateFmt = date('d-m-Y', $date); return $dateFmt; } private function envoyerMessageRecap() { $msgRecap = $this->obtenirMessageRecap(); $dateFmt = $this->formaterDateCourante(); $sujet = "IdentiPlante : commentaires du $dateFmt"; $messagerie = $this->conteneur->getMessagerie(); $destinataire = $this->conteneur->getParametre('email_recap'); $messagerie->envoyerTxt($destinataire, $sujet, $msgRecap); } private function obtenirMessageRecap() { if ($this->messageRecapitulatif == null) { $msgRecap = ''; $separateur = str_repeat('-', 50); $utilisateursIntitules = $this->obtenirInfosUtilisateurs(); foreach ($this->recapitulatifAdmin as $utilisateurId => $message) { $messageTxt = $message['txt']; $intitule = $utilisateursIntitules[$utilisateurId]; $msgRecap .= "Message envoyé à : $intitule\n\n$messageTxt\n$separateur\n"; } $intituleRecap = implode("\n", $utilisateursIntitules); $msgTpl = "Messages envoyés aux utilisateurs suivant :\n%s\n%s\n%s"; $this->messageRecapitulatif = sprintf($msgTpl, $intituleRecap, $separateur, $msgRecap); } return $this->messageRecapitulatif; } // envoie au testeur une copie de chaque type de message envoyé aux utilisateurs private function envoyerMessagesTesteur() { if ($this->testeurCourriel != null) { $messagerie = $this->conteneur->getMessagerie(); $dateFmt = $this->formaterDateCourante(); $sujet = "TESTEUR : commentaires du $dateFmt"; $msgRecap = $this->obtenirMessageRecap(); $messagerie->envoyerTxt($this->testeurCourriel, $sujet, $msgRecap); // deux types de messages (inscrits et non inscrits) $messageInscrits = null; $messageNonInscrits = null; // méga sous optimal foreach($this->recapitulatifAdmin as $idUtil => $mess) { if ($messageInscrits != null && $messageNonInscrits != null) { break; // arrière, Satan ! (mais c'est bien pratique) } if (is_numeric($idUtil) && ($idUtil != 0)) { $messageInscrits = $mess; } else { $messageNonInscrits = $mess; } } $sujet = "TESTEUR : HTML (inscrits) - commentaire du $dateFmt"; $messagerie->envoyerHtml($this->testeurCourriel, $sujet, $messageInscrits['html'], $messageInscrits['txt']); $sujet = "TESTEUR : TXT (inscrits) - commentaire du $dateFmt"; $messagerie->envoyerTxt($this->testeurCourriel, $sujet, $messageInscrits['txt']); $sujet = "TESTEUR : HTML (non inscrits) - commentaire du $dateFmt"; $messagerie->envoyerHtml($this->testeurCourriel, $sujet, $messageNonInscrits['html'], $messageNonInscrits['txt']); $sujet = "TESTEUR : TXT (non inscrits) - commentaire du $dateFmt"; $messagerie->envoyerTxt($this->testeurCourriel, $sujet, $messageNonInscrits['txt']); } } private function obtenirInfosUtilisateurs() { $idUtilisateurs = array_keys($this->recapitulatifAdmin); $utilisateursIntitules = []; foreach ($idUtilisateurs as $utilisateurId) { if (is_numeric($utilisateurId)) { // inscrit $infosUtilisateur = $this->telechargerUtilisateurInfos($utilisateurId); $intitule = $this->formaterUtilisateurInfos($infosUtilisateur); } else { // non-inscrit $intitule = $utilisateurId . " - utilisateur non inscrit"; } $utilisateursIntitules[$utilisateurId] = $intitule; } asort($utilisateursIntitules); return $utilisateursIntitules; } private function telechargerUtilisateurInfos($utilisateurId) { if (! isset($this->utilisateursInfos[$utilisateurId])) { $urlTpl = $this->conteneur->getParametre('url_service_utilisateur'); $url = sprintf($urlTpl, $utilisateurId); $clientRest = $this->conteneur->getRestClient(); $json = $clientRest->consulter($url); $infos = json_decode($json); $this->utilisateursInfos[$utilisateurId] = isset($infos->id) ? $infos : $utilisateurId; } return $this->utilisateursInfos[$utilisateurId]; } private function formaterUtilisateurInfos($infos) { $utilisateurId = isset($infos->id) ? $infos->id : intval($infos); if (isset($infos->courriel) && isset($infos->intitule)) { $prenomNom = $infos->nom.' '.$infos->prenom; $pseudo = empty($infos->pseudo) ? '' : '['.$infos->pseudo.'] '; $courriel = $infos->courriel; $intitule = "$prenomNom $pseudo($courriel) - id#$utilisateurId"; } else { $intitule = "Utilisateur avec id $utilisateurId introuvable"; } return $intitule; } }