Subversion Repositories eFlore/Applications.del

Compare Revisions

No changes between revisions

Ignore whitespace Rev 2162 → Rev 2171

/branches/v1.11-magnesium/scripts/modules/stats/Stats.php
New file
0,0 → 1,41
<?php
// declare(encoding='UTF-8');
/**
* Script gérant les statistiques de DEL.
*
* @category DEL
* @package Scripts
* @subpackage Stats
* @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 Stats extends DelScript {
 
public function executer() {
$this->mettreAJourStats();
}
 
/**
* Met à jour la table des statistiques en la vidant et en recalculant la moyenne pondérée,
* le nombre de votes et le nombre de points pour chaque image - utilise "commun".
* Attention, c'est over-pas-optimisé, ça prend du temps !
*/
private function mettreAJourStats() {
$requete = "SELECT id_image FROM del_image ORDER BY id_image ASC";
$ids = $this->getBdd()->recupererTous($requete);
 
$requete = "SELECT id_protocole FROM del_image_protocole ORDER BY id_protocole ASC";
$protos = $this->getBdd()->recupererTous($requete);
 
foreach ($ids as $id) {
echo "ID: " . $id['id_image'] . "\n";
foreach ($protos as $proto) {
TelaBotanica\Del\Commun\Stats::updateStats($this->getBdd(), $id['id_image'], $proto['id_protocole']);
}
}
}
}
/branches/v1.11-magnesium/scripts/modules/adeterminer/del_mail_recapitulatif_obs.sh
New file
0,0 → 1,23
#!/bin/bash
#
# /etc/init.d/del_mail_recapitulatif : traitement des mails récapitulatifs d'identiplante
# - 2014-03-31 [Jean-Pascal Milcent] : ajout gestion de l'heure d'été et du décalage du temps de lancement
# - 2014-03-20 [Jean-Pascal Milcent] : ajout gestion de l'heure d'execution
# - 2012-07-03 [Aurélien Peronnet] : création
#
while true
do
HEURE=$(date "+%H")
# Si nous sommes à 18h nous lançon le script
if [ $HEURE -eq 18 ] ; then
logger "del::alerteMailADeterminer : lancement script"
sudo -u apitela /usr/local/bin/php-cli-55 -f /home/apitela/www/scripts/del/cli.php adeterminer -a alerteMail
logger "del::alerteMailaDeterminer : arret scrip"
# Sleep 22h et non 1d : pour éviter pb changement heure d'été et le décalage de quelques secondes ajouté à chaque lancement
sleep 22h
else
# Tentative toutes les heures jusqu'à la bonne heure
logger "del::alerteMailADeterminer : tentative de lancement à $HEURE"
sleep 1h
fi
done
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:executable
+*
\ No newline at end of property
/branches/v1.11-magnesium/scripts/modules/adeterminer/squelettes/aDeterminer.tpl.txt
New file
0,0 → 1,18
IdentiPlante : Récapitulatif des observations à déterminer
 
Ce message récapitule l'ensemble des observations dont l'auteur a demandé confirmation ou détermination via
l'outil IdentiPlante <http://www.tela-botanica.org/appli:identiplante>.
 
<?php foreach ($liste_observations as $observation) : ?>
--> Observation <?= $observation['id'] ?> - <<?= $observation['lien']; ?>>
<?= $observation['nom_propose']; ?> observée le <?= $observation['date']; ?> à <?= $observation['lieu']; ?>, par <?= $observation['auteur']; ?>
 
 
<?php endforeach ; ?>
 
Bonne continuation sur nos outils botaniques !
--
L'équipe de Tela Botanica.
 
Ce message vous est envoyé par l'intermédiaire de l'application IdentiPlante du réseau Tela Botanica <http://www.tela-botanica.org/appli:identiplante>.
Si vous ne souhaitez plus recevoir ce mail décochez la case dans vos préférences sur l'interface d'IdentiPlante.
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/v1.11-magnesium/scripts/modules/adeterminer/squelettes/aDeterminer.tpl.html
New file
0,0 → 1,81
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
<!--
body {
font-family:Arial,Helvetica,Verdana,sans-serif;
font-size:14px;
}
h1 > a {
color:#fd8c13;
}
.emphase {
color: #777777;
}
.observation {
font-size: 13px;
color: #454341 !important;
margin: 0;
}
.observation strong {
font-weight: bold;
}
.observation > a {
color:#454341;
border-bottom: dotted 1px;
border-color: #CCCCCC;
}
a {
cursor:pointer;
color:#598000;
text-decoration:none;
border-bottom:1px dotted #95ae5d;
}
a:hover {
color:#FD8C13;
border-bottom:1px dotted #95ae5d;
}
.description_message {
color: #333333;
font-size: 1.1em;
font-style: italic;
padding-top: 10px;
}
-->
</style>
</head>
<body>
<div>
<h1><span class="emphase">IdentiPlante</span> : R&eacute;capitulatif des observations &agrave; d&eacute;terminer </h1>
</div>
<div class="description_message">
Ce message r&eacute;capitule l'ensemble des observations dont l'auteur a demand&eacute; confirmation ou d&eacute;termination via
l'outil <a href="http://www.tela-botanica.org/appli:identiplante" >Identiplante</a>
<br /><br />
</div>
 
<?php foreach ($liste_observations as $observation) : ?>
<div class="observation">
<a href="<?= $observation['lien']; ?>">Observation <?= $observation['id'] ?> - <em><strong><?= $observation['nom_propose']; ?></strong></em> </a>
observ&eacute;e le <strong><?= $observation['date']; ?></strong> &agrave; <strong><?= $observation['lieu']; ?></strong>, par <?= $observation['auteur']; ?>
</div>
<br />
<?php endforeach ; ?>
 
Bonne continuation sur nos outils botaniques !<br />
L'&eacute;quipe de Tela Botanica<br />
<div>---------</div>
<br/>
<small>
Ce message vous est envoy&eacute; par l'interm&eacute;diaire de l'application IdentiPlante du r&eacute;seau Tela Botanica.
<a href="http://www.tela-botanica.org/appli:identiplante">http://www.tela-botanica.org/appli:identiplante </a>
</small>
<br />
<small>
Si vous ne souhaitez plus recevoir ce mail d&eacute;cochez la case dans vos pr&eacute;f&eacute;rences
sur l'interface d'IdentiPlante.
</small>
</body>
</html>
/branches/v1.11-magnesium/scripts/modules/adeterminer/Adeterminer.php
New file
0,0 → 1,49
<?php
//declare(encoding='UTF-8');
/**
* Script récupèrant tous les commentaires et déterminations fait ce jour et envoie un mail
* aux auteurs des observations récapitulant celle-ci.
*
* Utilisation : <code>/opt/lampp/bin/php cli.php adeterminer -a alerteMail</code>
*
* @category DEL
* @package Scripts
* @subpackage ADeterminer
* @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 Adeterminer extends DelScript {
 
protected $parametres_autorises = array(
'-t' => array(false, true, 'Indiquez votre e-mail pour tester le script.'),
'-d' => array(false, true, "Indiquer une date au format yyyy-mm-dd pour renvoyer l'alerte de cette date."));
 
public function executer() {
try {
$this->initialiserProjet('adeterminer');
 
// Lancement de l'action demandée
$cmd = $this->getParametre('a');
switch ($cmd) {
case 'alerteMail' :
$script = $this->chargerClasse('AlerteMailADeterminer');
$script->executer();
break;
default :
throw new Exception("Erreur : la commande '$cmd' n'existe pas!");
}
} catch (Exception $e) {
$this->traiterErreur($e->getMessage());
}
}
 
private function chargerClasse($classe) {
require_once $classe.'.php';
$conteneur = new Conteneur($this->parametres);
return new $classe($conteneur);
}
}
/branches/v1.11-magnesium/scripts/modules/adeterminer/AlerteMailADeterminer.php
New file
0,0 → 1,349
<?php
// declare(encoding='UTF-8');
/**
* Récupère tous les commentaires et déterminations fait ce jour et envoie un mail
* aux auteurs des observations récapitulant celle-ci
*
* @category DEL
* @package Scripts
* @subpackage ADeterminer
* @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 AlerteMailADeterminer {
 
private $conteneur;
private $testeurCourriel = null;
private $recapitulatifAdmin = [];
private $messageRecapitulatif = null;
 
private $dateRenvoi = null;
 
private $observations_concernees = array();
private $messageHtml = null;
private $messageTxt = null;
private $utilisateursInfos = array();
 
public function __construct($conteneur) {
$this->conteneur = $conteneur;
}
 
public function executer() {
try {
$this->verifierModeUtilisation();
$this->verifierDateRenvoi();
$observations = $this->chargerObsADeterminer();
if (!empty($observations)) {
$this->formaterObservations($observations);
$this->genererMessage();
$this->envoyerMessageAuxDestinataires();
}
} catch (Exception $e) {
$this->traiterErreur($e->getMessage());
}
}
 
protected function traiterErreur($msg) {
echo "ERREUR: $msg\n";
}
 
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);
}
}
}
 
protected function chargerObsADeterminer() {
//TODO: optimiser la requête si beaucoup de mails sont envoyés
$date = ($this->dateRenvoi == null) ? 'CURDATE()' : "'$this->dateRenvoi'";
$requete = 'SELECT id_observation, do.courriel_utilisateur, '.
' date_observation, nom_ret, nom_sel, '.
' ce_zone_geo, zone_geo, lieudit, station '.
'FROM del_observation AS do '.
'WHERE do.ce_utilisateur != 0 '.
' AND do.ce_utilisateur IS NOT NULL '.
" AND (do.mots_cles_texte LIKE '%aDeterminer%' ".
" OR (do.certitude = 'aDeterminer' OR do.certitude = 'A déterminer' OR do.certitude = 'douteux') ".
" OR nom_sel IS NULL OR nom_sel = '' ".
" OR nom_ret IS NULL OR nom_ret = '') ".
' AND ('.
" TO_DAYS($date) - TO_DAYS(do.date_transmission) IN (0, 1) ".
" OR TO_DAYS($date) - TO_DAYS(do.date_creation) IN (0, 1) ".
" OR TO_DAYS($date) - TO_DAYS(do.date_modification) IN (0, 1) ".
' ) '.
"ORDER BY do.id_observation ";
$obs = $this->conteneur->getBdd()->recupererTous($requete);
return $obs;
}
 
private function formaterObservations($liste_obs) {
// lister les courriels
$courriels = array();
foreach ($liste_obs as $obs) {
$courriels[] = $obs['courriel_utilisateur'];
}
$courriels = array_unique($courriels);
$courriels = array_filter($courriels);
 
// récupérer les pseudos
$identites = TelaBotanica\Del\Commun\Utilisateurs::identiteUtilisateurs($courriels, $this->conteneur->getParametre('url_service_utilisateur'));
 
// emboîter tout
foreach ($liste_obs as $obs) {
// identité de l'utilisateur : pseudo, nom-prénom ou courriel tronqué
$identiteUtil = $obs['courriel_utilisateur']; // sera tronqué si rien de mieux n'a été trouvé
if (isset($identites[$obs['courriel_utilisateur']])) {
$identiteUtil = $identites[$obs['courriel_utilisateur']];
}
$identiteUtil = TelaBotanica\Del\Commun\Utilisateurs::identiteAAfficher($identiteUtil);
 
$id_obs = $obs['id_observation'];
$infos = array();
$infos['id'] = $id_obs;
$infos['date'] = $this->formaterDateObs($obs['date_observation']);
$infos['nom_propose'] = $this->formaterNomSci($obs);
$infos['lien'] = $this->obtenirLienFicheObs($id_obs);
$infos['lieu'] = $this->formaterLieu($obs);
$infos['auteur'] = $identiteUtil;
$this->observations_concernees[$id_obs] = $infos;
}
}
 
private function formaterDateObs($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 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 obtenirLienFicheObs($id_obs) {
return sprintf($this->conteneur->getParametre('url_fiche_observation'), $id_obs);
}
 
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 genererMessage() {
if ($this->messageHtml == null && $this->messageTxt == null) {
$donnees = array();
$donnees['liste_observations'] = $this->observations_concernees;
 
$squelettePhp = $this->conteneur->getSquelettePhp();
$messageHtmlTpl = dirname(__FILE__).DS.'squelettes'.DS.'aDeterminer.tpl.html';
$this->messageHtml = $squelettePhp->analyser($messageHtmlTpl, $donnees);
$messageTxtTpl = dirname(__FILE__).DS.'squelettes'.DS.'aDeterminer.tpl.txt';
$this->messageTxt = $squelettePhp->analyser($messageTxtTpl, $donnees);
}
}
 
private function envoyerMessageAuxDestinataires() {
if ($this->testeurCourriel == null) {
$this->envoyerMessageAuxUtilisateurs();
$this->envoyerMessageListeDetermination();
$this->envoyerMessageRecap();
} else {
$this->envoyerMessageTesteur();
}
}
 
private function envoyerMessageAuxUtilisateurs() {
$utilisateurs = $this->obtenirDestinatairesMailsRecapitulatif();
foreach ($utilisateurs as $idUtilisateur) {
$this->envoyerMessage($idUtilisateur);
}
}
 
private function obtenirDestinatairesMailsRecapitulatif() {
if (count($this->recapitulatifAdmin) == 0) {
$preference = $this->conteneur->getBdd()->proteger('%"mail_notification_toutes_obs":"1"%');
$requete = "SELECT id_utilisateur ".
"FROM del_utilisateur_infos ".
"WHERE preferences LIKE $preference ";
$utilisateurs = $this->conteneur->getBdd()->recupererTous($requete);
 
foreach ($utilisateurs as $utilisateur) {
$this->recapitulatifAdmin[] = $utilisateur['id_utilisateur'];
}
}
return $this->recapitulatifAdmin;
}
 
/**
* Appelle le web service message de l'annuaire afin d'envoyer un mail à l'utilisateur dont l'identifiant est donné
* en paramètre
* Enter description here ...
* @param string $message
* @param int $id_destinataire
*/
private function envoyerMessage($id_destinataire) {
$url = sprintf($this->conteneur->getParametre('url_service_messagerie'), $id_destinataire);
$dateFmt = $this->formaterDateCourante();
$sujet = sprintf($this->conteneur->getParametre('titre_message_recapitulatif_obs'), $dateFmt);
 
$donnees = array(
'utilisateur_courriel' => $this->conteneur->getParametre('adresse_expediteur_recapitulatif'),
'format' => 'html',
'sujet' => $sujet,
'message' => $this->messageHtml,
'message_txt' => $this->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;
}
 
/** La liste détermination n'étant pas un membre de tela botanica,
* on lui envoie un message directement
* @return boolean true ou false suivant le succès ou non de l'envoi
*/
private function envoyerMessageListeDetermination() {
$destinataires = $this->conteneur->getParametre('adresse_destinataire_recapitulatif_determination');
$dateFmt = $this->formaterDateCourante();
$sujet = sprintf($this->conteneur->getParametre('titre_message_recapitulatif_obs'), $dateFmt);
 
$messagerie = $this->conteneur->getMessagerie();
$envoieStatut = $messagerie->envoyerHtml($destinataires, $sujet, $this->messageHtml, $this->messageTxt);
return $envoieStatut;
}
 
private function envoyerMessageRecap() {
$msgRecap = $this->obtenirMessageRecap();
$dateFmt = $this->formaterDateCourante();
$sujet = "IdentiPlante : obs du $dateFmt";
$messagerie = $this->conteneur->getMessagerie();
$destinataire = $this->conteneur->getParametre('email_recap');
$messagerie->envoyerTxt($destinataire, $sujet, $msgRecap);
}
 
private function obtenirMessageRecap() {
if ($this->messageRecapitulatif == null) {
$utilisateursIntitules = $this->obtenirInfosUtilisateurs();
$intituleRecap = implode("\n", $utilisateursIntitules);
$separateur = str_repeat('-', 50);
$msgTpl = "Messages envoyés aux utilisateurs suivant :\n%s\n%s\n%s";
$this->messageRecapitulatif = sprintf($msgTpl, $intituleRecap, $separateur, $this->messageTxt);
}
return $this->messageRecapitulatif;
}
 
private function obtenirInfosUtilisateurs() {
$utilisateursIntitules = [];
$utilisateurs = $this->obtenirDestinatairesMailsRecapitulatif();
foreach ($utilisateurs as $utilisateurId) {
$infosUtilisateur = $this->telechargerUtilisateurInfos($utilisateurId);
$intitule = $this->formaterUtilisateurInfos($infosUtilisateur);
$utilisateursIntitules[$utilisateurId] = $intitule;
}
asort($utilisateursIntitules);
return $utilisateursIntitules;
}
 
protected 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, true);
if (is_array($infos)) {
$infos = array_shift($infos);
}
$this->utilisateursInfos[$utilisateurId] = isset($infos['id']) ? $infos : $utilisateurId;
}
return $this->utilisateursInfos[$utilisateurId];
}
 
protected 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;
}
 
private function envoyerMessageTesteur() {
if ($this->testeurCourriel != null) {
$messagerie = $this->conteneur->getMessagerie();
$dateFmt = $this->formaterDateCourante();
 
$sujet = "TESTEUR : obs du $dateFmt";
$msgRecap = $this->obtenirMessageRecap();
$messagerie->envoyerTxt($this->testeurCourriel, $sujet, $msgRecap);
 
$sujet = "TESTEUR : HTML - obs du $dateFmt";
$messagerie->envoyerHtml($this->testeurCourriel, $sujet, $this->messageHtml, $this->messageTxt);
 
$sujet = "TESTEUR : TXT - obs du $dateFmt";
$messagerie->envoyerTxt($this->testeurCourriel, $sujet, $this->messageTxt);
}
}
}
/branches/v1.11-magnesium/scripts/modules/maintenance/Maintenance.php
New file
0,0 → 1,533
<?php
// declare(encoding='UTF-8');
/**
* Script gérant la maintenance de DEL.
*
* @category DEL
* @package Scripts
* @subpackage Maintenance
* @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 Maintenance extends Script {
 
protected $tables_referentiel = array();
protected $conteneur = null;
public function executer() {
$cmd = $this->getParametre('a');
$this->conteneur = new Conteneur($this->parametres);
 
switch ($cmd) {
case 'corrigerNomsReferentiels' :
$this->corrigerNomsReferentiels();
break;
case 'supprimerPropositionsOrphelines' :
$this->supprimerPropositionsOrphelines();
break;
case 'affecterNnsAuxPropositionsViables' :
$this->affecterNnsAuxPropositionsViables();
break;
case 'dedoublonnerPropositions' :
$this->dedoublonnerPropositions();
break;
case 'dedoublonnerPropositionsEtCorrigerDonnees' :
// Suppression des propositions et votes sur des obs inexistantes
// pas la peine de traiter ce qui n'existe plus !
$this->supprimerPropositionsOrphelines();
// Corriger les noms de référentiels permet de simplifier les affectations de noms ensuite
$this->corrigerNomsReferentiels();
// Affectations de nums noms aux propositions qui le permettent
$this->affecterNnsAuxPropositionsViables();
echo "\n";
$this->dedoublonnerPropositions();
// A la fin, les votes qui n'ont pas été fusionnés pointent sur des propositions
// qui n'existent plus, donc on les supprime
$this->supprimerVotesOrphelins();
echo "\n";
break;
default :
throw new Exception("Erreur : la commande '$cmd' n'existe pas!");
}
}
private function corrigerNomsReferentiels() {
echo "Corrections des noms de referentiels ... ";
$requete = "UPDATE del_commentaire ".
"SET nom_referentiel = 'bdtfx' ".
"WHERE nom_referentiel IN ('bdtfx_v1', 'bdtfx:1.01') ";
$modif = $this->conteneur->getBdd()->executer($requete);
$requete = "UPDATE del_commentaire ".
"SET nom_referentiel = 'apd' ".
"WHERE nom_referentiel IN ('bdtao', 'bdtao:1.00') ";
$modif = $this->conteneur->getBdd()->executer($requete);
$requete = "UPDATE del_commentaire ".
"SET nom_referentiel = 'bdtxa' ".
"WHERE nom_referentiel IN ('bdtxa:1.00') ";
$modif = $this->conteneur->getBdd()->executer($requete);
$requete = "UPDATE del_commentaire ".
"SET nom_referentiel = 'isfan' ".
"WHERE nom_referentiel IN ('isfan:1.00') ";
$modif = $this->conteneur->getBdd()->executer($requete);
echo "OK \n";
return $modif;
}
private function supprimerPropositionsOrphelines() {
echo "Suppression des propositions orphelines (et des votes associés) ... ";
$requete_prop = "DELETE FROM del_commentaire ".
"WHERE ce_observation NOT IN (SELECT id_observation FROM del_observation)";
$modif_prop = $this->conteneur->getBdd()->executer($requete_prop);
 
$modif_votes = $this->supprimerVotesOrphelins();
echo "OK \n";
return $modif_prop && $modif_votes;
}
private function supprimerVotesOrphelins() {
$requete_votes = "DELETE FROM del_commentaire_vote ".
"WHERE ce_proposition NOT IN (SELECT id_commentaire FROM del_commentaire)";
$modif_votes = $this->conteneur->getBdd()->executer($requete_votes);
}
private function dedoublonnerPropositions() {
echo "Détection des propositions en doublons \n";
// La méthode n'est pas optimisée, on fait beaucoup de boucles, de traitement de données
// etc... mais la méthode ne doit pas être lancée souvent et elle est rapide
// donc lisibilité > performances
$infos_indexees = array();
$infos_indexees_par_obs = array();
// Selection des ids de commentaires dupliqués, groupés par observation et par nom (concaténés par des virgules)
$requete_id_prod_doubl = "SELECT GROUP_CONCAT(id_commentaire) as id_commentaires_dupliques ".
"FROM del_commentaire ".
"GROUP BY ce_observation, nom_sel ".
"HAVING COUNT(nom_sel) > 1 ";
$ids_propositions_doublonnees = $this->conteneur->getBdd()->recupererTous($requete_id_prod_doubl);
 
// Assemblage en une seule chaine des id de commentaires
$ids_propositions_doublonnees_chaine = "";
foreach($ids_propositions_doublonnees as $ids_p_d) {
$ids_propositions_doublonnees_chaine .= $ids_p_d['id_commentaires_dupliques'].",";
}
$ids_propositions_doublonnees_chaine = rtrim($ids_propositions_doublonnees_chaine, ',');
$propositions_doublonnees = array();
if(!empty($ids_propositions_doublonnees)) {
// Selection des informations relatives aux ids de commentaires trouvés au dessus
$requete_prop_doubl = "SELECT * FROM del_commentaire ".
"WHERE id_commentaire IN (".$ids_propositions_doublonnees_chaine.") ".
"ORDER BY ce_observation, date ";
$propositions_doublonnees = $this->conteneur->getBdd()->recupererTous($requete_prop_doubl);
}
echo count($propositions_doublonnees)." propositions pour ".count($ids_propositions_doublonnees)." couples 'observation-nom_sel' sont potentiellement en doublons \n";
// Organisation par numéro d'obs, puis par nom sel, puis par id proposition
// + Création d'un tableau indexé par id proposition pour accès direct au infos
foreach($propositions_doublonnees as &$proposition_d) {
$id_obs = $proposition_d['ce_observation'];
$id_proposition = $proposition_d['id_commentaire'];
$nom_sel_prop = $proposition_d['nom_sel'];
$infos_indexees[$id_proposition] = $proposition_d;
if(!isset($infos_indexees_par_obs[$id_obs])) {
$infos_indexees_par_obs[$id_obs] = array();
}
$proposition_d['votes'] = array();
$infos_indexees_par_obs[$id_obs][$nom_sel_prop][$id_proposition] = $proposition_d;
}
 
$votes_doublonnes = array();
if(!empty($ids_propositions_doublonnees)) {
// Selection des votes concernés par ces propositions
$requete_votes_prop_doubl = "SELECT * FROM del_commentaire_vote ".
"WHERE ce_proposition IN (".$ids_propositions_doublonnees_chaine.")";
$votes_doublonnes = $this->conteneur->getBdd()->recupererTous($requete_votes_prop_doubl);
}
echo count($votes_doublonnes)." votes sont concernés \n";
// Affectation des votes trouvés aux propositions concernées
foreach($votes_doublonnes as &$vote_d) {
$id_proposition = $vote_d['ce_proposition'];
$proposition_concernee = $infos_indexees[$id_proposition];
$id_obs = $proposition_concernee['ce_observation'];
$nom_sel_prop = $proposition_concernee['nom_sel'];
$infos_indexees_par_obs[$id_obs][$nom_sel_prop][$id_proposition]['votes'][] = $vote_d;
}
$nb_propositions_conservees = 0;
$nb_propositions_supprimees = 0;
$nb_votes_reaffectes = 0;
// Tri des propositions pour savoir lequelles conserver et lesquelles supprimer
foreach($infos_indexees_par_obs as $obs => &$propositions) {
foreach($propositions as $nom => &$doublons) {
$this->trierPropositionsEtFusionnerVotes($doublons);
$nb_propositions_conservees += count($doublons['propositions_conservees']);
$nb_propositions_supprimees += count($doublons['propositions_supprimees']);
$nb_votes_reaffectes += count($doublons['votes_propositions_doublons']);
}
}
echo $nb_propositions_conservees." propositions seront conservées \n";
echo $nb_propositions_supprimees." propositions seront supprimées car inutiles ou fusionnées \n";
echo $nb_votes_reaffectes." groupes de votes seront potentiellement réaffectés \n";
// Mise à jour proprement dite
foreach($infos_indexees_par_obs as $obs => &$nom_en_doublons) {
foreach($nom_en_doublons as $nom_en_doublon => &$propositions_en_doublons) {
$this->mettreAJourPropositions($propositions_en_doublons);
}
$this->afficherAvancement('observations traitées ', 1);
}
// Fichier json pour vérification par zieutage (chemin à modifier suivant l'emplacement désiré)
//file_put_contents('/home/aurelien/web/test_fusion.json', json_encode($infos_indexees_par_obs));
}
private function trierPropositionsEtFusionnerVotes(&$prop_a_doublons) {
$proposition_conservees = array();
$propositions_supprimees = array();
$votes_conserves = array();
foreach($prop_a_doublons as &$prop) {
// Indexer le tableau par nom_sel courriel permet de ne garder qu'un seul couple
// utilisateur / nom sci pour chaque observation
// (on utilise le courriel car les gens non identifiés peuvent proposer également
// et celui-ci est normalement toujours rempli)
$index = strtolower($prop['nom_sel']).'-'.$prop['utilisateur_courriel'];
// Fusion des votes s'il y en a
if(!empty($prop['votes'])) {
// Utiliser le couple utilisateur / nom sci pour stocker les votes permet facilement
// de réaffecter les votes à la proposition qui sera conservée
if(!isset($votes_conserves[$index])) {
$votes_conserves[$index] = array();
}
// Si on trouve un vote pour et un vote contre par le même utilisateur
// (cas d'une proposition doublonnée votée de manière contradictoire par
// un utilisateur), on garde le vote positif
foreach($prop['votes'] as $vote_proposition) {
// Un utilisateur ne peut avoir qu'un seul vote par proposition
$index_vote = $vote_proposition['ce_utilisateur'];
if(isset($votes_conserves[$index][$index_vote])) { // fusion
$vote_deja_present = $votes_conserves[$index][$index_vote];
// Mise à jour de la valeur dans le cas où le vote existe déjà
$valeur_vote_maj = max($votes_conserves[$index][$index_vote]['valeur'], $vote_proposition['valeur']);
$votes_conserves[$index][$index_vote]['valeur'] = $valeur_vote_maj;
$votes_conserves[$index][$index_vote]['raison'] .= ' - '.$vote_proposition['valeur'];
} else { // nouveau vote
$vote_proposition['raison'] = 'Fusion de '.$vote_proposition['valeur'];
$votes_conserves[$index][$index_vote] = $vote_proposition;
}
}
}
 
// valeurs par défaut
$conservee = false;
$raison = 'Supprimée car ne rentre dans aucun cas';
// Les cas ci dessous permettent de fusionner le maximum d'information
// dans la proposition qui sera conservée (car il existe des doublons qui ne sont
// pas tout à fait identiques (ex.: l'un à un num_nom et l'autre pas)
// Les propositions sont classées par ordre de saisie donc on gardera comme base de fusion,
// la plus ancienne qui correspond à au moins un cas de conservation
$this->fusionnerPropositions($index, $prop, $proposition_conservees, $propositions_supprimees);
}
// Si aucune proposition ne convient on prend la première
// qui est normalement la plus ancienne
// @TODO normalement ça sert plus à rien
if (empty($proposition_conservees)) {
$prop_conservee = reset($prop_a_doublons);
$index = strtolower($prop_conservee['nom_sel']).'-'.$prop_conservee['utilisateur_courriel'];
$proposition_conservees[$index] = $prop_conservee;
$proposition_conservees[$index]['raison'] = "Conservée car aucune autre ne convient \n";
// Dans ce cas on retire la proposition de la liste des propositions supprimées
unset($propositions_supprimees[$prop_conservee['id_commentaire']]);
}
$prop_a_doublons['propositions_conservees'] = $proposition_conservees;
$prop_a_doublons['propositions_supprimees'] = $propositions_supprimees;
$prop_a_doublons['votes_propositions_doublons'] = $votes_conserves;
}
 
/**
* Tente de fusionner toutes les propositions, pour une observation donnée,
* en se basant sur le couple auteur-nom_selectionne
*/
protected function fusionnerPropositions($index, &$prop, &$proposition_conservees, &$propositions_supprimees) {
// On garde un exemple de chaque couple auteur/nom scientifique
// Ca laisse des doublons en terme de nom mais au moins ça ne lèse pas les auteurs
if(!isset($proposition_conservees[$index])) {
// première rencontre avec le couple nom_sel/auteur
$proposition_conservees[$index] = $prop;
} else { // Cas de la fusion
// Fusion d'un nom valide : fusionner nom _sel_nn (et nom_ret_nn et nom_ret s'ils existent)
if(!empty($prop['nom_sel_nn'])) {
$proposition_conservees[$index]['nom_sel_nn'] = $prop['nom_sel_nn'];
// infos complémentaires concernant le référentiel
if(!empty($prop['nom_referentiel'])) {
$proposition_conservees[$index]['nom_referentiel'] = $prop['nom_referentiel'];
}
if(!empty($prop['famille'])) {
$proposition_conservees[$index]['famille'] = $prop['famille'];
}
// Fusion du nom retenu s'il existe
if($proposition_conservees[$index]['nom_ret_nn'] == 0) { // on admet que nom_ret existe aussi dans ce cas
$proposition_conservees[$index]['nom_ret'] = $prop['nom_ret'];
$proposition_conservees[$index]['nom_ret_nn'] = $prop['nom_ret_nn'];
}
}
// Fusion du caractère retenu : fusioner le booleen, la date de retention, le validateur
if($prop['proposition_retenue'] == "1") {
$proposition_conservees[$index]['proposition_retenue'] = $prop['proposition_retenue'];
if (empty($proposition_conservees[$index]['date_validation'])) {
$proposition_conservees[$index]['date_validation'] = $prop['date_validation'];
}
if (empty($proposition_conservees[$index]['ce_validateur'])) {
$proposition_conservees[$index]['ce_validateur'] = $prop['ce_validateur'];
}
}
// Fusion du caractère initial : fusion du booleen, fusion des infos d'auteur et de la date la plus ancienne
if($prop['proposition_initiale'] == "1") {
$proposition_conservees[$index]['proposition_initiale'] = $prop['proposition_initiale'];
if (empty($proposition_conservees[$index]['ce_utilisateur'])) {
$proposition_conservees[$index]['ce_utilisateur'] = $prop['ce_utilisateur'];
}
if (empty($proposition_conservees[$index]['utilisateur_prenom'])) {
$proposition_conservees[$index]['utilisateur_prenom'] = $prop['utilisateur_prenom'];
}
if (empty($proposition_conservees[$index]['utilisateur_nom'])) {
$proposition_conservees[$index]['utilisateur_nom'] = $prop['utilisateur_nom'];
}
if (empty($proposition_conservees[$index]['utilisateur_courriel'])) {
$proposition_conservees[$index]['utilisateur_courriel'] = $prop['utilisateur_courriel'];
}
// Test tout simple pour éviter les dates vides
if (empty($proposition_conservees[$index]['date'])) {
if(substr($prop['date'], 0, 4) != "0000") {
$proposition_conservees[$index]['date'] = $prop['date'];
}
}
}
 
// Fusion du commentaire (concaténation du texte)
if(trim($proposition_conservees[$index]['texte']) != "") {
$prop['texte'] = trim($prop['texte']);
// Suppression du texte fusionné, au cas où il existerait déjà (un peu barbare mais bon)
$proposition_conservees[$index]['texte'] = str_replace($prop['texte'].';', '', $proposition_conservees[$index]['texte']);
$proposition_conservees[$index]['texte'] .= $prop['texte'].';';
}
// À la fin, une proposition fusionnée doit être supprimée
$propositions_supprimees[$prop['id_commentaire']] = $prop;
}
}
 
private function mettreAJourPropositions(&$propositions_a_obs_a_nom) {
$propositions_conservees = $propositions_a_obs_a_nom['propositions_conservees'];
foreach($propositions_conservees as $index => $proposition_conservee) {
$id_proposition_conservee = $proposition_conservee['id_commentaire'];
// Pas besoin de mettre à jour une clé primaire !
unset($proposition_conservee['id_commentaire']);
// Suppression des champs qui n'existent pas dans la base de données
unset($proposition_conservee['votes']);
unset($proposition_conservee['raison']);
$maj_prop = array();
// on prend les champs un par un
foreach($proposition_conservee as $champ => $valeur) {
$maj_prop[] = $champ.'='.$this->conteneur->getBdd()->proteger($valeur);
}
$requete_maj_prop = "UPDATE del_commentaire SET ".implode(',', $maj_prop)." ".
"WHERE id_commentaire = ".$id_proposition_conservee;
// écriture de sinformations fusionnées dans la base
$modif_prop = $this->conteneur->getBdd()->requeter($requete_maj_prop);
if(isset($propositions_a_obs_a_nom['votes_propositions_doublons'][$index])) {
$votes_a_proposition = $propositions_a_obs_a_nom['votes_propositions_doublons'][$index];
foreach($votes_a_proposition as $vote_a_proposition) {
$id_vote_conserve = $vote_a_proposition['id_vote'];
// Pas besoin de mettre à jour un clé primaire !
unset($vote_a_proposition['id_vote']);
// Suppression des champs qui n'existent pas dans la base de données
unset($vote_a_proposition['raison']);
$maj_vote = array();
$vote_a_proposition['ce_proposition'] = $id_proposition_conservee;
foreach($vote_a_proposition as $champ_v => $valeur_v) {
$maj_vote[] = $champ_v.'='.$this->conteneur->getBdd()->proteger($valeur_v);
}
// tous les votes pertinents pointent maintenant vers la proposition fusionnée
// certains votes voient leur valeur mise à jour (fusion de valeurs de votes)
$requete_maj_vote = "UPDATE del_commentaire_vote SET ".implode(',', $maj_vote)." ".
"WHERE id_vote = ".$id_vote_conserve;
$modif_vote = $this->conteneur->getBdd()->requeter($requete_maj_vote);
}
}
}
// Suppression des propositions devenues inutiles
$propositions_supprimees = $propositions_a_obs_a_nom['propositions_supprimees'];
if(!empty($propositions_supprimees)) {
$ids_propositions_supprimees = array();
foreach($propositions_supprimees as $index_prop_supprimee => $proposition_supprimee) {
$ids_propositions_supprimees[] = $this->conteneur->getBdd()->proteger($proposition_supprimee['id_commentaire']);
}
$requete_supp_prop = "DELETE FROM del_commentaire ".
"WHERE id_commentaire IN (".
implode(',', $ids_propositions_supprimees).
")";
$supp_prop = $this->conteneur->getBdd()->requeter($requete_supp_prop);
}
}
private function affecterNnsAuxPropositionsViables() {
$this->construireTableauReferentiels();
echo "Affectation de nn aux propositions qui le permettent \n";
$propositions = $this->obtenirPropositionsPotentiellementViables();
$noms_ordonnes = array();
$nb_propositions_traitees = 0;
$nb_prop_orphelines = count($propositions);
$nb_prop_augmentees = 0;
$nb_noms_trouves = 0;
$nb_prop_inchangees = 0;
echo $nb_prop_orphelines.' propositions sont potentiellement améliorables'."\n";
foreach($propositions as $prop) {
$referentiel = in_array($prop['referentiel_proposition'], array_keys($this->tables_referentiel)) ?
$prop['referentiel_proposition'] : $prop['referentiel_observation'];
$referentiel = in_array($referentiel, array_keys($this->tables_referentiel)) ?
$referentiel : 'tous';
$referentiel = substr($referentiel, 0, 5);
if($referentiel == 'tous') {
$noms_trouves = 0;
foreach(array_keys($this->tables_referentiel) as $ref) {
$noms_possibles[$referentiel] = $this->rechercherNomsPossibles($ref, $prop['nom_sel']);
$nb_noms_trouves += count($noms_possibles[$referentiel]);
$noms_trouves += empty($noms_possibles[$referentiel]) ? 1 : 0;
}
$nb_prop_inchangees += $noms_trouves != 0 ? 1 : 0;
// Un seul référentiel correspond (c'est déjà bon signe)
if(count($noms_possibles) == 1) {
$nom_dans_ref = reset($noms_possibles);
$nom_referentiel = key($noms_possibles);
// Un seul nom trouvé : OK
if(count($nom_dans_ref) == 1) {
if(isset($nom_dans_ref['num_nom'])) {
$this->mettreAjourProposition($prop['id_commentaire'], $nom_referentiel, $nom_dans_ref);
$nb_prop_augmentees ++;
}
}
}
} else {
$noms_possibles = $this->rechercherNomsPossibles($referentiel, $prop['nom_sel']);
$noms_ordonnes[$prop['id_commentaire']]['nom_trouves'] = $noms_possibles;
$nb_noms_trouves += count($noms_possibles);
$nb_prop_inchangees += empty($noms_possibles) ? 1 : 0;
// On ne met à jour qu'en étant absolument sur (si l'on a trouvé un seul nom)
if(count($noms_possibles) == 1) {
$nom_dans_ref = array_pop($noms_possibles);
if(isset($nom_dans_ref['num_nom'])) {
$this->mettreAjourProposition($prop['id_commentaire'], $referentiel, $nom_dans_ref);
$nb_prop_augmentees ++;
}
}
}
$this->afficherAvancement('propositions traitées ', 1);
}
$noms_ordonnes[$prop['id_commentaire']]['proposition'] = $prop;
echo "\n";
echo $nb_noms_trouves.' noms ont été trouvés'."\n";
echo $nb_prop_augmentees.' propositions ont été améliorées'."\n";
echo $nb_prop_inchangees.' propositions n\'ont pas pu être améliorées'."\n";
}
private function obtenirPropositionsPotentiellementViables() {
$requete = 'SELECT DISTINCT dc.id_commentaire, dc.nom_sel, dc.nom_referentiel as referentiel_proposition, '.
'do.nom_referentiel as referentiel_observation '.
'FROM del_commentaire dc '.
'INNER JOIN del_observation do '.
' ON do.id_observation = dc.ce_observation '.
'WHERE dc.nom_sel != "" AND (dc.nom_sel_nn = 0 OR dc.nom_sel_nn IS NULL)';
 
$propositions = $this->conteneur->getBdd()->recupererTous($requete);
return $propositions;
}
private function rechercherNomsPossibles($referentiel, $nom) {
$nom = trim($nom);
$requete = "SELECT num_nom, num_nom_retenu, nom_sci, CONCAT(nom_sci, ' ', auteur) as nom_sci_etendu FROM ".$this->tables_referentiel[$referentiel]." ".
"WHERE CONCAT(nom_sci, ' ', auteur) = ".$this->conteneur->getBdd()->proteger($nom)." ".
" OR nom_sci = ".$this->conteneur->getBdd()->proteger($nom)." ".
"ORDER BY CONCAT(nom_sci, ' ', auteur) ASC";
$noms = $this->conteneur->getBdd()->recupererTous($requete);
return $noms;
}
private function mettreAjourProposition($id_proposition, $referentiel, $infos) {
$requete = "UPDATE del_commentaire ".
"SET nom_sel_nn = ".$this->conteneur->getBdd()->proteger($infos['num_nom']).", ".
" nom_referentiel = ".$this->conteneur->getBdd()->proteger($referentiel)." ".
"WHERE id_commentaire = ".$this->conteneur->getBdd()->proteger($id_proposition);
return $this->conteneur->getBdd()->executer($requete);
}
private function construireTableauReferentiels() {
$referentiels_dispos = explode(',', $this->conteneur->getParametre('referentiels'));
foreach($referentiels_dispos as $ref) {
$this->tables_referentiel[$ref] = $this->conteneur->getParametre('table_referentiel_'.$ref);
}
}
}
/branches/v1.11-magnesium/scripts/modules/commentaires/AlerteMailCommentaires.php
New file
0,0 → 1,576
<?php
// declare(encoding='UTF-8');
/**
* Le script récupère tous les commentaires et déterminations fait ce jour et envoie un mail
* aux auteurs des observations récapitulant celle-ci
* Si un commentaire porte sur un autre commentaire, un message est envoyé à l'auteur
* de celui-ci pour l'avertir de celui-là
*
* @category DEL
* @package Scripts
* @subpackage Commentaires
* @author Mathias CHOUET <mathias@tela-botanica.org>
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Aurelien PERONNET <aurelien@tela-botanica.org>
* @author Michel GALABRU <ajudant-gerber@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-2015 Tela Botanica <accueil@tela-botanica.org>
*/
class AlerteMailCommentaires {
 
protected $conteneur;
protected $testeurCourriel = null;
protected $recapitulatifAdmin = [];
protected $messageRecapitulatif = null;
protected $dateRenvoi = null;
 
protected $observations_concernees = array();
protected $commentaires_concernes = array();
protected $correspondance_id_obs_auteurs = array();
protected $utilisateursInfos = array();
 
public function __construct($conteneur) {
$this->conteneur = $conteneur;
}
 
public function executer() {
try {
$this->verifierModeUtilisation();
$this->verifierDateRenvoi();
$observationsCommentees = $this->chargerObservationsCommentees();
$commentairesCommentes = $this->chargerCommentairesCommentes();
$this->formaterObservationsCommentees($observationsCommentees);
$this->formaterCommentairesCommentes($commentairesCommentes);
$this->envoyerMessageAuxDestinataires();
} catch (Exception $e) {
echo "ERREUR: " . $e->getMessage() . "\n";
}
}
 
/**
* Passe en mode "test" si l'adresse email d'un testeur est fournie avec "-t"
*/
protected 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».";
throw new Exception($msg);
}
}
}
 
/**
* Simule l'envoi à une date arbitraire, si celle-ci est fournie avec "-d"
*/
protected 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».";
throw new Exception($msg);
}
}
}
 
/**
* Charge toutes les observations ayant été commentées dans les dernières 24h
* et dont l'auteur souhaite être averti des commentaires
*/
protected 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'";
 
$requete = "SELECT do.id_observation, do.ce_utilisateur, do.courriel_utilisateur, do.date_observation"
. ", do.nom_sel, do.nom_ret, do.zone_geo, do.ce_zone_geo, do.lieudit, do.station"
. ", dc.nom_sel as dc_nom_sel, dc.utilisateur_prenom as dc_utilisateur_prenom, dc.utilisateur_nom as dc_utilisateur_nom, dc.texte as dc_texte"
. " FROM del_commentaire dc" // tous les commentaires
. " LEFT JOIN del_observation do" // infos obs d'origine
. " ON dc.ce_observation = do.id_observation"
// ci-dessous un OR et non un AND car les non-inscrits ont tous le meme ce_utilisateur : 0
. " WHERE (dc.ce_utilisateur != do.ce_utilisateur OR dc.utilisateur_courriel != do.courriel_utilisateur)" // en excluant les gens qui se parlent à eux-mêmes
. " AND TO_SECONDS($date) - TO_SECONDS(dc.date) < 86400 AND TO_SECONDS($date) - TO_SECONDS(dc.date) > 0" // depuis moins de 24h
. " AND do.ce_utilisateur NOT IN ("
. " SELECT id_utilisateur FROM del_utilisateur_infos WHERE preferences LIKE $preferences" // si l'auteur d'origine souhaite être averti
. ")"
. " -- " .__FILE__.':'.__LINE__;
 
$observations = $this->conteneur->getBdd()->recupererTous($requete);
return $observations;
}
 
/**
* Charge tous les commentaires ayant reçu une réponse dans les dernières 24h
* et dont l'auteur souhaite être averti des réponses à ses commentaires,
* le tout accompagné des infos de l'observation d'origine
*/
protected function chargerCommentairesCommentes() {
// Seuls les utilisateurs ayant explicitement refusé le mail sont ignorés
$preferences = $this->conteneur->getBdd()->proteger('%"mail_notification_mes_com":"0"%');
$date = ($this->dateRenvoi == null) ? 'NOW()' : "'$this->dateRenvoi'";
 
$requete = "SELECT do.id_observation, do.ce_utilisateur, do.courriel_utilisateur, do.prenom_utilisateur, do.nom_utilisateur"
. ", do.date_observation, do.nom_sel, do.nom_ret, do.zone_geo, do.ce_zone_geo, do.lieudit, do.station"
. ", dc.nom_sel as dc_nom_sel, dc.utilisateur_prenom as dc_utilisateur_prenom, dc.utilisateur_nom as dc_utilisateur_nom, dc.texte as dc_texte"
. ", dco.id_commentaire as dco_id_commentaire, dco.ce_utilisateur as dco_ce_utilisateur, dco.date as dco_date, dco.texte as dco_texte"
. ", dco.nom_sel as dco_nom_sel, dco.nom_ret as dco_nom_ret, dco.utilisateur_courriel as dco_utilisateur_courriel"
. " FROM del_commentaire dc" // tous les commentaires
. " LEFT JOIN del_commentaire dco" // portant sur un autre commentaire
. " ON dc.ce_commentaire_parent = dco.id_commentaire"
. " LEFT JOIN del_observation do" // infos obs d'origine
. " ON dco.ce_observation = do.id_observation"
// ci-dessous un OR et non un AND car les non-inscrits ont tous le meme ce_utilisateur : 0
. " WHERE (dc.ce_utilisateur != dco.ce_utilisateur OR dc.utilisateur_courriel != dco.utilisateur_courriel)" // en excluant les gens qui se parlent à eux-mêmes
. " AND TO_SECONDS($date) - TO_SECONDS(dc.date) < 86400 AND TO_SECONDS($date) - TO_SECONDS(dc.date) > 0" // depuis moins de 24h
. " AND dco.ce_utilisateur NOT IN ("
. " SELECT id_utilisateur FROM del_utilisateur_infos WHERE preferences LIKE $preferences" // si l'auteur d'origine souhaite être averti
. " )"
. " -- " .__FILE__.':'.__LINE__;
 
$commentaires = $this->conteneur->getBdd()->recupererTous($requete);
return $commentaires;
}
 
/**
* Classe les observations par id_utilisateur (ou courriel si non inscrit)
* de l'auteur puis par id_observation, et enfile les données des commentaires
* relatifs à chaque obs
*/
protected function formaterObservationsCommentees($liste) {
//print_r($liste);
foreach ($liste as $o) {
$id_obs = $o['id_observation'];
$id_auteur_obs = $o['ce_utilisateur'];
// si utilisateur non inscrit
if (($id_auteur_obs == 0) && ($o['courriel_utilisateur'] != '')) {
$id_auteur_obs = $o['courriel_utilisateur'];
}
 
// mode gentleman
if (! isset($this->observations_concernees[$id_auteur_obs])) {
$this->observations_concernees[$id_auteur_obs] = array();
}
 
if (! isset($this->observations_concernees[$id_auteur_obs][$id_obs])) {
// infos à mentionner dans l'email
$infos = array();
$infos['id'] = $id_obs;
$infos['nom_sci'] = $this->formaterNomSci($o);
$infos['date'] = $this->formaterDate($o['date_observation']);
$infos['lieu'] = $this->formaterLieu($o);
$infos['lien'] = $this->obtenirLienFiche($id_obs);
$infos['commentaires'] = array();
 
$this->observations_concernees[$id_auteur_obs][$id_obs] = $infos;
$this->observations_concernees[$id_auteur_obs][$id_obs]['commentaires'] = array();
}
 
// isolation des données du commentaire
$commentaire = $this->arrayTranchette($o, 'dc_', true);
//print_r($commentaire);
$commentaire['auteur'] = $this->formaterAuteurCommentaire($o);
$this->observations_concernees[$id_auteur_obs][$id_obs]['commentaires'][] = $commentaire;
}
//print_r($this->observations_concernees); exit;
}
 
/**
* Classe les commentaires par id_utilisateur (ou courriel si non inscrit)
* de l'auteur puis par id_commentaire, et enfile les données des réponses
* (commentaires) relatives à chaque commentaire
*/
protected function formaterCommentairesCommentes($liste) {
//print_r($liste); exit;
foreach ($liste as $o) {
// infos sur le commentaire d'origine
$id_co = $o['dco_id_commentaire'];
$id_auteur_co = $o['dco_ce_utilisateur'];
// si utilisateur non inscrit
if (($id_auteur_co == 0) && ($o['dco_utilisateur_courriel'] != '')) {
$id_auteur_co = $o['dco_utilisateur_courriel'];
}
 
// mode gentleman
if (! isset($this->commentaires_concernes[$id_auteur_co])) {
$this->commentaires_concernes[$id_auteur_co] = array();
}
 
// @TODO grouper sur l'obs avant de grouper sur le commentaire d'origine
// afin de ne pas répéter l'obs si 2 commentaires d'origine portent
// sur la même
if (! isset($this->commentaires_concernes[$id_auteur_co][$id_co])) {
// infos à mentionner dans l'email
$infos = array();
$infos['id_obs'] = $o['id_observation'];
$id_auteur_obs = $o['ce_utilisateur'];
// si utilisateur non inscrit
if (($id_auteur_obs == 0) && ($o['courriel_utilisateur'] != '')) {
$id_auteur_obs = $o['courriel_utilisateur'];
}
$infos['id_auteur_obs'] = $id_auteur_obs;
$infos['auteur_obs'] = $this->formaterAuteurObs($o);
$infos['nom_sci'] = $this->formaterNomSci($o);
$infos['nom_sel_co'] = $o['dco_nom_sel'];
$infos['nom_sci_co'] = $this->formaterNomSci($o, 'dco_');
$infos['date'] = $this->formaterDate($o['date_observation']);
$infos['date_co'] = $this->formaterDate($o['dco_date']);
$infos['texte_co'] = $o['dco_texte'];
$infos['lieu'] = $this->formaterLieu($o);
$infos['lien'] = $this->obtenirLienFiche($o['id_observation']);
$infos['commentaires'] = array();
$this->commentaires_concernes[$id_auteur_co][$id_co] = $infos;
$this->commentaires_concernes[$id_auteur_co][$id_co]['commentaires'] = array();
}
 
// isolation des données du commentaire
$commentaire = $this->arrayTranchette($o, 'dc_', true);
$commentaire['auteur'] = $this->formaterAuteurCommentaire($o);
$this->commentaires_concernes[$id_auteur_co][$id_co]['commentaires'][] = $commentaire;
}
//print_r($this->commentaires_concernes); exit;
}
 
/**
* Envoie un message par destinataire (auteur d'une observation ayant reçu
* un commentaire ou d'un commentaire ayant reçu une réponse), en format
* texte brut + HTML, avec un squelette différent selon que le destinataire
* est inscrit ou non; en mode test, n'envoie les messages qu'au testeur
*/
protected function envoyerMessageAuxDestinataires() {
// liste des auteurs à contacter
$auteurs = array_unique(array_merge(array_keys($this->observations_concernees), array_keys($this->commentaires_concernes)));
 
foreach ($auteurs as $auteurId) {
$messageTxt = '';
$messageHtml = '';
 
// données concernant cet auteur
$obsDeCetAuteur = null;
if (isset($this->observations_concernees[$auteurId])) {
$obsDeCetAuteur = $this->observations_concernees[$auteurId];
}
$comDeCetAuteur = null;
if (isset($this->commentaires_concernes[$auteurId])) {
$comDeCetAuteur = $this->commentaires_concernes[$auteurId];
}
$donnees = $this->formaterDonneesPourMessage($auteurId, $obsDeCetAuteur, $comDeCetAuteur);
 
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
protected 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;
}
 
/**
* Organise un peu les données d'un auteur pour les traiter plus facilement
* dans les squelettes de messages
*/
protected function formaterDonneesPourMessage($auteurId, $liste_obs, $liste_com) {
$donnees = array(
'liste_observations' => array(),
'liste_commentaires' => array()
);
if ($liste_obs != null) {
$donnees['liste_observations'] = $liste_obs;
}
if ($liste_com != null) {
$donnees['liste_commentaires'] = $liste_com;
}
 
if (is_numeric($auteurId)) { // inscrit
$infosUtilisateur = $this->telechargerUtilisateurInfos($auteurId);
$donnees['auteur_formate'] = $this->formaterIntituleUtilisateur($infosUtilisateur);
} else { // non-inscrit
$donnees['auteur_formate'] = $auteurId;
}
 
return $donnees;
}
 
/**
* Rédige un message HTML en appliquant les données fournies au squelette
* adéquat, selon que le destinataire est inscrit ou non
*/
protected 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;
}
 
/**
* Rédige un message en texte brut en appliquant les données fournies au squelette
* adéquat, selon que le destinataire est inscrit ou non
*/
protected 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;
}
 
// envoie un message à un utilisateur inscrit, à l'aide de l'annuaire
protected 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);
}
 
protected 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);
}
 
protected 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
protected 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";
//echo "MNE HTML:: " . $messageNonInscrits['html'] . "\n\n"; exit;
$messagerie->envoyerHtml($this->testeurCourriel, $sujet, $messageNonInscrits['html'], $messageNonInscrits['txt']);
 
$sujet = "TESTEUR : TXT (non inscrits) - commentaire du $dateFmt";
$messagerie->envoyerTxt($this->testeurCourriel, $sujet, $messageNonInscrits['txt']);
}
}
 
protected 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;
}
 
protected 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, true);
if (is_array($infos)) {
$infos = array_shift($infos);
}
$this->utilisateursInfos[$utilisateurId] = isset($infos['id']) ? $infos : $utilisateurId;
}
return $this->utilisateursInfos[$utilisateurId];
}
 
protected 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;
}
 
protected function formaterNomSci($obs, $prefixe='') {
$cnr = $prefixe . 'nom_ret';
$cns = $prefixe . 'nom_sel';
$nom = 'Espèce indéterminée';
if ($obs[$cnr] != '') {
$nom = $obs[$cnr];
} else if($obs[$cns] != '') {
$nom = $obs[$cns];
}
return $nom;
}
 
protected 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;
}
 
protected 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;
}
 
protected function formaterDateCourante() {
$date = ($this->dateRenvoi == null) ? time() : strtotime($this->dateRenvoi);
$dateFmt = date('d-m-Y', $date);
return $dateFmt;
}
 
protected function formaterAuteurObs($obs) {
return $obs['prenom_utilisateur'].' '.$obs['nom_utilisateur'];
}
 
protected function formaterAuteurCommentaire($commentaire) {
return $commentaire['dc_utilisateur_prenom'].' '.$commentaire['dc_utilisateur_nom'];
}
 
protected function formaterIntituleUtilisateur($infos) {
$intituleUtilisateur = isset($infos['intitule']) ? $infos['intitule'] : '';
return $intituleUtilisateur;
}
 
protected 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;
}
 
protected function obtenirLienFiche($id_obs) {
return sprintf($this->conteneur->getParametre('url_fiche_observation'), $id_obs);
}
 
/**
* Copie dans un nouveau tableau les valeurs de $tab dont les clefs
* commencent par $prefixe
*/
protected function arrayTranchette($tab, $prefixe, $enleverPrefixe=false) {
$res = array();
foreach ($tab as $k => $v) {
if (strpos($k, $prefixe) === 0) {
$clef = $k;
if ($enleverPrefixe === true) {
$clef = substr($k, strlen($prefixe));
}
$res[$clef] = $v;
}
}
return $res;
}
}
/branches/v1.11-magnesium/scripts/modules/commentaires/Commentaires.php
New file
0,0 → 1,47
<?php
//declare(encoding='UTF-8');
/**
* Envoie les alertes par email sur les commentaires ajoutées dans DEL.
* Utilisation : <code>/opt/lampp/bin/php cli.php commentaires -a alerteMail</code>
*
* @category DEL
* @package Scripts
* @subpackage Commentaires
* @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 Commentaires extends DelScript {
 
protected $parametres_autorises = array(
'-t' => array(false, true, 'Indiquez votre e-mail pour tester le script.'),
'-d' => array(false, true, "Indiquer une date au format yyyy-mm-dd pour renvoyer l'alerte de cette date."));
 
public function executer() {
try {
$this->initialiserProjet('commentaires');
 
// Lancement de l'action demandée
$cmd = $this->getParametre('a');
switch ($cmd) {
case 'alerteMail' :
$script = $this->chargerClasse('AlerteMailCommentaires');
$script->executer();
break;
default :
throw new Exception("Erreur : la commande '$cmd' n'existe pas!");
}
} catch (Exception $e) {
$this->traiterErreur($e->getMessage());
}
}
 
private function chargerClasse($classe) {
require_once $classe.'.php';
$conteneur = new Conteneur($this->parametres);
return new $classe($conteneur);
}
}
/branches/v1.11-magnesium/scripts/modules/commentaires/squelettes/commentaires_non_inscrits.tpl.html
New file
0,0 → 1,211
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
<!--
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
}
.logo {
float: left;
}
h1 {
margin-left: 120px;
margin-top: 10px;
padding-top: 14px;
}
.identiplante {
color: #919F54;
}
.salutations {
/*border: solid blue 1px;*/
margin-top: 40px;
font-size: 16px;
}
.destinataire {
/*font-weight: bold;
color: #E16E38;*/
}
.description_bloc {
/*border: solid red 1px;*/
margin-top: 25px;
margin-bottom: 10px;
font-weight:bold;
font-size: 18px;
color: #919F54;
}
.bloc_observation {
margin-bottom: 10px;
/*border: solid blue 1px;*/
}
.observation {
/*border: dashed green 1px;*/
padding-left: 10px;
font-weight: bold;
}
.observation a {
/*color: #1AB113;*/
color: #E16E38;
text-decoration: none;
}
.puce_obs {
font-weight: normal;
}
.en_reponse_a {
/*border: dashed green 1px;*/
padding-left: 22px;
margin-top: 1px;
}
ul.liste_commentaires {
list-style-type: circle;
/*border: solid grey 1px;*/
padding-left: 52px;
margin: 0;
margin-top: 3px;
}
ul.liste_commentaires li {
margin-top: 1px;
}
.proposition {
font-weight: bold;
/*color: #E16E38;*/
}
.poursuivre {
font-size: 16px;
/*border: solid green 1px;*/
margin-bottom: 5px;
margin-top: 30px;
color: #333;
}
ul.options_participation {
/*border: solid green 1px;*/
margin-top: 0;
padding-left: 25px;
}
ul.options_participation li {
/*border: solid green 1px;*/
margin-top: 1px;
}
ul.options_participation a {
color: #1AB113;
}
.merci_au_revoir {
margin-top: 25px;
}
.signature {
/*color: #918A6F;*/
}
.mentions_legales {
font-size: 11px;
margin-top: 10px;
}
.mentions_legales a {
color: #1AB113;
}
-->
</style>
</head>
 
<body>
<img class="logo" src="http://resources.tela-botanica.org/tb/img/64x64/logo_carre_officiel.png" />
<h1><span class="identiplante">IdentiPlante</span> : un telabotaniste vous a aid&eacute; </h1>
<div class="salutations">
Bonjour <?= $auteur_formate ?>,
</div>
 
<?php if(count($liste_observations) > 0): ?>
<div class="description_bloc">
Un Telabotaniste vient de proposer une nouvelle d&eacute;termination ou de faire un commentaire concernant vos donn&eacute;es :
</div>
<?php endif; ?>
 
<?php foreach ($liste_observations as $observation) : ?>
<div class="bloc_observation">
<div class="observation">
<span class="puce_obs">&gt;&gt;&gt;</span>
<a href="<?= $observation['lien']; ?>">
Obs. n° <?= $observation['id'] ?> - <span class="proposition"><?= $observation['nom_sci']; ?></span>
</a>
observ&eacute; le <?= $observation['date']; ?> &agrave; <?= $observation['lieu']; ?>
</div>
<ul class="liste_commentaires">
<?php foreach ($observation['commentaires'] as $commentaire) : ?>
<li>
<?php if ($commentaire['nom_sel'] == '') : ?>
<?= $commentaire['auteur'] ?> a &eacute;crit <em>"<?= $commentaire['texte'] ?>"</em>
<?php else : ?>
<?= $commentaire['auteur'] ?> a propos&eacute; <span class="proposition"><?= $commentaire['nom_sel'] ?></span>
<?php if ($commentaire['texte'] != '') : ?>
et a &eacute;crit : <em>"<?= $commentaire['texte'] ?>"</em>
<?php endif; ?>
<?php endif; ?>
</li>
<?php endforeach ; ?>
</ul>
</div>
<?php endforeach ; ?>
 
<?php if(count($liste_commentaires) > 0): ?>
<div class="description_bloc">
Un Telabotaniste vient de r&eacute;pondre &agrave; un de vos commentaires ou une de vos propositions :
</div>
<?php endif; ?>
 
<?php foreach ($liste_commentaires as $co) : ?>
<div class="bloc_observation">
<div class="observation">
<span class="puce_obs">&gt;&gt;&gt;</span>
<a href="<?= $co['lien']; ?>">
Obs. n°<?= $co['id_obs'] ?> - <span class="proposition"><?= $co['nom_sci']; ?></span>
</a>
observ&eacute; le <?= $co['date']; ?> &agrave; <?= $co['lieu']; ?>
</div>
<div class="en_reponse_a">
<?php if ($co['nom_sel_co'] == '') : ?>
En r&eacute;ponse &agrave; votre commentaire "<?= $co['texte_co'] ?>",
<?php else : ?>
En r&eacute;ponse &agrave; votre proposition <span class="proposition">"<?= $co['nom_sci_co'] ?>"</span>
<?php if($co['texte_co'] != ''): ?> accompagn&eacute;e du commentaire "<?= $co['texte_co'] ?>"<?php endif; ?>,
<?php endif; ?>
</div>
<ul class="liste_commentaires">
<?php foreach ($co['commentaires'] as $commentaire) : ?>
<li>
<?= $commentaire['auteur'] ?> a &eacute;crit <em>"<?= $commentaire['texte'] ?>"</em>
</li>
<?php endforeach ; ?>
</ul>
</div>
<?php endforeach ; ?>
 
<h2 class="poursuivre">Je souhaite poursuivre ma participation</h2>
<ul class="options_participation">
<li>
En m'<a href="http://www.tela-botanica.org/inscription">inscrivant gratuitement &agrave; Tela Botanica</a>
</li>
<li>
En utilisant <a href="http://www.tela-botanica.org/page:validation_donnees">Identiplante</a> l'outil de d&eacute;termination collaborative en ligne
</li>
<li>
En g&eacute;rant finement mes donn&eacute;es avec le <a href="http://www.tela-botanica.org/page:cel">Carnet en Ligne</a>
</li>
</ul>
 
<div class="merci_au_revoir">
Bonne continuation sur nos outils botaniques !
<br/>
<span class="signature">L'&eacute;quipe de Tela Botanica</span>
<br/>
<div>---------</div>
</div>
 
<div class="mentions_legales">
Ce message vous est envoy&eacute; par l'interm&eacute;diaire de l'application IdentiPlante du r&eacute;seau Tela Botanica :
<a href="http://www.tela-botanica.org/appli:identiplante">http://www.tela-botanica.org/appli:identiplante</a>
<br/>
Si vous ne souhaitez plus recevoir ce mail d&eacute;cochez la case dans vos pr&eacute;f&eacute;rences sur l'interface d'IdentiPlante.
</div>
</body>
</html>
/branches/v1.11-magnesium/scripts/modules/commentaires/squelettes/commentaires.tpl.txt
New file
0,0 → 1,42
Bonjour <?= $auteur_formate ?>,
<?php if(count($liste_observations) > 0): ?>
 
Un Telabotaniste vient de proposer une nouvelle détermination ou de faire un commentaire concernant vos données :
 
<?php foreach ($liste_observations as $observation) : ?>
--> Observation #<?= $observation['id'] ?> - <<?= $observation['lien']; ?>>
* <?= $observation['nom_sci']; ?> observée le <?= $observation['date']; ?> à <?= $observation['lieu']; ?> *
<?php foreach ($observation['commentaires'] as $commentaire) : ?>
<?php if ($commentaire['nom_sel'] == '') : ?>
- <?= $commentaire['auteur'] ?> a écrit "<?= $commentaire['texte'] ?>"
<?php else : ?>
- <?= $commentaire['auteur'] ?> a proposé "<?= $commentaire['nom_sel'] ?>"<?php if ($commentaire['texte'] != '') : ?> et a écrit : "<?= $commentaire['texte'] ?>"<?php endif; ?>
 
<?php endif; ?>
<?php endforeach ; ?>
<?php endforeach ; ?>
<?php endif; ?>
 
<?php if(count($liste_commentaires) > 0): ?>
Un Telabotaniste vient de répondre à un de vos commentaires ou une de vos propositions :
 
<?php foreach ($liste_commentaires as $co) : ?>
--> Observation #<?= $co['id_obs'] ?> - <<?= $co['lien']; ?>>
* <?= $co['nom_sci']; ?> observée le <?= $co['date']; ?> à <?= $co['lieu']; ?> *
<?php if ($co['nom_sel_co'] == '') : ?>
En réponse à votre commentaire "<?= $co['texte_co'] ?>",
<?php else : ?>
En réponse à votre proposition "<?= $co['nom_sci_co'] ?>"<?php if($co['texte_co'] != ''): ?> accompagnée du commentaire "<?= $co['texte_co'] ?>"<?php endif; ?>,
<?php endif; ?>
<?php foreach ($co['commentaires'] as $commentaire) : ?>
- <?= $commentaire['auteur'] ?> a écrit "<?= $commentaire['texte'] ?>"
<?php endforeach ; ?>
<?php endforeach ; ?>
<?php endif; ?>
 
Bonne continuation sur nos outils botaniques !
--
L'équipe de Tela Botanica
 
Ce message vous est envoyé par l'intermédiaire de l'application IdentiPlante du réseau Tela Botanica : http://www.tela-botanica.org/appli:identiplante
Si vous ne souhaitez plus recevoir ce courriel décochez la case dans vos préférences sur l'interface d'IdentiPlante.
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/v1.11-magnesium/scripts/modules/commentaires/squelettes/commentaires.tpl.html
New file
0,0 → 1,198
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
<!--
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
}
.logo {
float: left;
}
h1 {
margin-left: 120px;
margin-top: 10px;
padding-top: 14px;
}
.identiplante {
color: #919F54;
}
.salutations {
/*border: solid blue 1px;*/
margin-top: 40px;
font-size: 16px;
}
.destinataire {
/*font-weight: bold;
color: #E16E38;*/
}
.description_bloc {
/*border: solid red 1px;*/
margin-top: 25px;
margin-bottom: 10px;
font-weight:bold;
font-size: 18px;
color: #919F54;
}
.bloc_observation {
margin-bottom: 10px;
/*border: solid blue 1px;*/
}
.observation {
/*border: dashed green 1px;*/
padding-left: 10px;
font-weight: bold;
}
.observation a {
/*color: #1AB113;*/
color: #E16E38;
text-decoration: none;
}
.puce_obs {
font-weight: normal;
}
.en_reponse_a {
/*border: dashed green 1px;*/
padding-left: 22px;
margin-top: 1px;
}
ul.liste_commentaires {
list-style-type: circle;
/*border: solid grey 1px;*/
padding-left: 52px;
margin: 0;
margin-top: 3px;
}
ul.liste_commentaires li {
margin-top: 1px;
}
.proposition {
font-weight: bold;
/*color: #E16E38;*/
}
.poursuivre {
font-size: 16px;
/*border: solid green 1px;*/
margin-bottom: 5px;
margin-top: 30px;
color: #333;
}
ul.options_participation {
/*border: solid green 1px;*/
margin-top: 0;
padding-left: 25px;
}
ul.options_participation li {
/*border: solid green 1px;*/
margin-top: 1px;
}
ul.options_participation a {
color: #1AB113;
}
.merci_au_revoir {
margin-top: 25px;
}
.signature {
/*color: #918A6F;*/
}
.mentions_legales {
font-size: 11px;
margin-top: 10px;
}
.mentions_legales a {
color: #1AB113;
}
-->
</style>
</head>
 
<body>
<img class="logo" src="http://resources.tela-botanica.org/tb/img/64x64/logo_carre_officiel.png" />
<h1><span class="identiplante">IdentiPlante</span> : un telabotaniste vous a aid&eacute; </h1>
<div class="salutations">
Bonjour <?= $auteur_formate ?>,
</div>
 
<?php if(count($liste_observations) > 0): ?>
<div class="description_bloc">
Un Telabotaniste vient de proposer une nouvelle d&eacute;termination ou de faire un commentaire concernant vos donn&eacute;es :
</div>
<?php endif; ?>
 
<?php foreach ($liste_observations as $observation) : ?>
<div class="bloc_observation">
<div class="observation">
<span class="puce_obs">&gt;&gt;&gt;</span>
<a href="<?= $observation['lien']; ?>">
Obs. n° <?= $observation['id'] ?> - <span class="proposition"><?= $observation['nom_sci']; ?></span>
</a>
observ&eacute; le <?= $observation['date']; ?> &agrave; <?= $observation['lieu']; ?>
</div>
<ul class="liste_commentaires">
<?php foreach ($observation['commentaires'] as $commentaire) : ?>
<li>
<?php if ($commentaire['nom_sel'] == '') : ?>
<?= $commentaire['auteur'] ?> a &eacute;crit <em>"<?= $commentaire['texte'] ?>"</em>
<?php else : ?>
<?= $commentaire['auteur'] ?> a propos&eacute; <span class="proposition"><?= $commentaire['nom_sel'] ?></span>
<?php if ($commentaire['texte'] != '') : ?>
et a &eacute;crit : <em>"<?= $commentaire['texte'] ?>"</em>
<?php endif; ?>
<?php endif; ?>
</li>
<?php endforeach ; ?>
</ul>
</div>
<?php endforeach ; ?>
 
<?php if(count($liste_commentaires) > 0): ?>
<div class="description_bloc">
Un Telabotaniste vient de r&eacute;pondre &agrave; un de vos commentaires ou une de vos propositions :
</div>
<?php endif; ?>
 
<?php foreach ($liste_commentaires as $co) : ?>
<div class="bloc_observation">
<div class="observation">
<span class="puce_obs">&gt;&gt;&gt;</span>
<a href="<?= $co['lien']; ?>">
Obs. n°<?= $co['id_obs'] ?> - <span class="proposition"><?= $co['nom_sci']; ?></span>
</a>
observ&eacute; le <?= $co['date']; ?> &agrave; <?= $co['lieu']; ?>
</div>
<div class="en_reponse_a">
<?php if ($co['nom_sel_co'] == '') : ?>
En r&eacute;ponse &agrave; votre commentaire "<?= $co['texte_co'] ?>",
<?php else : ?>
En r&eacute;ponse &agrave; votre proposition <span class="proposition">"<?= $co['nom_sci_co'] ?>"</span>
<?php if($co['texte_co'] != ''): ?> accompagn&eacute;e du commentaire "<?= $co['texte_co'] ?>"<?php endif; ?>,
<?php endif; ?>
</div>
<ul class="liste_commentaires">
<?php foreach ($co['commentaires'] as $commentaire) : ?>
<li>
<?= $commentaire['auteur'] ?> a &eacute;crit <em>"<?= $commentaire['texte'] ?>"</em>
</li>
<?php endforeach ; ?>
</ul>
</div>
<?php endforeach ; ?>
 
<div class="merci_au_revoir">
Bonne continuation sur nos outils botaniques !
<br/>
<span class="signature">L'&eacute;quipe de Tela Botanica</span>
<br/>
<div>---------</div>
</div>
 
<div class="mentions_legales">
Ce message vous est envoy&eacute; par l'interm&eacute;diaire de l'application IdentiPlante du r&eacute;seau Tela Botanica :
<a href="http://www.tela-botanica.org/appli:identiplante">http://www.tela-botanica.org/appli:identiplante</a>
<br/>
Si vous ne souhaitez plus recevoir ce mail d&eacute;cochez la case dans vos pr&eacute;f&eacute;rences sur l'interface d'IdentiPlante.
</div>
</body>
</html>
/branches/v1.11-magnesium/scripts/modules/commentaires/squelettes/commentaires_non_inscrits.tpl.txt
New file
0,0 → 1,47
Bonjour <?= $auteur_formate ?>,
<?php if(count($liste_observations) > 0): ?>
 
Un Telabotaniste vient de proposer une nouvelle détermination ou de faire un commentaire concernant vos données :
 
<?php foreach ($liste_observations as $observation) : ?>
--> Observation #<?= $observation['id'] ?> - <<?= $observation['lien']; ?>>
* <?= $observation['nom_sci']; ?> observée le <?= $observation['date']; ?> à <?= $observation['lieu']; ?> *
<?php foreach ($observation['commentaires'] as $commentaire) : ?>
<?php if ($commentaire['nom_sel'] == '') : ?>
- <?= $commentaire['auteur'] ?> a écrit "<?= $commentaire['texte'] ?>"
<?php else : ?>
- <?= $commentaire['auteur'] ?> a proposé "<?= $commentaire['nom_sel'] ?>"<?php if ($commentaire['texte'] != '') : ?> et a écrit : "<?= $commentaire['texte'] ?>"<?php endif; ?>
 
<?php endif; ?>
<?php endforeach ; ?>
<?php endforeach ; ?>
<?php endif; ?>
 
<?php if(count($liste_commentaires) > 0): ?>
Un Telabotaniste vient de répondre à un de vos commentaires ou une de vos propositions :
 
<?php foreach ($liste_commentaires as $co) : ?>
--> Observation #<?= $co['id_obs'] ?> - <<?= $co['lien']; ?>>
* <?= $co['nom_sci']; ?> observée le <?= $co['date']; ?> à <?= $co['lieu']; ?> *
<?php if ($co['nom_sel_co'] == '') : ?>
En réponse à votre commentaire "<?= $co['texte_co'] ?>",
<?php else : ?>
En réponse à votre proposition "<?= $co['nom_sci_co'] ?>"<?php if($co['texte_co'] != ''): ?> accompagnée du commentaire "<?= $co['texte_co'] ?>"<?php endif; ?>,
<?php endif; ?>
<?php foreach ($co['commentaires'] as $commentaire) : ?>
- <?= $commentaire['auteur'] ?> a écrit "<?= $commentaire['texte'] ?>"
<?php endforeach ; ?>
<?php endforeach ; ?>
<?php endif; ?>
 
Je souhaite poursuivre ma participation :
- En m'inscrivant gratuitement à Tela Botanica <http://www.tela-botanica.org/inscription>
- En utilisant Identiplante, l'outil de détermination collaborative en ligne <http://www.tela-botanica.org/page:validation_donnees>
- En gérant finement mes données avec le Carnet en Ligne <http://www.tela-botanica.org/page:cel>
 
Bonne continuation sur nos outils botaniques !
--
L'équipe de Tela Botanica
 
Ce message vous est envoyé par l'intermédiaire de l'application IdentiPlante du réseau Tela Botanica : http://www.tela-botanica.org/appli:identiplante
Si vous ne souhaitez plus recevoir ce courriel décochez la case dans vos préférences sur l'interface d'IdentiPlante.
/branches/v1.11-magnesium/scripts/modules/commentaires/del_mail_recapitulatif_commentaires.sh
New file
0,0 → 1,23
#!/bin/bash
#
# /etc/init.d/del_mail_recapitulatif : traitement des mails récapitulatifs d'identiplante
# - 2014-03-31 [Jean-Pascal Milcent] : ajout gestion de l'heure d'été et du décalage du temps de lancement
# - 2014-03-20 [Jean-Pascal Milcent] : ajout gestion de l'heure d'execution
# - 2012-07-03 [Aurélien Peronnet] : on
#
while true
do
HEURE=$(date "+%H")
# Si nous sommes à 18h nous lançons le script
if [ $HEURE -eq 18 ] ; then
logger "del::alerteMailCommentaires : lancement script"
sudo -u apitela /usr/local/bin/php-cli-55 -f /home/apitela/www/scripts/del/cli.php commentaires -a alerteMail
logger "del::alerteMailCommentaires : lancement script"
# Sleep 22h et non 1d : pour éviter pb changement heure d'été et le décalage de quelques secondes ajouté à chaque lancement
sleep 22h
else
# Tentative toutes les heures jusqu'à la bonne heure
logger "del::alerteMailCommentaires : tentative de lancement à $HEURE"
sleep 1h
fi
done
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:executable
+*
\ No newline at end of property
/branches/v1.11-magnesium/scripts/modules/maj/README-Index-maj1.txt
New file
0,0 → 1,106
1) accélérer le SELECT + ORDER BY sur del_observation
 
C'est un aspect important de la requête car il représente près de 33% du temps.
En local, près de 50ms sur 1.5s.
Solution:
 
CREATE INDEX `date_transmission` ON `cel_obs` (`date_transmission` DESC) COMMENT "nécessaire à l'ORDER BY utilisé dans la liste d'observation de DEL";
 
Malheureusement l'INDEX ne semble pas utilisable par la view (http://bugs.mysql.com/bug.php?id=43341 ?):
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM del_observation ORDER BY date_transmission desc LIMIT 0, 12;
# Using filesort (0.46s)
 
Allons donc invoquer directement la table, cependant, encore une fois l'INDEX n'est pas utilisé
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs ORDER BY date_transmission desc LIMIT 0, 12;
# Using filesort (0.46s)
 
Nous pouvons forcer l'index:
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs FORCE INDEX FOR ORDER BY (`date_transmission`) ORDER BY date_transmission desc LIMIT 0, 12;
# key=date_transmission (0.0s)
parfait !
 
 
Cependant, à peine ajoutons-nous le WHERE transmission = 1 que nous remontons à 0.60 seconde, et MySQL nous déconseille même l'utilisation de l'INDEX
qui, en effet, ralenti la requête (2.38s):
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs FORCE INDEX FOR ORDER BY (`date_transmission`) WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12;
# Using where; Using filesort (0.60s)
# Using where; (key=date_transmission) (2.38s) [avec FORCE INDEX]
Dans tous les cas ci-dessus, "rows" indique "291265", et le type "ref", sauf en "FORCE INDEX", mais comme nous l'avons vu, cela sous-optimal.
 
Le processing de transmission = 1 est très (très) couteux et c'est lui qui s'avère bloquant.
 
 
Tentons l'ajout d'un INDEX sur transmission ,tout TINYINT soit-il:
CREATE INDEX `transmission` ON `tb_cel`.`cel_obs` (`transmission`) COMMENT "nécessaire à CEL/DEL qui officie avec transmission = 1"
ANALYZE TABLE tb_cel.cel_obs;
EXPLAIN SELECT id_observation FROM tb_cel.cel_obs WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12;
# key = date_transmission, rows = 17, type = index (0.0s)
parfait...
et idem pour:
EXPLAIN SELECT * FROM del_observation ORDER BY date_transmission desc LIMIT 0, 12;
La vue prend donc bien en compte nos 2 INDEX.
 
MAIS... ...
EXPLAIN SELECT SQL_CALC_FOUND_ROWS id_observation FROM tb_cel.cel_obs WHERE transmission = 1 ORDER BY date_transmission desc LIMIT 0, 12;
# Using where; Using filesort, key = date_transmission, rows = 291265, type = index (0.80s)
... à peine retrouvons-nous SQL_CALC_FOUND_ROWS() que les perf' sont à nouveau déplorable puis MySQL itère sur les 300k lignes à transmission=1
 
Cependant:
EXPLAIN SELECT count(1) FROM del_observation;
# key = transmission, 291265 = 17, type = ref, MAIS... "Using index" (0.10s)
 
 
 
Ainsi pour une requête initiale:
SELECT SQL_CALC_FOUND_ROWS dob.id_observation, nom_sel, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, date_observation, dob.mots_cles_texte, date_transmission, dob.ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, dob.commentaire as dob_commentaire, dob.nt, dob.nom_sel_nn FROM del_observation AS dob LEFT JOIN del_utilisateur AS du ON dob.ce_utilisateur = du.id_utilisateur ORDER BY date_transmission desc LIMIT 0, 12
de 1.52s, la suppression SQL_CALC_FOUND_ROWS() nous amène à 0.00s + 0.10s de count() additionnel.
 
Sans INDEX sur transmission, la requête est de même rapidité, mais le count(1), trois fois plus long (0.30s)
Sans INDEX sur date_transmission, la requête est 4 fois plus longue (0.40s) et le count(1) identique (0.35s)
Bref, les 2 INDEX sont intéressant même si l'idéal est de rendre optionnel l'affichage du total des résultats
 
 
 
 
2) accélérer le GROUP en requête complexe:
SELECT SQL_CALC_FOUND_ROWS dob.id_observation, nom_sel, famille, ce_zone_geo, zone_geo, lieudit, station, milieu, date_observation, dob.mots_cles_texte, date_transmission, di.id_image, dob.ce_utilisateur, prenom, nom, courriel, dob.prenom_utilisateur, dob.nom_utilisateur, dob.courriel_utilisateur, nom_original, dob.commentaire AS dob_commentaire, dob.nt, dob.nom_sel_nn FROM del_observation AS dob LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = dob.ce_utilisateur LEFT JOIN del_obs_image AS doi ON doi.id_observation = dob.id_observation LEFT JOIN del_image AS di ON di.id_image = doi.id_image WHERE 1 GROUP BY doi.id_observation ORDER BY date_transmission desc LIMIT 0, 12
 
 
EXPLAIN SELECT dob.id_observation, di.id_image FROM cel_obs AS dob, cel_obs_images AS doi, cel_images AS di WHERE (transmission = 1 AND doi.id_observation = dob.id_observation AND di.id_image = doi.id_image) GROUP BY dob.id_observation ORDER BY date_transmission desc LIMIT 0, 12;
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+
| 1 | SIMPLE | doi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | di | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.doi.id_image | 1 | Using index |
| 1 | SIMPLE | dob | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.doi.id_observation | 1 | Using where |
+------+-------------+-------+--------+---------------------------+---------+---------+---------------------------+-------+----------------------------------------------+
# 0.04s
 
EXPLAIN SELECT dob.id_observation, di.id_image FROM del_observation AS dob, del_obs_image AS doi, del_image AS di WHERE (doi.id_observation = dob.id_observation AND di.id_image = doi.id_image) GROUP BY dob.id_observation ORDER BY date_transmission desc LIMIT 0, 12;
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
| 1 | SIMPLE | oi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | oi | ref | PRIMARY,observation,image | image | 8 | tb_cel.oi.id_image | 1 | |
| 1 | SIMPLE | i | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.oi.id_image | 1 | Using index |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
# 1.67s
 
 
En fait, del_image se définit comme suit:
select `i`.`id_image` AS `id_image`,if((char_length(`i`.`ce_utilisateur`) <> 32),cast(`i`.`ce_utilisateur` as unsigned),0) AS `ce_utilisateur`,`i`.`prenom_utilisateur` AS `prenom_utilisateur`,`i`.`nom_utilisateur` AS `nom_utilisateur`,`i`.`courriel_utilisateur` AS `courriel_utilisateur`,`i`.`hauteur` AS `hauteur`,`i`.`largeur` AS `largeur`,`i`.`date_prise_de_vue` AS `date_prise_de_vue`,`i`.`mots_cles_texte` AS `mots_cles_texte`,`i`.`commentaire` AS `commentaire`,`i`.`nom_original` AS `nom_original`,`i`.`date_modification` AS `date_modification`,`i`.`date_creation` AS `date_creation`,`i`.`publiable_eflore` AS `publiable_eflore` from ((`tb_cel`.`cel_images` `i` join `tb_cel`.`cel_obs_images` `oi` on((`i`.`id_image` = `oi`.`id_image`))) join `tb_cel`.`cel_obs` `o` on((`oi`.`id_observation` = `o`.`id_observation`))) where (`o`.`transmission` = '1')
 
soit déjà une double-jointure.
 
EXPLAIN SELECT id_observation, id_image FROM del_image AS di GROUP BY id_observation ORDER BY date_transmission desc LIMIT 0, 12;
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
| 1 | SIMPLE | oi | index | PRIMARY,observation,image | PRIMARY | 16 | NULL | 10314 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | i | eq_ref | PRIMARY,id_image | PRIMARY | 8 | tb_cel.oi.id_image | 1 | Using index |
| 1 | SIMPLE | o | eq_ref | PRIMARY,transmission | PRIMARY | 8 | tb_cel.oi.id_observation | 1 | Using where |
+------+-------------+-------+--------+---------------------------+---------+---------+--------------------------+-------+----------------------------------------------+
# 0.04s
/branches/v1.11-magnesium/scripts/modules/maj/201309-index-views.sql
New file
0,0 → 1,74
-- r1368/r1418: ajout de nom_referentiel à del_observations (attention, appelée "del_images" dans mysql-workbench)
DROP VIEW IF EXISTS `BASEDEL`.`del_image` ;
DROP TABLE IF EXISTS `BASEDEL`.`del_image`;
CREATE OR REPLACE VIEW `BASEDEL`.`del_image` AS
SELECT i.id_image, if((char_length(i.ce_utilisateur) <> 32),cast(i.ce_utilisateur as unsigned),0) AS ce_utilisateur, i.prenom_utilisateur, i.nom_utilisateur, i.courriel_utilisateur, i.hauteur, i.largeur, i.date_prise_de_vue, i.mots_cles_texte,
i.commentaire, i.nom_original, i.date_modification, i.date_creation, i.publiable_eflore, o.nom_referentiel
FROM `BASECEL`.`cel_images` AS i
INNER JOIN `BASECEL`.`cel_obs_images` AS oi ON (i.id_image = oi.id_image)
INNER JOIN `BASECEL`.`cel_obs` AS o ON (oi.id_observation = o.id_observation)
WHERE o.transmission = '1' ;
 
 
-- r1368/r1418: ajout de nom_referentiel à del_observations
DROP VIEW IF EXISTS `BASEDEL`.`del_observations` ;
DROP TABLE IF EXISTS `BASEDEL`.`del_observations`;
CREATE OR REPLACE VIEW `BASEDEL`.`del_observation` AS
SELECT id_observation, if((char_length(o.ce_utilisateur) <> 32),cast(o.ce_utilisateur as unsigned),0) AS ce_utilisateur, prenom_utilisateur, nom_utilisateur, courriel_utilisateur, nom_sel, nom_sel_nn, nom_ret, nom_ret_nn, nt, famille,
ce_zone_geo, zone_geo, lieudit, station, milieu, nom_referentiel,
date_observation, mots_cles_texte, commentaire,
date_creation, date_modification, date_transmission
FROM `BASECEL`.`cel_obs` AS o
WHERE transmission = '1' ;
 
-- r1376: ajout de la vue v_del_image (version modifiée de del_image
DROP VIEW IF EXISTS `BASEDEL`.`v_del_image` ;
DROP TABLE IF EXISTS `BASEDEL`.`v_del_image`;
CREATE OR REPLACE VIEW `BASEDEL`.`v_del_image` AS
SELECT o.*, i.id_image, i.ordre AS i_ordre,
i.ce_utilisateur AS i_ce_utilisateur, i.prenom_utilisateur AS i_prenom_utilisateur, i.nom_utilisateur AS i_nom_utilisateur, i.courriel_utilisateur AS i_courriel_utilisateur,
i.mots_cles_texte AS i_mots_cles_texte, hauteur, largeur, date_prise_de_vue, i.commentaire AS i_commentaire, nom_original, publiable_eflore
FROM `BASECEL`.cel_obs AS o
INNER JOIN `BASECEL`.cel_obs_images oi ON oi.id_observation = o.id_observation
INNER JOIN `BASECEL`.cel_images i ON i.id_image = oi.id_image
WHERE o.transmission = '1';
 
-- r1853 de CEL: ajout des INDEX (note: pas de commentaires sur les INDEX < MySQL 5.5)
CREATE INDEX `date_transmission` ON `BASECEL`.`cel_obs` (`date_transmission` DESC) /* COMMENT "nécessaire à l'ORDER BY utilisé dans la liste d'observation de DEL" */ ;
CREATE INDEX `transmission` ON `BASECEL`.`cel_obs` (`transmission`) /* COMMENT "nécessaire à CEL/DEL qui officie avec transmission = 1" */ ;
 
 
-- cleanup
DROP INDEX `tag_normalise_2` ON `BASEDEL`.`del_image_tag` ;
DROP INDEX `ce_image_2` ON `BASEDEL`.`del_image_tag` ;
 
 
 
DROP TABLE IF EXISTS `BASEDEL`.`del_image_stat`;
CREATE TABLE IF NOT EXISTS `BASEDEL`.`del_image_stat` (
`ce_image` BIGINT(20) NOT NULL COMMENT 'id_image (tb_cel.cel_images)',
`ce_protocole` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'un id de protocole',
`moyenne` FLOAT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'moyenne des votes pour une image et un protocole donné',
`nb_votes` SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'nombre de votes pour une image et un protocole donné',
`nb_tags` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'nombre de tags pictoflora pour une image donnée',
PRIMARY KEY (`ce_image`, `ce_protocole`),
KEY `ce_image` (`ce_image`),
KEY `ce_protocole` (`ce_protocole`, `moyenne` DESC),
KEY `nb_votes` (`nb_votes` DESC),
KEY `nb_tags` (`nb_tags` DESC)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'table de stockage des statistiques utilisées pour les tri de PictoFlora';
 
 
INSERT INTO `BASEDEL`.`del_image_stat` (
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags
FROM `BASECEL`.`cel_images` ci
LEFT JOIN
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes
FROM del_image_vote GROUP BY ce_image, ce_protocole ) AS divo
ON ci.id_image = divo.ce_image
LEFT JOIN
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag
GROUP BY ce_image ) AS dit
ON ci.id_image = dit.ce_image );
 
ALTER TABLE `BASEDEL`.`del_image_protocole` ADD `mots_cles` VARCHAR(600) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
/branches/v1.11-magnesium/scripts/modules/maj/201310-propret.sql
New file
0,0 → 1,103
DELIMITER |
 
-- initialisation (structure)
DROP PROCEDURE IF EXISTS update_struct;
CREATE PROCEDURE update_struct()
BEGIN
-- ajout de la colonne "proposition_retenue"
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S21' SELECT "colonne proposition_retenue déjà existante"; /* = Duplicate column name */
ALTER TABLE `BASEDEL`.`del_commentaire` ADD `proposition_retenue` INT(1) NOT NULL DEFAULT 0 COMMENT "La proposition \"validée\" une fois que l'auteur à validé et que le nom_sel de cel_obs a été modifié à partir du nom_sel de del_commentaire."; -- '
END;
|
 
-- petite functon bonus
DROP PROCEDURE IF EXISTS getCommentaires;
CREATE PROCEDURE getCommentaires(IN _s1 INT(20))
BEGIN
SELECT id_commentaire, ce_observation, ce_proposition, texte, nom_sel, nom_sel_nn, proposition_retenue FROM `BASEDEL`.`del_commentaire` WHERE ce_observation = _s1;
END;
|
 
-- procédure créant des tables temporaires permettant de faciliter le processus de MAJ
DROP PROCEDURE IF EXISTS rebuild_retenues;
CREATE PROCEDURE rebuild_retenues()
BEGIN
DROP TEMPORARY TABLE IF EXISTS _temp_having_retenue, _temp_multi_prop, _temp_mono_prop;
 
-- les observations déjà retenues (pour pouvoir être exclues des processus postérieurs)
CREATE TEMPORARY TABLE _temp_having_retenue ENGINE=MEMORY AS ( \
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` co WHERE proposition_retenue = 1);
 
-- observations ayant plusieurs propositions (= commentaire dont nom_sel IS NOT NULL)
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_multi_prop ENGINE=MEMORY AS ( \
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` co \
WHERE nom_sel IS NOT NULL \
AND proposition_retenue = 0 /* == ce_observation NOT IN (SELECT ce_proposition FROM _temp_having_retenue) */ \
GROUP BY ce_observation HAVING COUNT(id_commentaire) > 1); -- 1053
 
-- observations ayant une seule proposition (= commentaire dont nom_sel IS NOT NULL)
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_mono_prop ENGINE=MEMORY AS ( \
SELECT ce_observation FROM `BASEDEL`.`del_commentaire` \
WHERE nom_sel IS NOT NULL \
AND proposition_retenue = 0 /* == ce_observation NOT IN (SELECT ce_proposition FROM _temp_having_retenue) */ \
GROUP BY ce_observation HAVING COUNT(id_commentaire) = 1);
 
END;
|
 
DELIMITER ;
 
 
CALL update_struct();
-- table temporaire de tous les comptages (somme des votes pour chaque proposition de del_commentaire):
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_stats ENGINE=MEMORY AS ( SELECT ce_proposition, SUM(valeur) AS val FROM `BASEDEL`.`del_commentaire_vote` GROUP BY ce_proposition); -- 5912
-- propositions ayant eu au moins 1 vote positif non-anonyme
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_authvotepos ENGINE=MEMORY AS (SELECT DISTINCT ce_proposition FROM `BASEDEL`.`del_commentaire_vote` WHERE valeur = 1 AND LENGTH(ce_utilisateur) != 32 ORDER BY ce_proposition); -- 3794
CALL rebuild_retenues(); -- initialisation
 
-- core
-- SELECT ce_observation, id_commentaire FROM `BASEDEL`.`del_commentaire` co INNER JOIN _temp_multi_prop mp USING (ce_observation) INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) WHERE co.nom_sel_nn != 0 AND co.proposition_retenue = 0; -- 470, dont 464 ce_observation unique
-- SELECT ce_observation, id_commentaire FROM `BASEDEL`.`del_commentaire` co INNER JOIN _temp_multi_prop mp USING (ce_observation) INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel OR co.nom_sel_nn = ce.nom_sel_nn) WHERE co.nom_sel_nn != 0 AND co.proposition_retenue = 0; -- 520, dont 493 ce_observation unique
 
-- Étant donné les doublons nous pouvons commencer ou finir par filtrer sur les stats de vote
-- Comme il est plus simple de finir par ça, commençons correctement par les nom_sel comme ci-dessus avec ceux sans ambiguité:
-- C'est à dire observations dotées de multiples proposition dont une et une seule match exactement le nom-sel du carnet en ligne
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable1 ENGINE=MEMORY AS ( \
SELECT /* ce_observation, */ id_commentaire FROM `BASEDEL`.`del_commentaire` co \
INNER JOIN _temp_multi_prop mp USING (ce_observation) /* faisant partie des observations ayant plusieurs propositions */ \
INNER JOIN `BASECEL`.`cel_obs` ce ON mp.ce_observation = id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) \
WHERE co.nom_sel_nn != 0 \
GROUP BY co.ce_observation HAVING COUNT(co.id_commentaire) = 1 /* observations n'ayant pas d'ambiguité sur le commentaire correspondant au nom du carnet en ligne */ ); -- 335
 
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable1);
SELECT ROW_COUNT() AS "nb MAJ par match sur nom_sel sans ambiguité";
CALL rebuild_retenues();
 
 
-- Pour les observations ayant plusieurs nom-sel communs au carnet en ligne, il s'agit de ne sélectionner que celle
-- ayant le plus de vote (idéalement les propositions doublonnes ne devraient pas exister)
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable2 ENGINE=MEMORY AS ( \
SELECT co.ce_observation, id_commentaire, i1.val FROM `BASEDEL`.`del_commentaire` co \
INNER JOIN _temp_multi_prop mp USING (ce_observation) /* uniquement les multi-propositions, inclue le check sur _temp_having_retenue */ \
INNER JOIN (SELECT ce_proposition, val FROM _temp_stats) AS i1 ON i1.ce_proposition = co.id_commentaire /* et ayant des votes */ \
INNER JOIN `BASECEL`.`cel_obs` ce ON co.ce_observation = ce.id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) /* et un nom_sel acceptable */ \
WHERE co.nom_sel_nn != 0 \
GROUP BY ce_observation HAVING i1.val = MAX(i1.val) /* parmis les doublonnes, prendre la proposition ayant le plus de SUM(votes) */ \
ORDER BY ce_observation ); -- 249 (sans le processus précédent, moins autrement s'il l'on exclue les observation déjà "validée")
 
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable2);
SELECT ROW_COUNT() AS "nb MAJ par nombre de vote";
CALL rebuild_retenues();
 
-- Enfin, les observation n'ayant qu'une seule proposition sont passées comme retenues si
-- elles ont été notée positivement au moins une fois par un utilisateur authentifié
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_updatable3 ENGINE=MEMORY AS ( \
SELECT co.id_commentaire FROM `BASEDEL`.`del_commentaire` co \
INNER JOIN _temp_mono_prop AS i1 USING (ce_observation) /* l'inverse de _temp_multi_prop, inclue le check sur _temp_having_retenue */ \
INNER JOIN _temp_authvotepos ta ON co.id_commentaire = ta.ce_proposition /* et uniquement si faisant partie des commentaires doté d'un vote positif non-anonyme */ \
INNER JOIN `BASECEL`.`cel_obs` ce ON co.ce_observation = ce.id_observation AND (co.nom_sel = ce.nom_sel AND co.nom_sel_nn = ce.nom_sel_nn) \
WHERE co.nom_sel_nn != 0); -- 2216 sans test sur cel_obs, 2193 avec
 
UPDATE `BASEDEL`.`del_commentaire` co SET co.proposition_retenue = 1 WHERE co.id_commentaire IN (SELECT id_commentaire FROM _temp_updatable3);
SELECT ROW_COUNT() AS "nb MAJ ayant 1 seule proposition mais un vote non-anonyme positif";
CALL rebuild_retenues();
/branches/v1.11-magnesium/scripts/modules/maj/Makefile
New file
0,0 → 1,36
celdb = tb_cel_test
deldb = tb_del_test
 
# helper
do_subst = sed -e "1i--\n-- fichier d'origine: \"${1}\"\n" \
-e 's/`BASECEL`/`$(celdb)`/g' \
-e 's/`BASEDEL`/`$(deldb)`/g' \
$(1)
 
# default target
help:
@echo "make o_maj1 [celdb=<$(celdb)>] [deldb=<$(deldb)>]"
 
clean:
rm -f *.comp.sql
 
# maj1 contient: 201309-index-views.sql
o_maj1: fichiers_generes = $(addsuffix .comp.sql,$(filter-out clean,$?))
o_maj1: clean o_201309-index-views
cat $(fichiers_generes) > maj1.comp.sql
 
 
# maj1 contient: 201310-propret.sql
o_maj2: fichiers_generes = $(addsuffix .comp.sql,$(filter-out clean,$?))
o_maj2: clean o_201310-propret
cat $(fichiers_generes) > maj2.comp.sql
 
 
# SHOW INDEX FROM cel_obs WHERE Key_name = 'transmission';
# SHOW INDEX FROM cel_obs WHERE Key_name = 'date_transmission';
o_201309-index-views:
$(call do_subst,201309-index-views.sql) > $@.comp.sql
 
# SHOW COLUMNS FROM del_commentaire LIKE 'proposition_retenu';
o_201310-propret:
$(call do_subst,201310-propret.sql) > $@.comp.sql
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/v1.11-magnesium/scripts/modules/maj
New file
Property changes:
Added: svn:ignore
+o_*.comp.sql
/branches/v1.11-magnesium/scripts/configurations/config.defaut.ini
New file
0,0 → 1,94
; Encodage : UTF-8
 
; +------------------------------------------------------------------------------------------------------+
; URLs
; Le séparateur utilisé par le framework lorsqu'il génère des URL pour séparer les arguments.
; Pour remettre les valeurs par défaut, utitliser : "php:ini_get('arg_separator.output')"
url_arg_separateur_sortie = "&"
 
; +------------------------------------------------------------------------------------------------------+
; Info sur l'application
info.nom = Scripts d'identiplante
; Abréviation de l'application
info.abr = del-services
; Version du Framework nécessaire au fonctionnement de cette application
info.framework.version = 0.3
;Encodage de l'application
encodage_appli = "UTF-8"
 
; +------------------------------------------------------------------------------------------------------+
; Débogage
; Indique si oui ou non on veut afficher le débogage.
debogage = true
; Indique sous quelle forme les méssages de débogage doivent s'afficher :
; - "php:Debug::MODE_ECHO" : le message est affiché en utilisant echo
; - "php:Debug::MODE_NOTICE" : le message est affiché en utilisant une erreur de type notice
; - "php:Debug::MODE_ENTETE_HTTP" : les messages sont envoyés dans un entête HTTP "X_REST_DEBOGAGE".
; - "Autre valeur" : les messages sont formatés puis retournés par la méthode de débogage utilisée.
debogage_mode = "php:Debug::MODE_ECHO"
; Indique si oui ou non on veut lancer le chronométrage
chronometrage = false
 
 
; +------------------------------------------------------------------------------------------------------+
; Paramètrage de la base de données.
; Abstraction de la base de données.
bdd_abstraction = pdo
; Protocole de la base de données.
bdd_protocole = mysql
; Nom du serveur de bases de données.
bdd_serveur = localhost
; Nom de l'utilisateur de la base de données.
bdd_utilisateur = ""
; Mot de passe de l'utilisateur de la base de données.
bdd_mot_de_passe = ""
; Nom de la base de données principale.
bdd_nom = "del"
; Encodage de la base de données principale au format base de données (ex. pour l'utf-8 ne pas mettre le tiret!).
bdd_encodage = "utf8"
 
; +------------------------------------------------------------------------------------------------------+
; Standard utilisé pour les codes de langue
langue_source = "ISO-639-1";
; Standard utilisé pour les codes de zones géographiques
zone_geo_source = "ISO-3166-1";
 
; +------------------------------------------------------------------------------------------------------+
; Squelette d'Url de la fiche de l'observation à utiliser avec sprintf
url_fiche_observation = "http://www.tela-botanica.org/appli:identiplante/obs%s";
 
; +------------------------------------------------------------------------------------------------------+
; TEMPLATES d'URL
; Squelette d'Url du service permettant de récupérer les infos d'un utilisateur (à utiliser avec sprintf)
url_service_utilisateur = "http://www.tela-botanica.org/service:annuaire:Utilisateur/%s";
; Squelette d'Url du service de messagerie de l'annuaire (à utiliser avec sprintf)
url_service_messagerie = "{ref:url_service_utilisateur}/message";
 
; +------------------------------------------------------------------------------------------------------+
; EMAILS
; Adresse de l'expéditeur des messages récapitulatifs
adresse_expediteur_recapitulatif = "identiplante-determination@tela-botanica.org"
; Adresse à mettre dans le "reply-to" des messages récapitulatifs pour éviter des réponses impromptues
adresse_reponse = "identiplante-ne-pas-repondre@tela-botanica.org"
; Courriels destinataire récapitulatif emails commentaires
email_recap = "contact-identiplante@tela-botanica.org"
; Adresse destinataire des messages récapitulatifs de détermination
adresse_destinataire_recapitulatif_determination = "determination_plantes@tela-botanica.org"
 
; ATTENTION : les sujets des messages doivent comporter moins de 40 caractères pour éviter les pb de compatibilités
; Sujet du message de récapitulatif d'identiplante
titre_message_recapitulatif = "Commentaires et propositions du %s";
; Sujet du message de récapitulatif des obs à déterminer
titre_message_recapitulatif_obs = "Observations à déterminer du %s"
 
; +------------------------------------------------------------------------------------------------------+
; Espaces de noms
cheminDelCommun = '../commun'
 
; +------------------------------------------------------------------------------------------------------+
; tables et bases de données des réferentiels
referentiels = "bdtfx,bdtxa,apd,isfan";
table_referentiel_bdtfx = "tb_eflore.bdtfx_v2_01";
table_referentiel_bdtxa = "tb_eflore.bdtxa_v1_01";
table_referentiel_apd = "tb_eflore.apd_v3_4_0";
table_referentiel_isfan = "tb_eflore.isfan_v2013";
/branches/v1.11-magnesium/scripts/configurations
New file
Property changes:
Added: svn:ignore
+config.ini
+config.ini.ini
/branches/v1.11-magnesium/scripts/framework.defaut.php
New file
0,0 → 1,6
<?php
// Inclusion du Framework
// Renomer ce fichier en "framework.php"
// Indiquer ci-dessous le chemin absolu vers le fichier autoload.inc.php de la bonne version du Framework
require_once '/home/www/commun/framework/0.3/Framework.php';
?>
/branches/v1.11-magnesium/scripts/bibliotheque/Conteneur.php
New file
0,0 → 1,110
<?php
// declare(encoding='UTF-8');
/**
* Le conteneur encapsule les classes servant aux scripts.
* Il gère leur instanciation, ainsi que la récupération des paramètres depuis le fichier de configuration.
*
* @category DEL
* @package Scripts
* @subpackage Bibliotheque
* @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
class Conteneur {
 
protected $parametres = array();
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;
}
 
/**
* Permet d'obtenir un objet GestionBdd.
*/
public function getBdd() {
if (!isset($this->partages['Bdd'])){
$this->partages['Bdd'] = new Bdd();
}
return $this->partages['Bdd'];
}
 
/**
* Permet d'obtenir un objet RestClient.
*/
public function getRestClient() {
if (!isset($this->partages['RestClient'])) {
$this->partages['RestClient'] = new RestClient();
}
return $this->partages['RestClient'];
}
 
/**
* Permet d'obtenir un objet SquelettePhp.
*/
public function getSquelettePhp() {
if (!isset($this->partages['SquelettePhp'])) {
$this->partages['SquelettePhp'] = new SquelettePhp();
}
return $this->partages['SquelettePhp'];
}
 
/**
* Permet d'obtenir un objet Messagerie.
*/
public function getMessagerie() {
if (!isset($this->partages['Messagerie'])) {
$this->partages['Messagerie'] = new TelaBotanica\Del\Commun\Messagerie($this);
}
return $this->partages['Messagerie'];
}
}
/branches/v1.11-magnesium/scripts/bibliotheque/Messagerie.php
New file
0,0 → 1,141
<?php
// declare(encoding='UTF-8');
/**
* Classe Messagerie permettant d'envoyer des courriels (s'inspire de ce qui est fait dans l'appli Annuaire).
*
* @category DEL
* @package Scripts
* @subpackage Bibliotheque
* @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 Messagerie {
 
private $conteneur = null;
 
public function __construct($conteneur) {
$this->conteneur = $conteneur;
}
 
public function envoyerHtml($destinataires, $sujet, $messageHtml, $messageTxt = '') {
if ($messageTxt == '') {
$messageSansHtml = $this->supprimerHtml($messageHtml);
$messageTxt = $this->nettoyerTxt($messageSansHtml);
}
$encodage = $this->conteneur->getParametre('encodage_appli');
mb_internal_encoding($encodage);
// tentative d'acceptation des sujets longs (+ 76 caractères) par les clients mail tatillons
$sujetEncode = mb_encode_mimeheader($sujet, mb_internal_encoding(), "B", "\n");
 
$emailExpediteur = $this->conteneur->getParametre('adresse_expediteur_recapitulatif');
$dateHeure = date('r');
$uid = md5(uniqid(rand()));
$limite = "_----------=_parties_$uid";
$eol = "\n";// ATTENTION : avec Qmail laisser seulement \n car il remplace automatiquement par \r\n
 
// Définition d'un mail en texte simple et html
// multipart/alternative signifie même contenu de la forme la plus simple à la plus complexe
$entetes = 'X-Sender: <http://www.tela-botanica.org>'.$eol.
'X-Mailer: PHP-DEL-SCRIPTS'.$eol.
'X-auth-smtp-user: '.$emailExpediteur.$eol.
'X-abuse-contact: '.$emailExpediteur.$eol.
'Date: '.$dateHeure.$eol.
'From: '.$emailExpediteur.$eol.
'Reply-To: '.$this->conteneur->getParametre('adresse_reponse').$eol.
'MIME-Version: 1.0'.$eol.
"Content-Type: multipart/alternative; boundary=\"$limite\";".$eol.$eol;
 
// le message en html est préféré s'il est lisible
$contenu =
"--$limite".$eol.
"Content-Type: text/plain; charset=\"$encodage\";".$eol.
"Content-Transfer-Encoding: 8bit;".$eol.$eol.
"$messageTxt".$eol.$eol.
"--$limite".$eol.
"Content-Type: text/html; charset=\"$encodage\";".$eol.
"Content-Transfer-Encoding: 8bit;".$eol.$eol.
"$messageHtml".$eol.
"--".$limite."--".$eol.$eol;
 
$envoieStatut = $this->envoyer($destinataires, $entetes, $sujetEncode, $contenu);
 
return $envoieStatut;
}
 
public function envoyerTxt($destinataires, $sujet, $messageTxt) {
$emailExpediteur = $this->conteneur->getParametre('adresse_expediteur_recapitulatif');
$encodage = $this->conteneur->getParametre('encodage_appli');
mb_internal_encoding($encodage);
$sujetEncode = mb_encode_mimeheader($sujet, mb_internal_encoding(), "B", "\n");
$dateHeure = date('r');
$eol = "\n";// ATTENTION : avec Qmail laisser seulement \n car il remplace automatiquement par \r\n
 
// Définition d'un mail en texte simple et html
// multipart/alternative signifie même contenu de la forme la plus simple à la plus complexe
$entetes = 'X-Sender: <http://www.tela-botanica.org>'.$eol.
'X-Mailer: PHP-DEL-SCRIPTS'.$eol.
'X-auth-smtp-user: '.$emailExpediteur.$eol.
'X-abuse-contact: '.$emailExpediteur.$eol.
'Date: '.$dateHeure.$eol.
'From: '.$emailExpediteur.$eol.
'Reply-To: '.$this->conteneur->getParametre('adresse_reponse').$eol.
'MIME-Version: 1.0'.$eol.
"Content-Type: text/plain; charset=\"$encodage\";".$eol.$eol;
 
// le message en html est préféré s'il est lisible
$contenu = $messageTxt.$eol.$eol;
 
$envoieStatut = $this->envoyer($destinataires, $entetes, $sujetEncode, $contenu);
 
return $envoieStatut;
}
 
private function envoyer($destinataires, $entetes, $sujet, $contenu) {
$envoieStatut = mail(
$destinataires,
$sujet,
$contenu,
$entetes
);
return $envoieStatut;
}
 
/** Transforme automatiquement le message html en message txt.
*
* Réalise un stip_tags et avant ça un remplacement des liens sur mesure pour les mettre au format email txt.
*/
private function supprimerHtml($messageHtml) {
$html = $this->ajouterHrefDansBalise($messageHtml);
$messageSansHtml = strip_tags($html);
return $messageSansHtml;
}
 
/**
* Extrait la valeur de l'attribut href des balises HTML de liens (a) et ajoute le lien entre
* chevrons (<>) dans le contenu de la balise "a".
*/
private function ajouterHrefDansBalise($html) {
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
foreach ($dom->getElementsByTagName('a') as $node) {
if ($node->hasAttribute( 'href' )) {
$href = $node->getAttribute('href');
$node->nodeValue = $node->nodeValue." ( $href )";
}
}
$html = $dom->saveHtml();
return $html;
}
 
private function nettoyerTxt($message) {
$messageSansTab = preg_replace('/\t+/', '', $message);
$messageSansRetourMultiple = preg_replace('/\n+/', "\n", $messageSansTab);
$messageTxt = html_entity_decode($messageSansRetourMultiple, null, 'UTF-8');
return $messageTxt;
}
}
/branches/v1.11-magnesium/scripts/bibliotheque/DelScript.php
New file
0,0 → 1,27
<?php
// declare(encoding='UTF-8');
/**
* Classe mère des scripts DEL
*
* @category DEL
* @package Scripts
* @subpackage Bibliotheque
* @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>
*/
abstract class DelScript extends Script {
 
private $projetNom = null;
 
public function getProjetNom() {
return $this->projetNom;
}
 
protected function initialiserProjet($projetNom) {
$this->projetNom = $projetNom;
}
}
/branches/v1.11-magnesium/scripts/cli.php
New file
0,0 → 1,60
<?php
// Encodage : UTF-8
// +-------------------------------------------------------------------------------------------------------------------+
/**
* Initialise le chargement et l'exécution des scripts
*
* Lancer ce fichier en ligne de commande avec :
* <code>/opt/lampp/bin/php cli.php mon_script -a test</code>
*
//Auteur original :
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
* @copyright Tela-Botanica 1999-2008
* @licence GPL v3 & CeCILL v2
* @version $Id$
*/
// +-------------------------------------------------------------------------------------------------------------------+
 
 
 
// Autoloader pour les namespaces, à base de routes
function __autoload($nom_classe) {
//echo "AUTOLOAD\n";
$dernierAS = strrpos($nom_classe, "\\");
$ns = substr($nom_classe, 0, $dernierAS);
$nom = substr($nom_classe, strrpos($nom_classe, "\\") + 1);
//echo "Recherche : $nom / $ns\n";
// Routes selon les namespaces
$routes = array(
'TelaBotanica\Del\Commun' => Config::get('cheminDelCommun')
);
if (array_key_exists($ns, $routes)) {
//echo "Route trouvée: " . $routes[$ns] . "\n";
$fichier_a_inclure = dirname(__FILE__) . DS . $routes[$ns] . DS . $nom . '.php';
if (file_exists($fichier_a_inclure)) {
include_once $fichier_a_inclure;
return null;
}
}
}
 
// Le fichier Framework.php du Framework de Tela Botanica doit être appelée avant tout autre chose dans l'application.
// Sinon, rien ne sera chargé.
// Chemin du fichier chargeant le framework requis
$framework = dirname(__FILE__).DIRECTORY_SEPARATOR.'framework.php';
if (!file_exists($framework)) {
$e = "Veuillez paramétrer l'emplacement et la version du Framework dans le fichier $framework";
trigger_error($e, E_USER_ERROR);
} else {
// Inclusion du Framework
require_once $framework;
 
// Ajout d'information concernant cette application
Framework::setCheminAppli(__FILE__);// Obligatoire
Framework::setInfoAppli(Config::get('info'));
 
// Initialisation et lancement du script appelé en ligne de commande
Cli::executer();
}
?>
/branches/v1.11-magnesium/scripts/tb_del.sh
New file
0,0 → 1,65
#!/bin/sh
 
### BEGIN INIT INFO
# Provides: tb_del
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $all
# Should-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start/stop scripts maintenance et emails DEL
# Description: Start/stop les scripts de maintenance et d'envoie d'emails de DEL.
### END INIT INFO
 
#/etc/init.d/
#
# Aurélien Peronnet [7 avril 2011]
# Service de lancement des scripts DEL
#
case "$1" in
 
start)
 
echo "Demarrage de del_mail_recapitulatif_obs :"
nohup /usr/local/sbin/del_mail_recapitulatif_obs 1>/dev/null 2>/dev/null &
 
echo "Demarrage de del_mail_recapitulatif_commentaires :"
nohup /usr/local/sbin/del_mail_recapitulatif_commentaires 1>/dev/null 2>/dev/null &
 
;;
 
stop)
 
echo "Arret de del_mail_recapitulatif_obs"
PID=`ps -eaf | grep del_mail_recapitulatif_obs | grep -v grep | tr -s ' ' | cut -d' ' -f2 | head -n1`
kill -9 ${PID}
 
echo "Arret de del_mail_recapitulatif_commentaires"
PID=`ps -eaf | grep del_mail_recapitulatif_commentaires | grep -v grep | tr -s ' ' | cut -d' ' -f2 | head -n1`
kill -9 ${PID}
 
;;
 
status)
 
echo -n "Voici les PID du processus del_mail_recapitulatif_obs :"
PID=`ps -eaf | grep del_mail_recapitulatif_obs | grep -v grep | tr -s ' ' | cut -d' ' -f2 | head -n1`
echo ${PID}
 
echo -n "Voici les PID du processus del_mail_recapitulatif_commentaires :"
PID=`ps -eaf | grep del_mail_recapitulatif_commentaires | grep -v grep | tr -s ' ' | cut -d' ' -f2 | head -n1`
echo ${PID}
 
;;
 
 
*)
 
echo "Usage: {start|stop|status}"
 
exit 1
 
;;
 
esac
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:executable
+*
\ No newline at end of property
/branches/v1.11-magnesium/scripts/.
New file
Property changes:
Added: svn:mergeinfo
Merged /branches/v1.0-hydrogene/scripts:r1507
Merged /branches/v1.2-lithium:r1709-1736
Merged /branches/v1.3-beryllium/scripts:r1741-1746,1751,1768,1771
Merged /branches/v1.2-lithium/scripts:r1682-1736
Added: svn:ignore
+framework.php