Subversion Repositories eFlore/Applications.del

Rev

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

Rev 1452 Rev 1480
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 ListeObservations {
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->mappingVotes = $this->conteneur->getParametre('mapping_votes');
409
		$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
410
		$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
410
		$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
411
	}
411
	}
412
 
412
 
413
 
413
 
414
 
414
 
415
	/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
415
	/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
416
	   Il faut donc utiliser la syntaxe IFNULL(%s, "").
416
	   Il faut donc utiliser la syntaxe IFNULL(%s, "").
417
	   (Cette fonction effectue aussi l'implode() "final" */
417
	   (Cette fonction effectue aussi l'implode() "final" */
418
	static function sqlAddIfNullPourConcat($tab) {
418
	static function sqlAddIfNullPourConcat($tab) {
419
		// XXX: PHP-5.3
419
		// XXX: PHP-5.3
420
		return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
420
		return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
421
	}
421
	}
422
 
422
 
423
	/*
423
	/*
424
	  Retourne une clausse where du style:
424
	  Retourne une clausse where du style:
425
	  CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
425
	  CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
426
	  Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
426
	  Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
427
	  car cette information est redondante dans cel_image et devrait être supprimée.
427
	  car cette information est redondante dans cel_image et devrait être supprimée.
428
	*/
428
	*/
429
	static function addAuteursConstraint($val, $db, &$where) {
429
	static function addAuteursConstraint($val, $db, &$where) {
430
		@list($a, $b) = explode(' ', $val, 2);
430
		@list($a, $b) = explode(' ', $val, 2);
431
		// un seul terme
431
		// un seul terme
432
		$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
432
		$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
433
						  'vdi.prenom_utilisateur', // info user anonyme de l'obs
433
						  'vdi.prenom_utilisateur', // info user anonyme de l'obs
434
						  /* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
434
						  /* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
435
		$champs_p = array('du.nom', // idem pour le nom
435
		$champs_p = array('du.nom', // idem pour le nom
436
						  'vdi.nom_utilisateur',
436
						  'vdi.nom_utilisateur',
437
						  /* 'vdi.i_nom_utilisateur' */ );
437
						  /* 'vdi.i_nom_utilisateur' */ );
438
 
438
 
439
		/*
439
		/*
440
		  Note: pour l'heure, étant donnés:
440
		  Note: pour l'heure, étant donnés:
441
		  - les CONVERT() de la VIEW del_utilisateur
441
		  - les CONVERT() de la VIEW del_utilisateur
442
		  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
442
		  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
443
		  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
443
		  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
444
		  et l'âge du capitaine...
444
		  et l'âge du capitaine...
445
		  - REGEXP est case-sensitive, et collate les caractères accentués
445
		  - REGEXP est case-sensitive, et collate les caractères accentués
446
		  - LIKE est case-insensitive, et collate les caractères accentués
446
		  - LIKE est case-insensitive, et collate les caractères accentués
447
		 */
447
		 */
448
		if(! $b) {
448
		if(! $b) {
449
			$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
449
			$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
450
							   self::sqlAddIfNullPourConcat($champs_n),
450
							   self::sqlAddIfNullPourConcat($champs_n),
451
							   self::sqlAddIfNullPourConcat($champs_p),
451
							   self::sqlAddIfNullPourConcat($champs_p),
452
							   $db->proteger("%".$val."%"));
452
							   $db->proteger("%".$val."%"));
453
		}
453
		}
454
		else {
454
		else {
455
			$where[] = sprintf('CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s ',
455
			$where[] = sprintf('CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s ',
456
							   self::sqlAddIfNullPourConcat($champs_n),
456
							   self::sqlAddIfNullPourConcat($champs_n),
457
							   self::sqlAddIfNullPourConcat($champs_p),
457
							   self::sqlAddIfNullPourConcat($champs_p),
458
							   $db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
458
							   $db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
459
		}
459
		}
460
	}
460
	}
461
 
461
 
462
	/*
462
	/*
463
	 * @param $req: la représentation de la requête MySQL complète, à amender.
463
	 * @param $req: la représentation de la requête MySQL complète, à amender.
464
	 */
464
	 */
465
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
465
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
466
		if(array_key_exists('adeterminer', $val)) {
466
		if(array_key_exists('adeterminer', $val)) {
467
			//On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
467
			//On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
468
			$req['where'][] = '(' . implode(' OR ', array(
468
			$req['where'][] = '(' . implode(' OR ', array(
469
				'vdi.certitude = "aDeterminer"',
469
				'vdi.certitude = "aDeterminer"',
470
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
470
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
471
				'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
471
				'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
472
			)) . ')';
472
			)) . ')';
473
		}
473
		}
474
		if(array_key_exists('aconfirmer', $val)) {
474
		if(array_key_exists('aconfirmer', $val)) {
475
			//On récupère toutes les observations qui ne sont pas "aDeterminer" *et* qui ont un nom d'espèce
475
			//On récupère toutes les observations qui ne sont pas "aDeterminer" *et* qui ont un nom d'espèce
476
			$req['where'][] = '(' . implode(' AND ', array(
476
			$req['where'][] = '(' . implode(' AND ', array(
477
				'vdi.nom_sel IS NOT NULL',
477
				'vdi.nom_sel IS NOT NULL',
478
				'vdi.certitude != "aDeterminer"',
478
				'vdi.certitude != "aDeterminer"',
479
				'(vdi.mots_cles_texte IS NULL OR vdi.mots_cles_texte NOT LIKE "%aDeterminer%"',
479
				'(vdi.mots_cles_texte IS NULL OR vdi.mots_cles_texte NOT LIKE "%aDeterminer%"',
480
			)) . ')';
480
			)) . ')';
481
		}
481
		}
482
		if(array_key_exists('validees', $val)) {
482
		if(array_key_exists('validees', $val)) {
483
			//On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
483
			//On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
484
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
484
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
485
		}
485
		}
486
 
486
 
487
		// solution n°1: impraticable
487
		// solution n°1: impraticable
488
		if(false && array_key_exists('endiscussion', $val)) {
488
		if(false && array_key_exists('endiscussion', $val)) {
489
			//Si on veut les observations en discussion,
489
			//Si on veut les observations en discussion,
490
			// on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
490
			// on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
491
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
491
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
492
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
492
			$req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
493
			$req['groupby'][] = 'vdi.id_observation';
493
			$req['groupby'][] = 'vdi.id_observation';
494
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
494
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
495
		}
495
		}
496
 
496
 
497
		if(array_key_exists('endiscussion', $val)) {
497
		if(array_key_exists('endiscussion', $val)) {
498
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
498
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
499
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
499
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
500
		}
500
		}
501
	}
501
	}
502
 
502
 
503
 
503
 
504
	/**
504
	/**
505
	* Récupérer toutes les déterminations et le nombre de commentaire au total
505
	* Récupérer toutes les déterminations et le nombre de commentaire au total
506
	* @param array $observations la liste des observations à mettre à jour
506
	* @param array $observations la liste des observations à mettre à jour
507
	* */
507
	* */
508
	private function chargerDeterminations(&$observations) {
508
	private function chargerDeterminations(&$observations) {
509
		$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
509
		$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
510
									$observations));
510
									$observations));
511
		$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
511
		$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
512
					 implode(',',$idObs),
512
					 implode(',',$idObs),
513
					 __FILE__ . ':' . __LINE__);
513
					 __FILE__ . ':' . __LINE__);
514
		$propositions = $this->bdd->recupererTous($r);
514
		$propositions = $this->bdd->recupererTous($r);
515
		if(!$propositions) return;
515
		if(!$propositions) return;
516
		foreach ($propositions as $proposition) {
516
		foreach ($propositions as $proposition) {
517
			$idObs = $proposition['ce_observation'];
517
			$idObs = $proposition['ce_observation'];
518
			$idComment = $proposition['id_commentaire'];
518
			$idComment = $proposition['id_commentaire'];
519
			$comment = $this->formaterDetermination($idComment, $proposition);
519
			$comment = $this->formaterDetermination($idComment, $proposition);
520
			if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
520
			if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
521
				
521
				
522
		}
522
		}
523
	}
523
	}
524
 
524
 
525
	private function formaterDetermination($commentId, $proposition) {
525
	private function formaterDetermination($commentId, $proposition) {
526
		if(!$proposition) return NULL;
526
		if(!$proposition) return NULL;
527
 
527
 
528
		$proposition_formatee = array('nb_commentaires' => '0');
528
		$proposition_formatee = array('nb_commentaires' => '0');
529
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
529
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
530
			if (isset($proposition[$nomOriginal])) {
530
			if (isset($proposition[$nomOriginal])) {
531
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
531
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
532
			}
532
			}
533
		}
533
		}
534
 
534
 
535
		// Charger les votes sur les déterminations
535
		// Charger les votes sur les déterminations
536
		$resultatsVotes = $this->bdd->recupererTous(
536
		$resultatsVotes = $this->bdd->recupererTous(
537
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
537
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
538
		
538
		
539
		foreach ($resultatsVotes as $vote) {
539
		foreach ($resultatsVotes as $vote) {
540
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
540
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
541
		}
541
		}
542
 
542
 
543
 
543
 
544
		// chargerNombreCommentaire()
544
		// chargerNombreCommentaire()
545
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
545
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
546
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
546
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
547
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
547
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
548
			'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
548
			'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
549
			$commentId, __FILE__ . ':' . __LINE__));
549
			$commentId, __FILE__ . ':' . __LINE__));
550
		foreach ($listeCommentaires as $ligneProposition) {
550
		foreach ($listeCommentaires as $ligneProposition) {
551
			// ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
551
			// ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
552
			if($ligneProposition['ce_commentaire_parent']) {
552
			if($ligneProposition['ce_commentaire_parent']) {
553
				// TODO/debug: id_commentaire_parent != $commentId ??
553
				// TODO/debug: id_commentaire_parent != $commentId ??
554
				// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
554
				// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
555
				if($ligneProposition['ce_commentaire_parent'] != $commentId) {
555
				if($ligneProposition['ce_commentaire_parent'] != $commentId) {
556
					// restore_error_handler();
556
					// restore_error_handler();
557
					error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
557
					error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
558
									  $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
558
									  $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
559
				}
559
				}
560
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
560
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
561
			} else {
561
			} else {
562
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
562
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
563
			}
563
			}
564
		}
564
		}
565
 
565
 
566
		return $proposition_formatee;
566
		return $proposition_formatee;
567
	}
567
	}
568
 
568
 
569
	/**
569
	/**
570
	*  Formater un vote en fonction du fichier de configuration config_votes.ini
570
	*  Formater un vote en fonction du fichier de configuration config_votes.ini
571
	*  @param $votes array()
571
	*  @param $votes array()
572
	* */
572
	* */
573
	private function formaterVote($vote) {
573
	private function formaterVote($vote) {
574
		$retour = array();
574
		$retour = array();
575
		foreach ($vote as $param=>$valeur) {
575
		foreach ($vote as $param=>$valeur) {
576
			$retour[$this->mappingVotes[$param]] = $valeur;
576
			$retour[$this->mappingVotes[$param]] = $valeur;
577
		}
577
		}
578
		return $retour;
578
		return $retour;
579
	}
579
	}
580
 
580
 
581
 
581
 
582
	// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
582
	// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
583
	// au spectre passé par $values.
583
	// au spectre passé par $values.
584
	static function unsetIfInvalid(&$var, $index, $values) {
584
	static function unsetIfInvalid(&$var, $index, $values) {
585
		if(array_key_exists($index, $var)) {
585
		if(array_key_exists($index, $var)) {
586
			if(!in_array($var[$index], $values)) unset($var[$index]);
586
			if(!in_array($var[$index], $values)) unset($var[$index]);
587
			else return $var[$index];
587
			else return $var[$index];
588
		}
588
		}
589
		return NULL;
589
		return NULL;
590
	}
590
	}
591
 
591
 
592
 
592
 
593
	/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
593
	/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
594
	   Cette fonction est appelée:
594
	   Cette fonction est appelée:
595
	   - une fois sur les champs de recherche avancées
595
	   - une fois sur les champs de recherche avancées
596
	   - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
596
	   - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
597
	   la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
597
	   la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
598
	   avec les traitements particuliers qui s'imposent
598
	   avec les traitements particuliers qui s'imposent
599
	   Par exemple: si l'on cherche "Languedoc", cela impliquera:
599
	   Par exemple: si l'on cherche "Languedoc", cela impliquera:
600
	   WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
600
	   WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
601
	   qui s'assure d'un pattern particulier */
601
	   qui s'assure d'un pattern particulier */
602
	static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
602
	static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
603
		if($parametres_autorises) { // filtrage de toute clef inconnue
603
		if($parametres_autorises) { // filtrage de toute clef inconnue
604
			$params = array_intersect_key($params, array_flip($parametres_autorises));
604
			$params = array_intersect_key($params, array_flip($parametres_autorises));
605
		}
605
		}
606
 
606
 
607
		$p['tri'] = self::unsetIfInvalid($params, 'tri', array('date_observation'));
607
		$p['tri'] = self::unsetIfInvalid($params, 'tri', array('date_observation'));
608
		$p['ordre'] = self::unsetIfInvalid($params, 'ordre', array('asc','desc'));
608
		$p['ordre'] = self::unsetIfInvalid($params, 'ordre', array('asc','desc'));
609
		$p['masque.referentiel'] = self::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
609
		$p['masque.referentiel'] = self::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
610
 
610
 
611
		// TODO: use filter_input(INPUT_GET);
611
		// TODO: use filter_input(INPUT_GET);
612
		// renvoie FALSE ou NULL si absent ou invalide
612
		// renvoie FALSE ou NULL si absent ou invalide
613
		$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
613
		$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
614
											 FILTER_VALIDATE_INT,
614
											 FILTER_VALIDATE_INT,
615
											 array('options' => array('default' => NULL,
615
											 array('options' => array('default' => NULL,
616
																	  'min_range' => 1,
616
																	  'min_range' => 1,
617
																	  'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
617
																	  'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
618
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
618
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
619
											 FILTER_VALIDATE_INT,
619
											 FILTER_VALIDATE_INT,
620
											 array('options' => array('default' => NULL,
620
											 array('options' => array('default' => NULL,
621
																	  'min_range' => 0,
621
																	  'min_range' => 0,
622
																	  'max_range' => _LISTE_OBS_MAX_ID_OBS)));
622
																	  'max_range' => _LISTE_OBS_MAX_ID_OBS)));
623
		if(isset($params['masque.departement'])) {
623
		if(isset($params['masque.departement'])) {
624
			// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
624
			// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
625
			// accept leading 0 ?
625
			// accept leading 0 ?
626
			// TODO; filter patterns like 555.
626
			// TODO; filter patterns like 555.
627
			if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
627
			if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
628
				$p['masque.departement'] = $params['masque.departement'];
628
				$p['masque.departement'] = $params['masque.departement'];
629
			}
629
			}
630
			// cf configurations/config_departements_bruts.ini
630
			// cf configurations/config_departements_bruts.ini
631
			elseif( !is_null($c) && ( $x = $c->getParametre(
631
			elseif( !is_null($c) && ( $x = $c->getParametre(
632
				strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
632
				strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
633
			))) {
633
			))) {
634
				$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
634
				$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
635
			}
635
			}
636
		}
636
		}
637
 
637
 
638
		if(isset($params['masque.date'])) {
638
		if(isset($params['masque.date'])) {
639
			// une année, TODO: masque.annee
639
			// une année, TODO: masque.annee
640
			if(is_numeric($params['masque.date'])) {
640
			if(is_numeric($params['masque.date'])) {
641
				$p['masque.date'] = $params['masque.date'];
641
				$p['masque.date'] = $params['masque.date'];
642
			}
642
			}
643
			elseif(strpos($params['masque.date'], '/' !== false) &&
643
			elseif(strpos($params['masque.date'], '/' !== false) &&
644
				   ($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
644
				   ($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
645
				$p['masque.date'] = $x;
645
				$p['masque.date'] = $x;
646
			}
646
			}
647
			elseif(strpos($params['masque.date'], '-' !== false) &&
647
			elseif(strpos($params['masque.date'], '-' !== false) &&
648
				   ($x = strtotime($params['masque.date'])) ) {
648
				   ($x = strtotime($params['masque.date'])) ) {
649
				$p['masque.date'] = $x;
649
				$p['masque.date'] = $x;
650
			}
650
			}
651
		}
651
		}
652
 
652
 
653
		$p['masque.nn'] = filter_var(@$params['masque.nn'],
653
		$p['masque.nn'] = filter_var(@$params['masque.nn'],
654
									 FILTER_VALIDATE_INT,
654
									 FILTER_VALIDATE_INT,
655
									 array('options' => array('default' => NULL,
655
									 array('options' => array('default' => NULL,
656
															  'min_range' => 0,
656
															  'min_range' => 0,
657
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
657
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
658
 
658
 
659
		$p['masque.nt'] = filter_var(@$params['masque.nt'],
659
		$p['masque.nt'] = filter_var(@$params['masque.nt'],
660
									 FILTER_VALIDATE_INT,
660
									 FILTER_VALIDATE_INT,
661
									 array('options' => array('default' => NULL,
661
									 array('options' => array('default' => NULL,
662
															  'min_range' => 0,
662
															  'min_range' => 0,
663
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
663
															  'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
664
 
664
 
665
 
665
 
666
		// TODO: should we really trim() ?
666
		// TODO: should we really trim() ?
667
 
667
 
668
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
668
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
669
		// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
669
		// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
670
 
670
 
671
		if(isset($params['masque.famille'])) {
671
		if(isset($params['masque.famille'])) {
672
			// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
672
			// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
673
			$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
673
			$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
674
																			"ASCII//TRANSLIT",
674
																			"ASCII//TRANSLIT",
675
																			$params['masque.famille']));
675
																			$params['masque.famille']));
676
		}
676
		}
677
 
677
 
678
		// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
678
		// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
679
		// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
679
		// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
680
		// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
680
		// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
681
		// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
681
		// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
682
		if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
682
		if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
683
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
683
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
684
		// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
684
		// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
685
		if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
685
		if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
686
 
686
 
687
		// idem pour id_zone_geo qui mappait à ce_zone_geo:
687
		// idem pour id_zone_geo qui mappait à ce_zone_geo:
688
		if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
688
		if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
689
			$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
689
			$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
690
		}
690
		}
691
 
691
 
692
		// masque.commune (zone_geo)
692
		// masque.commune (zone_geo)
693
		// TODO: que faire avec des '%' en INPUT ?
693
		// TODO: que faire avec des '%' en INPUT ?
694
		// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
694
		// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
695
		if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
695
		if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
696
 
696
 
697
		// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
697
		// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
698
		if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
698
		if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
699
		// sera trimmé plus tard, cf sqlAddConstraint
699
		// sera trimmé plus tard, cf sqlAddConstraint
700
		if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
700
		if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
701
 
701
 
702
		// masque.tag, idem que pour masque.genre et masque.commune
702
		// masque.tag, idem que pour masque.genre et masque.commune
703
		if(isset($params['masque.tag'])) {
703
		if(isset($params['masque.tag'])) {
704
			$x = explode(',',$params['masque.tag']);
704
			$x = explode(',',$params['masque.tag']);
705
			$x = array_map('trim', $x);
705
			$x = array_map('trim', $x);
706
			$p['masque.tag'] = implode('|', array_filter($x));
706
			$p['masque.tag'] = implode('|', array_filter($x));
707
		}
707
		}
708
 
708
 
709
		// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
709
		// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
710
		if(isset($params['masque.type'])) {
710
		if(isset($params['masque.type'])) {
711
			$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
711
			$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
712
														   array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
712
														   array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
713
		}
713
		}
714
 
714
 
715
 
715
 
716
		// TODO: masque (général)
716
		// TODO: masque (général)
717
 
717
 
718
 
718
 
719
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
719
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
720
		// TODO: PHP-5.3
720
		// TODO: PHP-5.3
721
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
721
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
722
	}
722
	}
723
 
723
 
724
	static function makeJSONHeader($total, $params, $url_service) {
724
	static function makeJSONHeader($total, $params, $url_service) {
-
 
725
	
725
		$prev_url = $next_url = NULL;
726
		$prev_url = $next_url = NULL;
-
 
727
		$url_service_sans_slash = substr($url_service, 0, -1);
-
 
728
 
-
 
729
		// aplatissons les params! - une seule couche cela dit, après débrouillez-vous
-
 
730
		$params_a_plat = $params;
-
 
731
		foreach ($params_a_plat as $cle_plate => $pap) {
-
 
732
			if (is_array($pap)) {
-
 
733
				$params_a_plat[$cle_plate] = implode(array_keys($pap), ',');
-
 
734
			}
-
 
735
		}
726
 
736
 
727
		$next_offset = $params['navigation.depart'] + $params['navigation.limite'];
737
		$next_offset = $params['navigation.depart'] + $params['navigation.limite'];
728
		if($next_offset < $total) {
738
		if($next_offset < $total) {
729
			$next_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
-
 
730
								http_build_query(array_merge($params, array('navigation.depart' => $next_offset))));
739
			$next_url = $url_service_sans_slash . '?' . http_build_query(array_merge($params_a_plat, array('navigation.depart' => $next_offset)));
731
		}
740
		}
732
 
741
 
733
		$prev_offset = $params['navigation.depart'] - $params['navigation.limite'];
742
		$prev_offset = $params['navigation.depart'] - $params['navigation.limite'];
734
		if($prev_offset > 0) {
743
		if($prev_offset > 0) {
735
			$prev_url = sprintf("http://%s?%s", isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $url_service,
-
 
736
								http_build_query(array_merge($params, array('navigation.depart' => $prev_offset))));
744
			$prev_url = $url_service_sans_slash . '?' . http_build_query(array_merge($params_a_plat, array('navigation.depart' => $prev_offset)));
737
		}
745
		}
738
 
746
 
739
		return array(
747
		return array(
740
			'masque' => http_build_query(array_diff_key($params, array_flip(array('navigation.depart', 'navigation.limite')))),
748
			'masque' => http_build_query(array_diff_key($params, array_flip(array('navigation.depart', 'navigation.limite')))),
741
			'total' => $total,
749
			'total' => $total,
742
			'depart' => $params['navigation.depart'],
750
			'depart' => $params['navigation.depart'],
743
			'limite' => $params['navigation.limite'],
751
			'limite' => $params['navigation.limite'],
744
			'href.precedent' => $prev_url,
752
			'href.precedent' => $prev_url,
745
			'href.suivant' => $next_url
753
			'href.suivant' => $next_url
746
		);
754
		);
747
	}
755
	}
748
}
756
}