Subversion Repositories eFlore/Applications.del

Rev

Rev 1444 | Rev 1452 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1444 Rev 1451
1
<?php
1
<?php
2
/**
2
/**
3
 * Le web service observations récupère toutes les observations et, pour chacune d'elle, les
3
 * Le web service observations récupère toutes les observations et, pour chacune d'elle, les
4
 * images qui lui sont associées.
4
 * images qui lui sont associées.
5
 * Basée sur la classe antérieure dans ListeObservations.php de
5
 * Basée sur la classe antérieure dans ListeObservations.php de
6
 * Grégoire Duché et Aurélien Peronnet
6
 * Grégoire Duché et Aurélien Peronnet
7
 * (formaterVote, formaterDeterminations, chargerNombreCommentaire, chargerVotes, chargerDeterminations)
7
 * (formaterVote, formaterDeterminations, chargerNombreCommentaire, chargerVotes, chargerDeterminations)
8
 *
8
 *
9
 * @category	php 5.2
9
 * @category	php 5.2
10
 * @package		del
10
 * @package		del
11
 * @author		Raphaël Droz <raphael@tela-botanica.org>
11
 * @author		Raphaël Droz <raphael@tela-botanica.org>
12
 * @copyright	Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
12
 * @copyright	Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
13
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
13
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
14
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
14
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
15
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
15
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
16
 *
16
 *
17
 * TODO:
17
 * TODO:
18
 * PDO::prepare()
18
 * PDO::prepare()
19
 * Sphinx pour auteur, genre, ns, commune, tag et masque-général
19
 * Sphinx pour auteur, genre, ns, commune, tag et masque-général
20
 */
20
 */
21
 
21
 
22
define('_LISTE_OBS_MAX_RESULT_LIMIT', 1000);
22
define('_LISTE_OBS_MAX_RESULT_LIMIT', 1000);
23
define('_LISTE_OBS_MAX_ID_OBS', 10e7);
23
define('_LISTE_OBS_MAX_ID_OBS', 10e7);
24
// SELECT MAX(num_taxonomique) FROM bdtfx_v2_00;
24
// SELECT MAX(num_taxonomique) FROM bdtfx_v2_00;
25
define('_LISTE_OBS_MAX_BDTFX_NT', 44378 + 1000);
25
define('_LISTE_OBS_MAX_BDTFX_NT', 44378 + 1000);
26
// SELECT MAX(num_nom) FROM bdtfx_v2_00;
26
// SELECT MAX(num_nom) FROM bdtfx_v2_00;
27
define('_LISTE_OBS_MAX_BDTFX_NN', 103386 + 10000);
27
define('_LISTE_OBS_MAX_BDTFX_NN', 103386 + 10000);
28
 
28
 
29
restore_error_handler();
29
restore_error_handler();
30
restore_exception_handler();
30
restore_exception_handler();
31
error_reporting(E_ALL);
31
error_reporting(E_ALL);
32
 
32
 
33
class ListeObservations2 {
33
class ListeObservations {
34
 
34
 
35
	private $conteneur;
35
	private $conteneur;
36
	private $gestionBdd;
36
	private $gestionBdd;
37
	private $bdd;
37
	private $bdd;
38
	private $parametres = array();
38
	private $parametres = array();
39
	private $ressources = array();
39
	private $ressources = array();
40
 
40
 
41
	static $tris_possibles = array('date_observation');
41
	static $tris_possibles = array('date_observation');
42
	// paramètres autorisés
42
	// paramètres autorisés
43
	static $parametres_autorises = array('masque', 'masque.famille', 'masque.nn', 'masque.referentiel', // taxon
43
	static $parametres_autorises = array('masque', 'masque.famille', 'masque.nn', 'masque.referentiel', // taxon
44
										 'masque.genre', 'masque.espece', 'masque.ns', // nom_sel
44
										 'masque.genre', 'masque.espece', 'masque.ns', // nom_sel
45
										 'masque.commune', 'masque.departement', 'masque.id_zone_geo', // loc
45
										 'masque.commune', 'masque.departement', 'masque.id_zone_geo', // loc
46
										 'masque.auteur', 'masque.date', 'masque.tag', 'masque.type', // autres
46
										 'masque.auteur', 'masque.date', 'masque.tag', 'masque.type', // autres
47
										 // tri, offset
47
										 // tri, offset
48
										 'navigation.depart', 'navigation.limite',
48
										 'navigation.depart', 'navigation.limite',
49
										 'tri', 'ordre', // TODO: 'total=[yes]', 'fields=[x,y,...]'
49
										 'tri', 'ordre', // TODO: 'total=[yes]', 'fields=[x,y,...]'
50
										 // TODO: masque.annee, masque.insee (!= departement)
50
										 // TODO: masque.annee, masque.insee (!= departement)
51
	);
51
	);
52
 
52
 
53
	static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
53
	static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
54
								   'tri' => 'date_transmission', 'ordre' => 'desc');
54
								   'tri' => 'date_transmission', 'ordre' => 'desc');
55
 
55
 
56
	static $sql_fields_liaisons = array(
56
	static $sql_fields_liaisons = array(
57
		'dob' => array('id_observation', 'nom_sel AS `determination.ns`', 'nt AS `determination.nt`',
57
		'dob' => array('id_observation', 'nom_sel AS `determination.ns`', 'nt AS `determination.nt`',
58
					   'nom_sel_nn AS `determination.nn`', 'famille AS `determination.famille`',
58
					   'nom_sel_nn AS `determination.nn`', 'famille AS `determination.famille`',
59
					   'nom_referentiel AS `determination.referentiel`',
59
					   'nom_referentiel AS `determination.referentiel`',
60
					   'ce_zone_geo AS id_zone_geo', 'zone_geo', 'lieudit',
60
					   'ce_zone_geo AS id_zone_geo', 'zone_geo', 'lieudit',
61
					   'station', 'milieu', 'date_observation', 'mots_cles_texte', 'date_transmission',
61
					   'station', 'milieu', 'date_observation', 'mots_cles_texte', 'date_transmission',
62
					   'ce_utilisateur AS `auteur.id`', 'prenom_utilisateur AS `auteur.prenom`',
62
					   'ce_utilisateur AS `auteur.id`', 'prenom_utilisateur AS `auteur.prenom`',
63
					   'nom_utilisateur AS `auteur.nom`', 'courriel_utilisateur AS observateur',
63
					   'nom_utilisateur AS `auteur.nom`', 'courriel_utilisateur AS observateur',
64
					   'commentaire'),
64
					   'commentaire'),
65
		'di' => array('id_image', 'date_prise_de_vue AS `date`', 'hauteur',/* 'largeur','nom_original' // apparemment inutilisés */),
65
		'di' => array('id_image', 'date_prise_de_vue AS `date`', 'hauteur',/* 'largeur','nom_original' // apparemment inutilisés */),
66
		'du' => array('prenom', 'nom', 'courriel'),
66
		'du' => array('prenom', 'nom', 'courriel'),
67
		'dc' => array('commentaire')
67
		'dc' => array('commentaire')
68
	);
68
	);
69
 
69
 
70
 
70
 
71
	public function __construct(Conteneur $conteneur = null) {
71
	public function __construct(Conteneur $conteneur = null) {
72
		$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
72
		$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
73
		$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
73
		$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
74
		$this->conteneur->chargerConfiguration('config_observations.ini');
74
		$this->conteneur->chargerConfiguration('config_observations.ini');
75
		$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
75
		$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
76
		$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
76
		$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
77
		$this->navigation = $conteneur->getNavigation();
77
		$this->navigation = $conteneur->getNavigation();
78
		$this->masque = $conteneur->getMasque();
78
		$this->masque = $conteneur->getMasque();
79
		$this->gestionBdd = $conteneur->getGestionBdd();
79
		$this->gestionBdd = $conteneur->getGestionBdd();
80
		$this->bdd = $this->gestionBdd->getBdd();
80
		$this->bdd = $this->gestionBdd->getBdd();
81
	}
81
	}
82
 
82
 
83
	static function reformateObservation($obs, $url_pattern = '') {
83
	static function reformateObservation($obs, $url_pattern = '') {
84
		$obs = array_map('array_filter', $obs);
84
		$obs = array_map('array_filter', $obs);
85
		$obs_merged = array();
85
		$obs_merged = array();
86
		foreach($obs as $o) {
86
		foreach($obs as $o) {
87
			$id = $o['id_observation'];
87
			$id = $o['id_observation'];
88
 
88
 
89
			// car auteur.id peut être un email, un hash, ou un annuaire_tela.U_ID
89
			// car auteur.id peut être un email, un hash, ou un annuaire_tela.U_ID
90
			// mais dans les deux premiers cas SELECT courriel AS observateur fait déjà l'affaire
90
			// mais dans les deux premiers cas SELECT courriel AS observateur fait déjà l'affaire
91
			if(!is_numeric($o['auteur.id'])) $o['auteur.id'] = "0";
91
			if(!is_numeric($o['auteur.id'])) $o['auteur.id'] = "0";
92
			if(!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
92
			if(!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
93
 
93
 
94
			$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
94
			$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
95
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
95
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
96
			unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
96
			unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
97
			if(!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
97
			if(!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
98
			$obs_merged['"' . $id . '"']['images'][] = $image;
98
			$obs_merged['"' . $id . '"']['images'][] = $image;
99
		}
99
		}
100
		return $obs_merged;
100
		return $obs_merged;
101
	}
101
	}
102
 
102
 
103
	// utilisée uniquement par Observation.php
103
	// utilisée uniquement par Observation.php
104
	static function reformateObservationSimpleIndex($obs, $url_pattern = '') {
104
	static function reformateObservationSimpleIndex($obs, $url_pattern = '') {
105
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
105
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
106
		// conserver les valeurs vides (pour les phptests notamment, ou non)
106
		// conserver les valeurs vides (pour les phptests notamment, ou non)
107
		$obs = array_map('array_filter', $obs);
107
		$obs = array_map('array_filter', $obs);
108
		$obs_merged = array();
108
		$obs_merged = array();
109
		foreach($obs as $o) {
109
		foreach($obs as $o) {
110
			$id = $o['id_observation'];
110
			$id = $o['id_observation'];
111
			$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
111
			$image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
112
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
112
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
113
			unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
113
			unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
114
			if(!isset($obs_merged[$id])) $obs_merged[$id] = $o;
114
			if(!isset($obs_merged[$id])) $obs_merged[$id] = $o;
115
			$obs_merged[$id]['images'][$image['id_image']] = $image;
115
			$obs_merged[$id]['images'][$image['id_image']] = $image;
116
		}
116
		}
117
		return $obs_merged;
117
		return $obs_merged;
118
	}
118
	}
119
 
119
 
120
 
120
 
121
	// utilisée uniquement par ListeImages.php
121
	// utilisée uniquement par ListeImages.php
122
	static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
122
	static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
123
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
123
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
124
		// conserver les valeurs vides (pour les phptests notamment, ou non)
124
		// conserver les valeurs vides (pour les phptests notamment, ou non)
125
		// $obs = array_map('array_filter', $obs);
125
		// $obs = array_map('array_filter', $obs);
126
		$obs_merged = $obs_keyed_by_id_image = array();
126
		$obs_merged = $obs_keyed_by_id_image = array();
127
		foreach($obs as $o) {
127
		foreach($obs as $o) {
128
			// ceci nous complique la tâche pour le reste du processing...
128
			// ceci nous complique la tâche pour le reste du processing...
129
			$id = $o['jsonindex'];
129
			$id = $o['jsonindex'];
130
			// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
130
			// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
131
			// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
131
			// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
132
			// mais tout deux partage leur référence à "protocole"
132
			// mais tout deux partage leur référence à "protocole"
133
			$image = array(
133
			$image = array(
134
				'id_image' => $o['id_image'],
134
				'id_image' => $o['id_image'],
135
				'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
135
				'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
136
				'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
136
				'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
137
			);
137
			);
138
			unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
138
			unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
139
			if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
139
			if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
140
			$obs_merged[$id]['observation'] = $o;
140
			$obs_merged[$id]['observation'] = $o;
141
			$obs_merged[$id]['protocoles_votes'] = array();
141
			$obs_merged[$id]['protocoles_votes'] = array();
142
			
142
			
143
			$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
143
			$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
144
		}
144
		}
145
 
145
 
146
		return array($obs_merged,$obs_keyed_by_id_image);
146
		return array($obs_merged,$obs_keyed_by_id_image);
147
	}
147
	}
148
 
148
 
149
	/**
149
	/**
150
	 * Méthode principale de la classe.
150
	 * Méthode principale de la classe.
151
	 * Lance la récupération des images dans la base et les place dans un objet ResultatService
151
	 * Lance la récupération des images dans la base et les place dans un objet ResultatService
152
	 * pour l'afficher.
152
	 * pour l'afficher.
153
	 * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
153
	 * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
154
	 * @param array $parametres les paramètres situés après le ? dans l'url
154
	 * @param array $parametres les paramètres situés après le ? dans l'url
155
	 **/	
155
	 **/	
156
	public function consulter($ressources, $parametres) {
156
	public function consulter($ressources, $parametres) {
157
		// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
157
		// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
158
		$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
158
		$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
159
 
159
 
160
		// toujours nécessaire puisque nous tapons sur v_del_image qui INNER JOIN cel_images, or nous voulons certes
160
		// toujours nécessaire puisque nous tapons sur v_del_image qui INNER JOIN cel_images, or nous voulons certes
161
		// toutes les images, mais nous voulons $limite observations uniques.
161
		// toutes les images, mais nous voulons $limite observations uniques.
162
		$req['groupby'][] = 'vdi.id_observation';
162
		$req['groupby'][] = 'vdi.id_observation';
163
 
163
 
164
		$db = $this->bdd;
164
		$db = $this->bdd;
165
 
165
 
166
		// filtrage de l'INPUT
166
		// filtrage de l'INPUT
167
		$params = self::requestFilterParams($parametres, self::$parametres_autorises, $this->conteneur);
167
		$params = self::requestFilterParams($parametres, self::$parametres_autorises, $this->conteneur);
168
		// ... et paramètres par défaut
168
		// ... et paramètres par défaut
169
		$params = array_merge(self::$default_params, $params);
169
		$params = array_merge(self::$default_params, $params);
170
 
170
 
171
		// création des contraintes (masques)
171
		// création des contraintes (masques)
172
		self::sqlAddConstraint($params, $db, $req, $this->conteneur);
172
		self::sqlAddConstraint($params, $db, $req, $this->conteneur);
173
		self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
173
		self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
174
 
174
 
175
		// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
175
		// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
176
		$idobs_tab = self::getIdObs($params, $req, $db);
176
		$idobs_tab = self::getIdObs($params, $req, $db);
177
		// idobs est une liste (toujours ordonnée) des id d'observations recherchées
177
		// idobs est une liste (toujours ordonnée) des id d'observations recherchées
178
		$idobs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'), $idobs_tab));
178
		$idobs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'), $idobs_tab));
179
 
179
 
180
		if($idobs) {
180
		if($idobs) {
181
			$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
181
			$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
182
 
182
 
183
			// 2) récupération des données nécessaires pour ces observations (obs + images)
183
			// 2) récupération des données nécessaires pour ces observations (obs + images)
184
			// ici les champs récupérés sont issus de self::$sql_fields_liaisons mais sans préfixes
184
			// ici les champs récupérés sont issus de self::$sql_fields_liaisons mais sans préfixes
185
			// car tout provient de v_del_image
185
			// car tout provient de v_del_image
186
			$obs_unfmt = self::getInfos($idobs, $db);
186
			$obs_unfmt = self::getInfos($idobs, $db);
187
 
187
 
188
			// 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
188
			// 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
189
			$observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
189
			$observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
190
 
190
 
191
			// 4) récupération des données nécessaires pour ces observations (commentaires + votes)
191
			// 4) récupération des données nécessaires pour ces observations (commentaires + votes)
192
			// modifie $observations
192
			// modifie $observations
193
			$this->configurer();
193
			$this->configurer();
194
			$this->chargerDeterminations($observations);
194
			$this->chargerDeterminations($observations);
195
 
195
 
196
			// 5) restauration de l'ordre souhaité initialement
196
			// 5) restauration de l'ordre souhaité initialement
197
			$observations = self::sortArrayByArray($observations, $idobs);
197
			$observations = self::sortArrayByArray($observations, $idobs);
198
		} else {
198
		} else {
199
			$observations = array();
199
			$observations = array();
200
			$total = 0;
200
			$total = 0;
201
		}
201
		}
202
 
202
 
203
		// 6) JSON output
203
		// 6) JSON output
204
		$resultat = new ResultatService();
204
		$resultat = new ResultatService();
205
		$resultat->corps = array('entete' => self::makeJSONHeader($total, $params, Config::get('url_service')),
205
		$resultat->corps = array('entete' => self::makeJSONHeader($total, $params, Config::get('url_service')),
206
								 'resultats' => $observations);
206
								 'resultats' => $observations);
207
		
207
		
208
		return $resultat;
208
		return $resultat;
209
	}
209
	}
210
 
210
 
211
	static function sortArrayByArray($array, $orderArray) {
211
	static function sortArrayByArray($array, $orderArray) {
212
		$ordered = array();
212
		$ordered = array();
213
		foreach($orderArray as $key) {
213
		foreach($orderArray as $key) {
214
			if(array_key_exists('"' . $key . '"', $array)) {
214
			if(array_key_exists('"' . $key . '"', $array)) {
215
				$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
215
				$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
216
				unset($array['"' . $key . '"']);
216
				unset($array['"' . $key . '"']);
217
			}
217
			}
218
		}
218
		}
219
		return $ordered + $array;
219
		return $ordered + $array;
220
	}
220
	}
221
 
221
 
222
	// SQL helpers
222
	// SQL helpers
223
	/*
223
	/*
224
	 * Retourne une liste ordonnée d'id d'observation correspondant aux critères
224
	 * Retourne une liste ordonnée d'id d'observation correspondant aux critères
225
	 * passés dans p et aux clauses where/join présentes dans le tableau $req
225
	 * passés dans p et aux clauses where/join présentes dans le tableau $req
226
	 *
226
	 *
227
	 * @param p: $params (filtrés sauf escape-string)
227
	 * @param p: $params (filtrés sauf escape-string)
228
	 * @param req: le tableau représentant les composants de la requete SQL
228
	 * @param req: le tableau représentant les composants de la requete SQL
229
	 * @param db: l'instance de db
229
	 * @param db: l'instance de db
230
	 */
230
	 */
231
	static function getIdObs($p, $req, $db) {
231
	static function getIdObs($p, $req, $db) {
232
		$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
232
		$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
233
						 ' FROM v_del_image vdi'.
233
						 ' FROM v_del_image vdi'.
234
						 ' %s' . // LEFT JOIN if any
234
						 ' %s' . // LEFT JOIN if any
235
						 ' WHERE %s'. // where-clause ou TRUE
235
						 ' WHERE %s'. // where-clause ou TRUE
236
						 ' %s'. // group-by
236
						 ' %s'. // group-by
237
						 ' %s'. // having (si commentaires)
237
						 ' %s'. // having (si commentaires)
238
						 ' ORDER BY %s %s %s'.
238
						 ' ORDER BY %s %s %s'.
239
						 ' LIMIT %d, %d -- %s',
239
						 ' LIMIT %d, %d -- %s',
240
						 
240
						 
241
						 $req['join'] ? implode(' ', $req['join']) : '',
241
						 $req['join'] ? implode(' ', $req['join']) : '',
242
						 $req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
242
						 $req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
243
 
243
 
244
						 $req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
244
						 $req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
245
						 $req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
245
						 $req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
246
 
246
 
247
						 $p['tri'], strtoupper($p['ordre']),
247
						 $p['tri'], strtoupper($p['ordre']),
248
						 // date_transmission peut-être NULL et nous voulons de la consistence
248
						 // date_transmission peut-être NULL et nous voulons de la consistence
249
						 // (sauf après r1860 de Cel)
249
						 // (sauf après r1860 de Cel)
250
						 $p['tri'] == 'date_transmission' ? ', id_observation' : '',
250
						 $p['tri'] == 'date_transmission' ? ', id_observation' : '',
251
 
251
 
252
						 $p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
252
						 $p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
253
 
253
 
254
		$res = $db->recupererTous($req_s);
254
		$res = $db->recupererTous($req_s);
255
		$err = mysql_error();
255
		$err = mysql_error();
256
		if(!$res && $err) {
256
		if(!$res && $err) {
257
			// http_response_code(400);
257
			// http_response_code(400);
258
			// if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
258
			// if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
259
			throw new Exception('not found', 400);
259
			throw new Exception('not found', 400);
260
		}
260
		}
261
		// ordre préservé, à partir d'ici c'est important.
261
		// ordre préservé, à partir d'ici c'est important.
262
		return $res;
262
		return $res;
263
	}
263
	}
264
 
264
 
265
	/*
265
	/*
266
	  Champs récupérés:
266
	  Champs récupérés:
267
	  Pour del_images, la vue retourne déjà ce que nous recherchons de cel_obs et cel_images
267
	  Pour del_images, la vue retourne déjà ce que nous recherchons de cel_obs et cel_images
268
	  (cel_obs.* et cel_[obs_]images.{id_observation, id_image, date_prise_de_vue AS date, hauteur, largeur})
268
	  (cel_obs.* et cel_[obs_]images.{id_observation, id_image, date_prise_de_vue AS date, hauteur, largeur})
269
	  Pour del_commentaires: nous voulons *
269
	  Pour del_commentaires: nous voulons *
270
	  Reste ensuite à formatter.
270
	  Reste ensuite à formatter.
271
	  Note: le préfixe de table utilisé ici (vdi) n'impacte *aucune* autre partie du code car rien
271
	  Note: le préfixe de table utilisé ici (vdi) n'impacte *aucune* autre partie du code car rien
272
	  n'en dépend pour l'heure. (inutilisation de $req['select'])
272
	  n'en dépend pour l'heure. (inutilisation de $req['select'])
273
	*/
273
	*/
274
	static function getInfos($idobs, $db) {
274
	static function getInfos($idobs, $db) {
275
		/*$select_fields = implode(',', array_merge(
275
		/*$select_fields = implode(',', array_merge(
276
			array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['dob']),
276
			array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['dob']),
277
			array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['di']),
277
			array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['di']),
278
			array_map(create_function('$a', 'return "du.".$a;'), self::$sql_fields_liaisons['du'])));*/
278
			array_map(create_function('$a', 'return "du.".$a;'), self::$sql_fields_liaisons['du'])));*/
279
		$select_fields = array_merge(self::$sql_fields_liaisons['dob'],
279
		$select_fields = array_merge(self::$sql_fields_liaisons['dob'],
280
									 self::$sql_fields_liaisons['di']);
280
									 self::$sql_fields_liaisons['di']);
281
		$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
281
		$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
282
						 // ' LEFT JOIN del_commentaire AS dc ON di.id_observation = dc.ce_observation AND dc.nom_sel IS NOT NULL'.
282
						 // ' LEFT JOIN del_commentaire AS dc ON di.id_observation = dc.ce_observation AND dc.nom_sel IS NOT NULL'.
283
						 ' WHERE id_observation IN (%s)',
283
						 ' WHERE id_observation IN (%s)',
284
						 implode(',', $select_fields),
284
						 implode(',', $select_fields),
285
						 implode(',', $idobs));
285
						 implode(',', $idobs));
286
		return $db->recupererTous($req_s);
286
		return $db->recupererTous($req_s);
287
	}
287
	}
288
 
288
 
289
 
289
 
290
	/**
290
	/**
291
	 * - Rempli le tableau des contraintes "where" et "join" nécessaire
291
	 * - Rempli le tableau des contraintes "where" et "join" nécessaire
292
	 * à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
292
	 * à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
293
	 *
293
	 *
294
	 * Attention, cela signifie que toutes les tables ne sont pas *forcément*
294
	 * Attention, cela signifie que toutes les tables ne sont pas *forcément*
295
	 * join'ées, par exemple si aucune contrainte ne le nécessite.
295
	 * join'ées, par exemple si aucune contrainte ne le nécessite.
296
	 * $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
296
	 * $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
297
	 * id d'observation qui match.
297
	 * id d'observation qui match.
298
	 * Pour la récupération effective de "toutes" les données correspondante, il faut
298
	 * Pour la récupération effective de "toutes" les données correspondante, il faut
299
	 * réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
299
	 * réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
300
	 *
300
	 *
301
	 * Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
301
	 * Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
302
	 * les JOIN qui sont utilisés.
302
	 * les JOIN qui sont utilisés.
303
	 * le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
303
	 * le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
304
	 * le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
304
	 * le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
305
	 *
305
	 *
306
	 * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
306
	 * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
307
	 * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
307
	 * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
308
	 * @param $c conteneur, utilisé soit pour l'appel récursif à requestFilterParams() en cas de param "masque"
308
	 * @param $c conteneur, utilisé soit pour l'appel récursif à requestFilterParams() en cas de param "masque"
309
	 *								soit pour la définition du type (qui utilise la variable nb_commentaires_discussion)
309
	 *								soit pour la définition du type (qui utilise la variable nb_commentaires_discussion)
310
	 */
310
	 */
311
	static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
311
	static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
312
		if(!empty($p['masque.auteur'])) {
312
		if(!empty($p['masque.auteur'])) {
313
			// id du poster de l'obs
313
			// id du poster de l'obs
314
			$req['join'][] = 'LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = vdi.ce_utilisateur';
314
			$req['join'][] = 'LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = vdi.ce_utilisateur';
315
			// id du poster de l'image... NON, c'est le même que le posteur de l'obs
315
			// id du poster de l'image... NON, c'est le même que le posteur de l'obs
316
			// Cette jointure de table est ignoré ci-dessous pour les recherches d'auteurs
316
			// Cette jointure de table est ignoré ci-dessous pour les recherches d'auteurs
317
			// $req['join'][] = 'LEFT JOIN del_utilisateur AS dui ON dui.id_utilisateur = vdi.i_ce_utilisateur';
317
			// $req['join'][] = 'LEFT JOIN del_utilisateur AS dui ON dui.id_utilisateur = vdi.i_ce_utilisateur';
318
 
318
 
319
			if(is_numeric($p['masque.auteur'])) {
319
			if(is_numeric($p['masque.auteur'])) {
320
				$req['where'][] = sprintf('(du.id_utilisateur = %1$d OR vdi.id_utilisateur = %1$d )', $p['masque.auteur']);
320
				$req['where'][] = sprintf('(du.id_utilisateur = %1$d OR vdi.id_utilisateur = %1$d )', $p['masque.auteur']);
321
			}
321
			}
322
			elseif(preg_match(';^.{5,}@[a-z0-9-.]{5,}$;i', $p['masque.auteur'])) {
322
			elseif(preg_match(';^.{5,}@[a-z0-9-.]{5,}$;i', $p['masque.auteur'])) {
323
				$req['where'][] = sprintf('(du.courriel LIKE %1$s OR vdi.courriel LIKE %1$s )',
323
				$req['where'][] = sprintf('(du.courriel LIKE %1$s OR vdi.courriel LIKE %1$s )',
324
										  $db->proteger($p['masque.auteur'] . '%'));
324
										  $db->proteger($p['masque.auteur'] . '%'));
325
			}
325
			}
326
			else {
326
			else {
327
				self::addAuteursConstraint($p['masque.auteur'], $db, $req['where']);
327
				self::addAuteursConstraint($p['masque.auteur'], $db, $req['where']);
328
			}
328
			}
329
		}
329
		}
330
 
330
 
331
		if(!empty($p['masque.date'])) {
331
		if(!empty($p['masque.date'])) {
332
			if(is_integer($p['masque.date']) && $p['masque.date'] < 2030 && $p['masque.date'] > 1600) {
332
			if(is_integer($p['masque.date']) && $p['masque.date'] < 2030 && $p['masque.date'] > 1600) {
333
				$req['where'][] = sprintf("YEAR(vdi.date_observation) = %d", $p['masque.date']);
333
				$req['where'][] = sprintf("YEAR(vdi.date_observation) = %d", $p['masque.date']);
334
			}
334
			}
335
			else {
335
			else {
336
				$req['where'][] = sprintf("DATE_FORMAT(vdi.date_observation, '%%Y-%%m-%%d') = %s",
336
				$req['where'][] = sprintf("DATE_FORMAT(vdi.date_observation, '%%Y-%%m-%%d') = %s",
337
										  $db->proteger(strftime('%Y-%m-%d', $p['masque.date'])));
337
										  $db->proteger(strftime('%Y-%m-%d', $p['masque.date'])));
338
			}
338
			}
339
		}
339
		}
340
 
340
 
341
		// TODO: avoir des champs d'entrée distinct
341
		// TODO: avoir des champs d'entrée distinct
342
		if(!empty($p['masque.departement'])) {
342
		if(!empty($p['masque.departement'])) {
343
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger('INSEE-C:'.$p['masque.departement']));
343
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger('INSEE-C:'.$p['masque.departement']));
344
		}
344
		}
345
		if(!empty($p['masque.id_zone_geo'])) {
345
		if(!empty($p['masque.id_zone_geo'])) {
346
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger($p['masque.id_zone_geo']));
346
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger($p['masque.id_zone_geo']));
347
		}
347
		}
348
		if(!empty($p['masque.genre'])) {
348
		if(!empty($p['masque.genre'])) {
349
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger('%' . $p['masque.genre'].'% %');
349
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger('%' . $p['masque.genre'].'% %');
350
		}
350
		}
351
		if(!empty($p['masque.famille'])) {
351
		if(!empty($p['masque.famille'])) {
352
			$req['where'][] = 'vdi.famille = '.$db->proteger($p['masque.famille']);
352
			$req['where'][] = 'vdi.famille = '.$db->proteger($p['masque.famille']);
353
		}
353
		}
354
		if(!empty($p['masque.ns'])) {
354
		if(!empty($p['masque.ns'])) {
355
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger($p['masque.ns'].'%');
355
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger($p['masque.ns'].'%');
356
		}
356
		}
357
		if(!empty($p['masque.nn'])) {
357
		if(!empty($p['masque.nn'])) {
358
			$req['where'][] = sprintf('vdi.nom_sel_nn = %1$d OR vdi.nom_ret_nn = %1$d', $p['masque.nn']);
358
			$req['where'][] = sprintf('vdi.nom_sel_nn = %1$d OR vdi.nom_ret_nn = %1$d', $p['masque.nn']);
359
		}
359
		}
360
		if(!empty($p['masque.referentiel'])) {
360
		if(!empty($p['masque.referentiel'])) {
361
			$req['where'][] = sprintf('vdi.nom_referentiel LIKE %s', $db->proteger($p['masque.referentiel'].'%'));
361
			$req['where'][] = sprintf('vdi.nom_referentiel LIKE %s', $db->proteger($p['masque.referentiel'].'%'));
362
		}
362
		}
363
		if(!empty($p['masque.commune'])) {
363
		if(!empty($p['masque.commune'])) {
364
			$req['where'][] = 'vdi.zone_geo LIKE '.$db->proteger($p['masque.commune'].'%');
364
			$req['where'][] = 'vdi.zone_geo LIKE '.$db->proteger($p['masque.commune'].'%');
365
		}
365
		}
366
		if(!empty($p['masque.tag'])) {
366
		if(!empty($p['masque.tag'])) {
367
			// i_mots_cles_texte provient de la VIEW v_del_image
367
			// i_mots_cles_texte provient de la VIEW v_del_image
368
			// TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
368
			// TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
369
			$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
369
			$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
370
									  self::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
370
									  self::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
371
									  $db->proteger(strtolower($p['masque.tag'])));
371
									  $db->proteger(strtolower($p['masque.tag'])));
372
		}
372
		}
373
 
373
 
374
		if(!empty($p['masque.type'])) {
374
		if(!empty($p['masque.type'])) {
375
			self::addTypeConstraints($p['masque.type'], $db, $req, $c);
375
			self::addTypeConstraints($p['masque.type'], $db, $req, $c);
376
		}
376
		}
377
	}
377
	}
378
 
378
 
379
	/* Le masque fait une recherche générique parmi de nombreux champs ci-dessus.
379
	/* Le masque fait une recherche générique parmi de nombreux champs ci-dessus.
380
	   Nous initialisons donc ces paramètres (excepté masque biensur), et nous rappelons
380
	   Nous initialisons donc ces paramètres (excepté masque biensur), et nous rappelons
381
	   récursivement. À la seule différence que nous n'utiliserons que $or_req['where']
381
	   récursivement. À la seule différence que nous n'utiliserons que $or_req['where']
382
	   imploded par des " OR ". */
382
	   imploded par des " OR ". */
383
	static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
383
	static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
384
		if(!empty($p['masque'])) {
384
		if(!empty($p['masque'])) {
385
			$or_params = array('masque.auteur' => $p['masque'],
385
			$or_params = array('masque.auteur' => $p['masque'],
386
							   'masque.departement' => $p['masque'],
386
							   'masque.departement' => $p['masque'],
387
							   'masque.id_zone_geo' => $p['masque'],
387
							   'masque.id_zone_geo' => $p['masque'],
388
							   'masque.tag' => $p['masque'],
388
							   'masque.tag' => $p['masque'],
389
							   'masque.ns' => $p['masque'],
389
							   'masque.ns' => $p['masque'],
390
							   'masque.famille' => $p['masque'],
390
							   'masque.famille' => $p['masque'],
391
							   'masque.date' => $p['masque'],
391
							   'masque.date' => $p['masque'],
392
							   'masque.genre' => $p['masque'],
392
							   'masque.genre' => $p['masque'],
393
							   /* milieu: TODO ? */ );
393
							   /* milieu: TODO ? */ );
394
			$or_masque = self::requestFilterParams($or_params, array_keys($or_params), $c);
394
			$or_masque = self::requestFilterParams($or_params, array_keys($or_params), $c);
395
			// $or_req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
395
			// $or_req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
396
			$or_req = array('join' => array(), 'where' => array());
396
			$or_req = array('join' => array(), 'where' => array());
397
			self::sqlAddConstraint($or_masque, $db, $or_req);
397
			self::sqlAddConstraint($or_masque, $db, $or_req);
398
 
398
 
399
			if($or_req['where']) {
399
			if($or_req['where']) {
400
				$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
400
				$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
401
				// utile au cas ou des jointures seraient rajoutées
401
				// utile au cas ou des jointures seraient rajoutées
402
				$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
402
				$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
403
			}
403
			}
404
		}
404
		}
405
	}
405
	}
406
 
406
 
407
 
407
 
408
	private function configurer() {
408
	private function configurer() {
409
		$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
409
		$this->mappingFiltre = $this->conteneur->getParametre('mapping_masque');
410
		$this->mappingObservation = $this->conteneur->getParametre('mapping_observation');
410
		$this->mappingObservation = $this->conteneur->getParametre('mapping_observation');
411
		$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
411
		$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
412
		$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
412
		$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
413
	}
413
	}
414
 
414
 
415
 
415
 
416
 
416
 
417
	/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
417
	/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
418
	   Il faut donc utiliser la syntaxe IFNULL(%s, "").
418
	   Il faut donc utiliser la syntaxe IFNULL(%s, "").
419
	   (Cette fonction effectue aussi l'implode() "final" */
419
	   (Cette fonction effectue aussi l'implode() "final" */
420
	static function sqlAddIfNullPourConcat($tab) {
420
	static function sqlAddIfNullPourConcat($tab) {
421
		// XXX: PHP-5.3
421
		// XXX: PHP-5.3
422
		return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
422
		return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
423
	}
423
	}
424
 
424
 
425
	/*
425
	/*
426
	  Retourne une clausse where du style:
426
	  Retourne une clausse where du style:
427
	  CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
427
	  CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
428
	  Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
428
	  Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
429
	  car cette information est redondante dans cel_image et devrait être supprimée.
429
	  car cette information est redondante dans cel_image et devrait être supprimée.
430
	*/
430
	*/
431
	static function addAuteursConstraint($val, $db, &$where) {
431
	static function addAuteursConstraint($val, $db, &$where) {
432
		@list($a, $b) = explode(' ', $val, 2);
432
		@list($a, $b) = explode(' ', $val, 2);
433
		// un seul terme
433
		// un seul terme
434
		$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
434
		$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
435
						  'vdi.prenom_utilisateur', // info user anonyme de l'obs
435
						  'vdi.prenom_utilisateur', // info user anonyme de l'obs
436
						  /* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
436
						  /* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
437
		$champs_p = array('du.nom', // idem pour le nom
437
		$champs_p = array('du.nom', // idem pour le nom
438
						  'vdi.nom_utilisateur',
438
						  'vdi.nom_utilisateur',
439
						  /* 'vdi.i_nom_utilisateur' */ );
439
						  /* 'vdi.i_nom_utilisateur' */ );
440
 
440
 
441
		/*
441
		/*
442
		  Note: pour l'heure, étant donnés:
442
		  Note: pour l'heure, étant donnés:
443
		  - les CONVERT() de la VIEW del_utilisateur
443
		  - les CONVERT() de la VIEW del_utilisateur
444
		  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
444
		  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
445
		  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
445
		  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
446
		  et l'âge du capitaine...
446
		  et l'âge du capitaine...
447
		  - REGEXP est case-sensitive, et collate les caractères accentués
447
		  - REGEXP est case-sensitive, et collate les caractères accentués
448
		  - LIKE est case-insensitive, et collate les caractères accentués
448
		  - LIKE est case-insensitive, et collate les caractères accentués
449
		 */
449
		 */
450
		if(! $b) {
450
		if(! $b) {
451
			$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
451
			$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
452
							   self::sqlAddIfNullPourConcat($champs_n),
452
							   self::sqlAddIfNullPourConcat($champs_n),
453
							   self::sqlAddIfNullPourConcat($champs_p),
453
							   self::sqlAddIfNullPourConcat($champs_p),
454
							   $db->proteger("%".$val."%"));
454
							   $db->proteger("%".$val."%"));
455
		}
455
		}
456
		else {
456
		else {
457
			$where[] = sprintf('CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s ',
457
			$where[] = sprintf('CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s ',
458
							   self::sqlAddIfNullPourConcat($champs_n),
458
							   self::sqlAddIfNullPourConcat($champs_n),
459
							   self::sqlAddIfNullPourConcat($champs_p),
459
							   self::sqlAddIfNullPourConcat($champs_p),
460
							   $db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
460
							   $db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
461
		}
461
		}
462
	}
462
	}
463
 
463
 
464
	/*
464
	/*
465
	 * @param $req: la représentation de la requête MySQL complète, à amender.
465
	 * @param $req: la représentation de la requête MySQL complète, à amender.
466
	 */
466
	 */
467
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
467
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
468
		if(array_key_exists('adeterminer', $val)) {
468
		if(array_key_exists('adeterminer', $val)) {
469
			//On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
469
			//On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
470
			$req['where'][] = '(' . implode(' OR ', array(
470
			$req['where'][] = '(' . implode(' OR ', array(
471
				'vdi.certitude = "aDeterminer"',
471
				'vdi.certitude = "aDeterminer"',
472
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
472
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
473
				'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
473
				'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
474
			)) . ')';
474
			)) . ')';
475
		}
475
		}
476
		if(array_key_exists('aconfirmer', $val)) {
476
		if(array_key_exists('aconfirmer', $val)) {
477
			//On récupère toutes les observations qui ne sont pas "aDeterminer" *et* qui ont un nom d'espèce
477
			//On récupère toutes les observations qui ne sont pas "aDeterminer" *et* qui ont un nom d'espèce
478
			$req['where'][] = '(' . implode(' AND ', array(
478
			$req['where'][] = '(' . implode(' AND ', array(
479
				'vdi.nom_sel IS NOT NULL',
479
				'vdi.nom_sel IS NOT NULL',
480
				'vdi.certitude != "aDeterminer"',
480
				'vdi.certitude != "aDeterminer"',
481
				'(vdi.mots_cles_texte IS NULL OR vdi.mots_cles_texte NOT LIKE "%aDeterminer%"',
481
				'(vdi.mots_cles_texte IS NULL OR vdi.mots_cles_texte NOT LIKE "%aDeterminer%"',
482
			)) . ')';
482
			)) . ')';
483
		}
483
		}
484
		if(array_key_exists('validees', $val)) {
484
		if(array_key_exists('validees', $val)) {
485
			//On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
485
			//On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
486
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
486
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
487
		}
487
		}
488
 
488
 
489
		// solution n°1: impraticable
489
		// solution n°1: impraticable
490
		if(false && array_key_exists('endiscussion', $val)) {
490
		if(false && array_key_exists('endiscussion', $val)) {
491
			//Si on veut les observations en discussion,
491
			//Si on veut les observations en discussion,
492
			// on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
492
			// on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
493
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
493
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
494
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
494
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
495
			$req['groupby'][] = 'vdi.id_observation';
495
			$req['groupby'][] = 'vdi.id_observation';
496
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
496
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
497
		}
497
		}
498
 
498
 
499
		if(array_key_exists('endiscussion', $val)) {
499
		if(array_key_exists('endiscussion', $val)) {
500
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
500
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
501
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
501
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
502
		}
502
		}
503
	}
503
	}
504
 
504
 
505
 
505
 
506
	/**
506
	/**
507
	* Récupérer toutes les déterminations et le nombre de commentaire au total
507
	* Récupérer toutes les déterminations et le nombre de commentaire au total
508
	* @param array $observations la liste des observations à mettre à jour
508
	* @param array $observations la liste des observations à mettre à jour
509
	* */
509
	* */
510
	private function chargerDeterminations(&$observations) {
510
	private function chargerDeterminations(&$observations) {
511
		$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
511
		$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
512
									$observations));
512
									$observations));
513
		$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
513
		$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
514
					 implode(',',$idObs),
514
					 implode(',',$idObs),
515
					 __FILE__ . ':' . __LINE__);
515
					 __FILE__ . ':' . __LINE__);
516
		$propositions = $this->bdd->recupererTous($r);
516
		$propositions = $this->bdd->recupererTous($r);
517
		if(!$propositions) return;
517
		if(!$propositions) return;
518
		foreach ($propositions as $proposition) {
518
		foreach ($propositions as $proposition) {
519
			$idObs = $proposition['ce_observation'];
519
			$idObs = $proposition['ce_observation'];
520
			$idComment = $proposition['id_commentaire'];
520
			$idComment = $proposition['id_commentaire'];
521
			$comment = $this->formaterDetermination($idComment, $proposition);
521
			$comment = $this->formaterDetermination($idComment, $proposition);
522
			if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
522
			if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
523
				
523
				
524
		}
524
		}
525
	}
525
	}
526
 
526
 
527
	private function formaterDetermination($commentId, $proposition) {
527
	private function formaterDetermination($commentId, $proposition) {
528
		if(!$proposition) return NULL;
528
		if(!$proposition) return NULL;
529
 
529
 
530
		$proposition_formatee = array('nb_commentaires' => '0');
530
		$proposition_formatee = array('nb_commentaires' => '0');
531
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
531
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
532
			if (isset($proposition[$nomOriginal])) {
532
			if (isset($proposition[$nomOriginal])) {
533
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
533
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
534
			}
534
			}
535
		}
535
		}
536
 
536
 
537
		// Charger les votes sur les déterminations
537
		// Charger les votes sur les déterminations
538
		$resultatsVotes = $this->bdd->recupererTous(
538
		$resultatsVotes = $this->bdd->recupererTous(
539
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
539
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
540
		
540
		
541
		foreach ($resultatsVotes as $vote) {
541
		foreach ($resultatsVotes as $vote) {
542
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
542
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
543
		}
543
		}
544
 
544
 
545
 
545
 
546
		// chargerNombreCommentaire()
546
		// chargerNombreCommentaire()
547
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
547
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
548
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
548
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
549
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
549
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
550
			'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
550
			'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
551
			$commentId, __FILE__ . ':' . __LINE__));
551
			$commentId, __FILE__ . ':' . __LINE__));
552
		foreach ($listeCommentaires as $ligneProposition) {
552
		foreach ($listeCommentaires as $ligneProposition) {
553
			// ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
553
			// ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
554
			if($ligneProposition['ce_commentaire_parent']) {
554
			if($ligneProposition['ce_commentaire_parent']) {
555
				// TODO/debug: id_commentaire_parent != $commentId ??
555
				// TODO/debug: id_commentaire_parent != $commentId ??
556
				// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
556
				// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
557
				if($ligneProposition['ce_commentaire_parent'] != $commentId) {
557
				if($ligneProposition['ce_commentaire_parent'] != $commentId) {
558
					// restore_error_handler();
558
					// restore_error_handler();
559
					error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
559
					error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
560
									  $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
560
									  $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
561
				}
561
				}
562
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
562
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
563
			} else {
563
			} else {
564
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
564
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
565
			}
565
			}
566
		}
566
		}
567
 
567
 
568
		return $proposition_formatee;
568
		return $proposition_formatee;
569
	}
569
	}
570
 
570
 
571
	/**
571
	/**
572
	*  Formater un vote en fonction du fichier de configuration config_votes.ini
572
	*  Formater un vote en fonction du fichier de configuration config_votes.ini
573
	*  @param $votes array()
573
	*  @param $votes array()
574
	* */
574
	* */
575
	private function formaterVote($vote) {
575
	private function formaterVote($vote) {
576
		$retour = array();
576
		$retour = array();
577
		foreach ($vote as $param=>$valeur) {
577
		foreach ($vote as $param=>$valeur) {
578
			$retour[$this->mappingVotes[$param]] = $valeur;
578
			$retour[$this->mappingVotes[$param]] = $valeur;
579
		}
579
		}
580
		return $retour;
580
		return $retour;
581
	}
581
	}
582
 
582
 
583
 
583
 
584
	// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
584
	// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
585
	// au spectre passé par $values.
585
	// au spectre passé par $values.
586
	static function unsetIfInvalid(&$var, $index, $values) {
586
	static function unsetIfInvalid(&$var, $index, $values) {
587
		if(array_key_exists($index, $var)) {
587
		if(array_key_exists($index, $var)) {
588
			if(!in_array($var[$index], $values)) unset($var[$index]);
588
			if(!in_array($var[$index], $values)) unset($var[$index]);
589
			else return $var[$index];
589
			else return $var[$index];
590
		}
590
		}
591
		return NULL;
591
		return NULL;
592
	}
592
	}
593
 
593
 
594
 
594
 
595
	/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
595
	/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
596
	   Cette fonction est appelée:
596
	   Cette fonction est appelée:
597
	   - une fois sur les champs de recherche avancées
597
	   - une fois sur les champs de recherche avancées
598
	   - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
598
	   - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
599
	   la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
599
	   la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
600
	   avec les traitements particuliers qui s'imposent
600
	   avec les traitements particuliers qui s'imposent
601
	   Par exemple: si l'on cherche "Languedoc", cela impliquera:
601
	   Par exemple: si l'on cherche "Languedoc", cela impliquera:
602
	   WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
602
	   WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
603
	   qui s'assure d'un pattern particulier */
603
	   qui s'assure d'un pattern particulier */
604
	static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
604
	static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
605
		if($parametres_autorises) { // filtrage de toute clef inconnue
605
		if($parametres_autorises) { // filtrage de toute clef inconnue
606
			$params = array_intersect_key($params, array_flip($parametres_autorises));
606
			$params = array_intersect_key($params, array_flip($parametres_autorises));
607
		}
607
		}
608
 
608
 
609
		$p['tri'] = self::unsetIfInvalid($params, 'tri', array('date_observation'));
609
		$p['tri'] = self::unsetIfInvalid($params, 'tri', array('date_observation'));
610
		$p['ordre'] = self::unsetIfInvalid($params, 'ordre', array('asc','desc'));
610
		$p['ordre'] = self::unsetIfInvalid($params, 'ordre', array('asc','desc'));
611
		$p['masque.referentiel'] = self::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
611
		$p['masque.referentiel'] = self::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
612
 
612
 
613
		// TODO: use filter_input(INPUT_GET);
613
		// TODO: use filter_input(INPUT_GET);
614
		// renvoie FALSE ou NULL si absent ou invalide
614
		// renvoie FALSE ou NULL si absent ou invalide
615
		$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
615
		$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
616
											 FILTER_VALIDATE_INT,
616
											 FILTER_VALIDATE_INT,
617
											 array('options' => array('default' => NULL,
617
											 array('options' => array('default' => NULL,
618
																	  'min_range' => 1,
618
																	  'min_range' => 1,
619
																	  'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
619
																	  'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
620
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
620
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
621
											 FILTER_VALIDATE_INT,
621
											 FILTER_VALIDATE_INT,
622
											 array('options' => array('default' => NULL,
622
											 array('options' => array('default' => NULL,
623
																	  'min_range' => 0,
623
																	  'min_range' => 0,
624
																	  'max_range' => _LISTE_OBS_MAX_ID_OBS)));
624
																	  'max_range' => _LISTE_OBS_MAX_ID_OBS)));
625
		if(isset($params['masque.departement'])) {
625
		if(isset($params['masque.departement'])) {
626
			// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
626
			// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
627
			// accept leading 0 ?
627
			// accept leading 0 ?
628
			// TODO; filter patterns like 555.
628
			// TODO; filter patterns like 555.
629
			if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
629
			if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
630
				$p['masque.departement'] = $params['masque.departement'];
630
				$p['masque.departement'] = $params['masque.departement'];
631
			}
631
			}
632
			// cf configurations/config_departements_bruts.ini
632
			// cf configurations/config_departements_bruts.ini
633
			elseif( !is_null($c) && ( $x = $c->getParametre(
633
			elseif( !is_null($c) && ( $x = $c->getParametre(
634
				strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
634
				strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
635
			))) {
635
			))) {
636
				$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
636
				$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
637
			}
637
			}
638
		}
638
		}
639
 
639
 
640
		if(isset($params['masque.date'])) {
640
		if(isset($params['masque.date'])) {
641
			// une année, TODO: masque.annee
641
			// une année, TODO: masque.annee
642
			if(is_numeric($params['masque.date'])) {
642
			if(is_numeric($params['masque.date'])) {
643
				$p['masque.date'] = $params['masque.date'];
643
				$p['masque.date'] = $params['masque.date'];
644
			}
644
			}
645
			elseif(strpos($params['masque.date'], '/' !== false) &&
645
			elseif(strpos($params['masque.date'], '/' !== false) &&
646
				   ($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
646
				   ($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
647
				$p['masque.date'] = $x;
647
				$p['masque.date'] = $x;
648
			}
648
			}
649
			elseif(strpos($params['masque.date'], '-' !== false) &&
649
			elseif(strpos($params['masque.date'], '-' !== false) &&
650
				   ($x = strtotime($params['masque.date'])) ) {
650
				   ($x = strtotime($params['masque.date'])) ) {
651
				$p['masque.date'] = $x;
651
				$p['masque.date'] = $x;
652
			}
652
			}
653
		}
653
		}
654
 
654
 
655
		$p['masque.nn'] = filter_var(@$params['masque.nn'],
655
		$p['masque.nn'] = filter_var(@$params['masque.nn'],
656
									 FILTER_VALIDATE_INT,
656
									 FILTER_VALIDATE_INT,
657
									 array('options' => array('default' => NULL,
657
									 array('options' => array('default' => NULL,
658
															  'min_range' => 0,
658
															  'min_range' => 0,
659
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
659
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
660
 
660
 
661
		$p['masque.nt'] = filter_var(@$params['masque.nt'],
661
		$p['masque.nt'] = filter_var(@$params['masque.nt'],
662
									 FILTER_VALIDATE_INT,
662
									 FILTER_VALIDATE_INT,
663
									 array('options' => array('default' => NULL,
663
									 array('options' => array('default' => NULL,
664
															  'min_range' => 0,
664
															  'min_range' => 0,
665
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
665
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
666
 
666
 
667
 
667
 
668
		// TODO: should we really trim() ?
668
		// TODO: should we really trim() ?
669
 
669
 
670
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
670
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
671
		// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
671
		// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
672
 
672
 
673
		if(isset($params['masque.famille'])) {
673
		if(isset($params['masque.famille'])) {
674
			// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
674
			// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
675
			$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
675
			$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
676
																			"ASCII//TRANSLIT",
676
																			"ASCII//TRANSLIT",
677
																			$params['masque.famille']));
677
																			$params['masque.famille']));
678
		}
678
		}
679
 
679
 
680
		// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
680
		// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
681
		// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
681
		// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
682
		// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
682
		// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
683
		// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
683
		// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
684
		if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
684
		if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
685
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
685
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
686
		// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
686
		// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
687
		if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
687
		if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
688
 
688
 
689
		// idem pour id_zone_geo qui mappait à ce_zone_geo:
689
		// idem pour id_zone_geo qui mappait à ce_zone_geo:
690
		if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
690
		if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
691
			$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
691
			$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
692
		}
692
		}
693
 
693
 
694
		// masque.commune (zone_geo)
694
		// masque.commune (zone_geo)
695
		// TODO: que faire avec des '%' en INPUT ?
695
		// TODO: que faire avec des '%' en INPUT ?
696
		// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
696
		// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
697
		if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
697
		if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
698
 
698
 
699
		// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
699
		// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
700
		if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
700
		if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
701
		// sera trimmé plus tard, cf sqlAddConstraint
701
		// sera trimmé plus tard, cf sqlAddConstraint
702
		if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
702
		if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
703
 
703
 
704
		// masque.tag, idem que pour masque.genre et masque.commune
704
		// masque.tag, idem que pour masque.genre et masque.commune
705
		if(isset($params['masque.tag'])) {
705
		if(isset($params['masque.tag'])) {
706
			$x = explode(',',$params['masque.tag']);
706
			$x = explode(',',$params['masque.tag']);
707
			$x = array_map('trim', $x);
707
			$x = array_map('trim', $x);
708
			$p['masque.tag'] = implode('|', array_filter($x));
708
			$p['masque.tag'] = implode('|', array_filter($x));
709
		}
709
		}
710
 
710
 
711
		// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
711
		// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
712
		if(isset($params['masque.type'])) {
712
		if(isset($params['masque.type'])) {
713
			$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
713
			$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
714
														   array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
714
														   array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
715
		}
715
		}
716
 
716
 
717
 
717
 
718
		// TODO: masque (général)
718
		// TODO: masque (général)
719
 
719
 
720
 
720
 
721
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
721
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
722
		// TODO: PHP-5.3
722
		// TODO: PHP-5.3
723
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
723
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
724
	}
724
	}
725
 
725
 
726
	static function makeJSONHeader($total, $params, $url_service) {
726
	static function makeJSONHeader($total, $params, $url_service) {
727
		$prev_url = $next_url = NULL;
727
		$prev_url = $next_url = NULL;
728
 
728
 
729
		$next_offset = $params['navigation.depart'] + $params['navigation.limite'];
729
		$next_offset = $params['navigation.depart'] + $params['navigation.limite'];
730
		if($next_offset < $total) {
730
		if($next_offset < $total) {
731
			$next_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
731
			$next_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
732
								http_build_query(array_merge($params, array('navigation.depart' => $next_offset))));
732
								http_build_query(array_merge($params, array('navigation.depart' => $next_offset))));
733
		}
733
		}
734
 
734
 
735
		$prev_offset = $params['navigation.depart'] - $params['navigation.limite'];
735
		$prev_offset = $params['navigation.depart'] - $params['navigation.limite'];
736
		if($prev_offset > 0) {
736
		if($prev_offset > 0) {
737
			$prev_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
737
			$prev_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
738
								http_build_query(array_merge($params, array('navigation.depart' => $prev_offset))));
738
								http_build_query(array_merge($params, array('navigation.depart' => $prev_offset))));
739
		}
739
		}
740
 
740
 
741
		return array(
741
		return array(
742
			'masque' => http_build_query(array_diff_key($params, array_flip(array('navigation.depart', 'navigation.limite')))),
742
			'masque' => http_build_query(array_diff_key($params, array_flip(array('navigation.depart', 'navigation.limite')))),
743
			'total' => $total,
743
			'total' => $total,
744
			'depart' => $params['navigation.depart'],
744
			'depart' => $params['navigation.depart'],
745
			'limite' => $params['navigation.limite'],
745
			'limite' => $params['navigation.limite'],
746
			'href.precedent' => $prev_url,
746
			'href.precedent' => $prev_url,
747
			'href.suivant' => $next_url
747
			'href.suivant' => $next_url
748
		);
748
		);
749
	}
749
	}
750
}
750
}