Subversion Repositories eFlore/Applications.del

Rev

Rev 1490 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1490 Rev 1494
Line 26... Line 26...
26
define('_LISTE_OBS_MAX_BDTFX_NT', 1000000); // 44378 + 1000
26
define('_LISTE_OBS_MAX_BDTFX_NT', 1000000); // 44378 + 1000
27
// SELECT MAX(num_nom) FROM bdtfx_v2_00;
27
// SELECT MAX(num_nom) FROM bdtfx_v2_00;
28
define('_LISTE_OBS_MAX_BDTFX_NN', 1000000); // 120816 + 10000
28
define('_LISTE_OBS_MAX_BDTFX_NN', 1000000); // 120816 + 10000
Line 29... Line 29...
29
 
29
 
30
class DelTk {
30
class DelTk {
31
	static $parametres_autorises = array(
31
    static $parametres_autorises = array(
32
        'masque', 'masque.famille', 'masque.nn', 'masque.referentiel', // taxon
32
        'masque', 'masque.famille', 'masque.nn', 'masque.referentiel', // taxon
33
        'masque.genre', 'masque.espece', 'masque.ns', // nom_sel
33
        'masque.genre', 'masque.espece', 'masque.ns', // nom_sel
34
        'masque.commune', 'masque.departement', 'masque.id_zone_geo', // loc
34
        'masque.commune', 'masque.departement', 'masque.id_zone_geo', // loc
35
        'masque.auteur', 'masque.date', 'masque.tag', 'masque.type', // autres
35
        'masque.auteur', 'masque.date', 'masque.tag', 'masque.type', // autres
36
        // tri, offset
36
        // tri, offset
37
        'navigation.depart', 'navigation.limite',
37
        'navigation.depart', 'navigation.limite',
38
        'tri', 'ordre', // TODO: 'total=[yes]', 'fields=[x,y,...]'
38
        'tri', 'ordre', // TODO: 'total=[yes]', 'fields=[x,y,...]'
39
        // TODO: masque.annee, masque.insee (!= departement)
39
        // TODO: masque.annee, masque.insee (!= departement)
Line 40... Line 40...
40
	);
40
    );
41
 
41
 
42
	static $default_params = array(
42
    static $default_params = array(
Line 43... Line 43...
43
        'navigation.depart' => 0, 'navigation.limite' => 10,
43
        'navigation.depart' => 0, 'navigation.limite' => 10,
Line 44... Line 44...
44
        'tri' => 'date_transmission', 'ordre' => 'desc');
44
        'tri' => 'date_transmission', 'ordre' => 'desc');
45
 
45
 
46
 
46
 
47
    // input filtering
47
    // input filtering
48
 
48
 
49
 
49
 
50
	/* Construit un (vulgaire) abstract syntax tree:
50
    /* Construit un (vulgaire) abstract syntax tree:
51
	   "AND" => [ "tag1", "tag2" ]
51
       "AND" => [ "tag1", "tag2" ]
52
	   Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
52
       Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
53
	   nous aurions:
53
       nous aurions:
54
	   "AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
54
       "AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
55
 
55
 
56
	   Ici nous devons traiter les cas suivants:
56
       Ici nous devons traiter les cas suivants:
57
	   tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
57
       tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
58
	   Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
58
       Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
59
	   ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
59
       ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
60
 
60
 
61
	   Théorie:
61
       Théorie:
62
	   1) tags passés par "champ tag":
62
       1) tags passés par "champ tag":
63
	   - support du ET/OU, et explode par virgule.
63
       - support du ET/OU, et explode par virgule.
64
	   - si pas d'opérande détectée: "OU"
64
       - si pas d'opérande détectée: "OU"
65
 
65
 
66
	   2) tags passés par "recherche générale":
66
       2) tags passés par "recherche générale":
67
	   - support du ET/OU, et explode par whitespace.
67
       - support du ET/OU, et explode par whitespace.
68
	   - si pas d'opérande détectée: "ET"
68
       - si pas d'opérande détectée: "ET"
69
 
69
 
70
	   La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
70
       La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
71
	   Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
71
       Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
72
	   la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
72
       la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
73
	   * a,b,c => "a" $default_op "b" $default_op "c"
73
       * a,b,c => "a" $default_op "b" $default_op "c"
74
	   * a,b AND c => "a" AND "b" AND "c"
74
       * a,b AND c => "a" AND "b" AND "c"
75
	   * a OR b AND c,d => "a" AND "b" AND "c" AND "d"
75
       * a OR b AND c,d => "a" AND "b" AND "c" AND "d"
76
	   C'est à dire par ordre décroissant de priorité:
76
       C'est à dire par ordre décroissant de priorité:
77
	   1) opérande contenu dans la chaîne
77
       1) opérande contenu dans la chaîne
78
	   2) opérande par défaut
78
       2) opérande par défaut
79
	   3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
79
       3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
80
 
80
 
81
	   // TODO: support des parenthèses, imbrications & co: "(", ")"
81
       // TODO: support des parenthèses, imbrications & co: "(", ")"
82
	   // http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
82
       // http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
83
	   // http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
83
       // http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
84
 
84
 
85
	   @param $str: la chaîne à "parser"
85
       @param $str: la chaîne à "parser"
86
	   @param $default_op: "AND" ou "OR"
86
       @param $default_op: "AND" ou "OR"
87
	   @param $additional_sep: séparateur de mots:
87
       @param $additional_sep: séparateur de mots:
88
	*/
88
    */
89
	static function buildTagsAST($str = NULL, $default_op, $additional_sep = ',') {
89
    static function buildTagsAST($str = NULL, $default_op, $additional_sep = ',') {
90
		if(!$str) return;
90
	if(!$str) return;
91
		$words = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
91
	$words = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
92
 
92
 
93
		if(preg_match('/\b(ET|AND)\b/', $str)) $op = 'AND';
93
	if(preg_match('/\b(ET|AND)\b/', $str)) $op = 'AND';
94
		elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
94
	elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
95
		else $op = $default_op;
-
 
96
 
-
 
97
		if($additional_sep) {
-
 
98
			array_walk($words,
-
 
99
            create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
-
 
100
            $additional_sep);
-
 
101
		}
-
 
102
		$words = DelTk::array_flatten($words);
-
 
103
		$words = array_map('trim', $words);
-
 
104
		return array($op => array_filter($words));
-
 
105
	}
-
 
106
 
-
 
107
 
-
 
108
	static function array_flatten($arr) {
-
 
109
		$arr = array_values($arr);
-
 
110
		while (list($k,$v)=each($arr)) {
-
 
111
			if (is_array($v)) {
-
 
112
				array_splice($arr,$k,1,$v);
-
 
113
				next($arr);
-
 
114
			}
-
 
115
		}
-
 
116
		return $arr;
-
 
117
	}
-
 
118
 
-
 
119
	// supprime l'index du tableau des paramètres si sa valeur ne correspond pas
-
 
120
	// au spectre passé par $values.
-
 
121
	static function unsetIfInvalid(&$var, $index, $values) {
95
	else $op = $default_op;
-
 
96
 
-
 
97
	if($additional_sep) {
-
 
98
	    array_walk($words,
-
 
99
		       create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
Line -... Line 100...
-
 
100
		       $additional_sep);
-
 
101
	}
-
 
102
	$words = DelTk::array_flatten($words);
-
 
103
	$words = array_map('trim', $words);
-
 
104
	return array($op => array_filter($words));
-
 
105
    }
-
 
106
 
-
 
107
 
-
 
108
    static function array_flatten($arr) {
-
 
109
	$arr = array_values($arr);
Line 122... Line -...
122
		if(array_key_exists($index, $var)) {
-
 
123
			if(!in_array($var[$index], $values)) unset($var[$index]);
-
 
124
			else return $var[$index];
-
 
125
		}
-
 
126
		return NULL;
-
 
127
	}
110
	while (list($k,$v)=each($arr)) {
128
 
-
 
129
 
-
 
130
 
-
 
131
 
111
	    if (is_array($v)) {
132
	/* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
-
 
133
	   Cette fonction est appelée:
-
 
134
	   - une fois sur les champs de recherche avancées
-
 
135
	   - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
-
 
136
	   la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
-
 
137
	   avec les traitements particuliers qui s'imposent
-
 
138
	   Par exemple: si l'on cherche "Languedoc", cela impliquera:
112
		array_splice($arr,$k,1,$v);
139
	   WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
-
 
140
	   qui s'assure d'un pattern particulier */
-
 
141
	static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
-
 
142
		if($parametres_autorises) { // filtrage de toute clef inconnue
-
 
143
			$params = array_intersect_key($params, array_flip($parametres_autorises));
-
 
144
		}
-
 
145
 
-
 
146
		$p['tri'] = DelTK::unsetIfInvalid($params, 'tri', array('date_observation'));
-
 
147
		$p['ordre'] = DelTK::unsetIfInvalid($params, 'ordre', array('asc','desc'));
-
 
148
		$p['masque.referentiel'] = DelTK::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
-
 
149
 
-
 
150
		// TODO: use filter_input(INPUT_GET);
-
 
151
		// renvoie FALSE ou NULL si absent ou invalide
-
 
152
		$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
-
 
153
		FILTER_VALIDATE_INT,
-
 
154
		array('options' => array('default' => NULL,
-
 
155
		'min_range' => 1,
-
 
156
		'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
-
 
157
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
-
 
158
		FILTER_VALIDATE_INT,
-
 
159
		array('options' => array('default' => NULL,
-
 
160
		'min_range' => 0,
-
 
161
		'max_range' => _LISTE_OBS_MAX_ID_OBS)));
-
 
162
		if(isset($params['masque.departement'])) {
-
 
163
			// STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
-
 
164
			// accept leading 0 ?
-
 
165
			// TODO; filter patterns like 555.
-
 
166
			if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
-
 
167
				$p['masque.departement'] = $params['masque.departement'];
-
 
168
			}
113
		next($arr);
169
			// cf configurations/config_departements_bruts.ini
-
 
170
			elseif( !is_null($c) && ( $x = $c->getParametre(
-
 
171
				strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
-
 
172
			))) {
-
 
173
				$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
-
 
174
			}
-
 
175
		}
-
 
176
 
-
 
177
		if(isset($params['masque.date'])) {
-
 
178
			// une année, TODO: masque.annee
-
 
179
			if(is_numeric($params['masque.date'])) {
-
 
180
				$p['masque.date'] = $params['masque.date'];
-
 
181
			}
-
 
182
			elseif(strpos($params['masque.date'], '/' !== false) &&
-
 
183
			($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
-
 
184
				$p['masque.date'] = $x;
-
 
185
			}
-
 
186
			elseif(strpos($params['masque.date'], '-' !== false) &&
-
 
187
			($x = strtotime($params['masque.date'])) ) {
-
 
188
				$p['masque.date'] = $x;
-
 
189
			}
-
 
190
		}
-
 
191
 
-
 
192
		$p['masque.nn'] = filter_var(@$params['masque.nn'],
-
 
193
		FILTER_VALIDATE_INT,
-
 
194
		array('options' => array('default' => NULL,
-
 
195
		'min_range' => 0,
-
 
196
		'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
-
 
197
 
-
 
198
		$p['masque.nt'] = filter_var(@$params['masque.nt'],
-
 
199
		FILTER_VALIDATE_INT,
-
 
200
		array('options' => array('default' => NULL,
-
 
201
		'min_range' => 0,
-
 
202
		'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
-
 
203
 
-
 
204
 
-
 
205
		// TODO: should we really trim() ?
-
 
206
 
-
 
207
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
-
 
208
		// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
-
 
209
 
-
 
210
		if(isset($params['masque.famille'])) {
-
 
211
			// mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
-
 
212
			$p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
-
 
213
			"ASCII//TRANSLIT",
-
 
214
			$params['masque.famille']));
-
 
215
		}
-
 
216
 
-
 
217
		// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
-
 
218
		// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
-
 
219
		// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
-
 
220
		// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
-
 
221
		if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
-
 
222
		if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
-
 
223
		// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
-
 
224
		if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
-
 
225
 
-
 
226
		// idem pour id_zone_geo qui mappait à ce_zone_geo:
-
 
227
		if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
-
 
228
			$p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
-
 
229
		}
-
 
230
 
114
	    }
231
		// masque.commune (zone_geo)
-
 
232
		// TODO: que faire avec des '%' en INPUT ?
-
 
233
		// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
-
 
234
		if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
-
 
235
 
115
	}
236
		// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
-
 
237
		if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
-
 
238
		// sera trimmé plus tard, cf sqlAddConstraint
-
 
239
		if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
-
 
240
 
-
 
241
		// masque.tag, idem que pour masque.genre et masque.commune
-
 
242
		if(isset($params['masque.tag'])) {
-
 
243
			$x = explode(',',$params['masque.tag']);
-
 
244
			$x = array_map('trim', $x);
-
 
245
			$p['masque.tag'] = implode('|', array_filter($x));
-
 
246
		}
-
 
247
 
-
 
248
		// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
-
 
249
		if(isset($params['masque.type'])) {
-
 
250
			$p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
-
 
251
			array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
-
 
252
		}
116
	return $arr;
-
 
117
    }
-
 
118
 
Line 253... Line -...
253
 
-
 
Line -... Line 119...
-
 
119
    // supprime l'index du tableau des paramètres si sa valeur ne correspond pas
-
 
120
    // au spectre passé par $values.
254
 
121
    static function unsetIfInvalid(&$var, $index, $values) {
-
 
122
	if(array_key_exists($index, $var)) {
-
 
123
	    if(!in_array($var[$index], $values)) unset($var[$index]);
-
 
124
	    else return $var[$index];
-
 
125
	}
-
 
126
	return NULL;
255
		// TODO: masque (général)
127
    }
-
 
128
 
-
 
129
 
-
 
130
 
-
 
131
 
-
 
132
    /* Filtre et valide les paramètres reconnus. Effectue *toute* la sanitization *sauf* l'escape-string
-
 
133
       Cette fonction est appelée:
-
 
134
       - une fois sur les champs de recherche avancées
-
 
135
       - une fois sur le masque général si celui-ci à été spécifié. Dans ce cas,
-
 
136
       la chaîne générale saisie est utilisée comme valeur pour chacun des champs particuliers
-
 
137
       avec les traitements particuliers qui s'imposent
256
 
138
       Par exemple: si l'on cherche "Languedoc", cela impliquera:
-
 
139
       WHERE (nom_sel like "Languedoc" OR nom_ret ... OR ...) mais pas masque.date ou masque.departement
-
 
140
       qui s'assure d'un pattern particulier */
-
 
141
    static function requestFilterParams(Array $params, $parametres_autorises = NULL, Conteneur $c = NULL /* pour la récup des départements */ ) {
-
 
142
	if($parametres_autorises) { // filtrage de toute clef inconnue
-
 
143
	    $params = array_intersect_key($params, array_flip($parametres_autorises));
-
 
144
	}
-
 
145
 
-
 
146
	$p['tri'] = DelTK::unsetIfInvalid($params, 'tri', array('date_observation'));
-
 
147
	$p['ordre'] = DelTK::unsetIfInvalid($params, 'ordre', array('asc','desc'));
-
 
148
	$p['masque.referentiel'] = DelTK::unsetIfInvalid($params, 'masque.referentiel', array('bdtfx','bdtxa','isfan'));
257
 
149
 
-
 
150
	// TODO: use filter_input(INPUT_GET);
258
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
151
	// renvoie FALSE ou NULL si absent ou invalide
-
 
152
	$p['navigation.limite'] = filter_var(@$params['navigation.limite'],
-
 
153
					     FILTER_VALIDATE_INT,
-
 
154
					     array('options' => array('default' => NULL,
-
 
155
								      'min_range' => 1,
-
 
156
								      'max_range' => _LISTE_OBS_MAX_RESULT_LIMIT)));
-
 
157
	$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
259
		// TODO: PHP-5.3
158
					     FILTER_VALIDATE_INT,
-
 
159
					     array('options' => array('default' => NULL,
-
 
160
								      'min_range' => 0,
-
 
161
								      'max_range' => _LISTE_OBS_MAX_ID_OBS)));
260
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
162
	if(isset($params['masque.departement'])) {
Line -... Line 163...
-
 
163
	    // STRING: 0 -> 95, 971 -> 976, 2A + 2B (./services/configurations/config_departements_bruts.ini)
-
 
164
	    // accept leading 0 ?
-
 
165
	    // TODO; filter patterns like 555.
-
 
166
	    if(preg_match(';^(\d{2}|\d{3}|2a|2b)$;i', $params['masque.departement'])) {
-
 
167
		$p['masque.departement'] = $params['masque.departement'];
-
 
168
	    }
-
 
169
	    // cf configurations/config_departements_bruts.ini
-
 
170
	    elseif( !is_null($c) && ( $x = $c->getParametre(
-
 
171
		strtolower(str_replace(' ','-',iconv("UTF-8", "ASCII//TRANSLIT", $params['masque.departement'])))
-
 
172
	    ))) {
-
 
173
		$p['masque.departement'] = sprintf("INSEE-C:%02d___", $x);
-
 
174
	    }
-
 
175
	}
-
 
176
 
Line -... Line 177...
-
 
177
	if(isset($params['masque.date'])) {
-
 
178
	    // une année, TODO: masque.annee
-
 
179
	    if(is_numeric($params['masque.date'])) {
-
 
180
		$p['masque.date'] = $params['masque.date'];
-
 
181
	    }
-
 
182
	    elseif(strpos($params['masque.date'], '/' !== false) &&
-
 
183
		   ($x = strtotime(str_replace('/','-',$params['masque.date'])))) {
-
 
184
		$p['masque.date'] = $x;
-
 
185
	    }
-
 
186
	    elseif(strpos($params['masque.date'], '-' !== false) &&
-
 
187
		   ($x = strtotime($params['masque.date'])) ) {
-
 
188
		$p['masque.date'] = $x;
-
 
189
	    }
-
 
190
	}
-
 
191
 
-
 
192
	$p['masque.nn'] = filter_var(@$params['masque.nn'],
-
 
193
				     FILTER_VALIDATE_INT,
-
 
194
				     array('options' => array('default' => NULL,
-
 
195
							      'min_range' => 0,
-
 
196
							      'max_range' => _LISTE_OBS_MAX_BDTFX_NN)));
-
 
197
 
-
 
198
	$p['masque.nt'] = filter_var(@$params['masque.nt'],
-
 
199
				     FILTER_VALIDATE_INT,
-
 
200
				     array('options' => array('default' => NULL,
Line 261... Line 201...
261
	}
201
							      'min_range' => 0,
262
 
202
							      'max_range' => _LISTE_OBS_MAX_BDTFX_NT)));
263
 
203
 
264
 
204
 
265
    // SQL helpers
205
	// TODO: should we really trim() ?
266
 
206
 
-
 
207
	if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
267
	/* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
208
	// if(isset($params['masque.texte'])) $p['masque.texte'] = trim($params['masque.texte']);
-
 
209
 
268
	   Il faut donc utiliser la syntaxe IFNULL(%s, "").
210
	if(isset($params['masque.famille'])) {
-
 
211
	    // mysql -N<<<"SELECT DISTINCT famille FROM bdtfx_v1_02;"|sed -r "s/(.)/\1\n/g"|sort -u|tr -d "\n"
269
	   (Cette fonction effectue aussi l'implode() "final" */
212
	    $p['masque.famille'] = preg_replace('/[^a-zA-Z %_]/', '', iconv("UTF-8",
-
 
213
									    "ASCII//TRANSLIT",
Line -... Line 214...
-
 
214
									    $params['masque.famille']));
-
 
215
	}
-
 
216
 
270
	static function sqlAddIfNullPourConcat($tab) {
217
	// masque.genre est un alias pour masque.ns (nom_sel), mais permet de rajouter une clause supplémentaire
-
 
218
	// sur nom_sel. Précédemment: WHERE nom_sel LIKE '%<masque.genre>% %'.
-
 
219
	// Désormais masque.genre doit être intégralement spécifié, les caractères '%' et '_' seront interprétés.
271
		// XXX: PHP-5.3
220
	// Attention toutefois car la table del_observation intègre des nom_sel contenant '_'
-
 
221
	if(isset($params['masque.genre'])) $p['masque.genre'] = trim($params['masque.genre']);
-
 
222
	if(isset($params['masque.ns'])) $p['masque.ns'] = trim($params['masque.ns']);
-
 
223
	// masque.espece n'était pas déclaré dans la "where" mais utilisé via config + switch//default
-
 
224
	if(isset($params['masque.espece'])) $p['masque.espece'] = trim($params['masque.espece']);
-
 
225
 
-
 
226
	// idem pour id_zone_geo qui mappait à ce_zone_geo:
-
 
227
	if(isset($params['masque.id_zone_geo']) && preg_match(';^(INSEE-C:\d{5}|\d{2})$;', $params['masque.id_zone_geo'])) {
-
 
228
	    $p['masque.id_zone_geo'] = $params['masque.id_zone_geo'];
-
 
229
	}
Line -... Line 230...
-
 
230
 
-
 
231
	// masque.commune (zone_geo)
272
		return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
232
	// TODO: que faire avec des '%' en INPUT ?
-
 
233
	// Le masque doit *permettre* une regexp et non l'imposer. Charge au client de faire son travail
273
	}
234
	if(isset($params['masque.commune'])) $p['masque.commune'] = str_replace(array('-',' '), '_', $params['masque.commune']);
Line -... Line 235...
-
 
235
 
-
 
236
	// masque.auteur: peut-être un id, un courriel, ou un nom ou prénom, ...
-
 
237
	if(isset($params['masque.auteur'])) $p['masque.auteur'] = trim($params['masque.auteur']);
-
 
238
	// sera trimmé plus tard, cf sqlAddConstraint
-
 
239
	if(isset($params['masque'])) $p['masque'] = trim($params['masque']);
-
 
240
 
-
 
241
	// masque.tag, idem que pour masque.genre et masque.commune
-
 
242
	if(isset($params['masque.tag'])) {
-
 
243
	    $x = explode(',',$params['masque.tag']);
-
 
244
	    $x = array_map('trim', $x);
-
 
245
	    $p['masque.tag'] = implode('|', array_filter($x));
-
 
246
	}
-
 
247
 
-
 
248
	// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
-
 
249
	if(isset($params['masque.type'])) {
-
 
250
	    $p['masque.type'] = array_flip(array_intersect(array_filter(explode(';', $params['masque.type'])),
-
 
251
							   array('adeterminer', 'aconfirmer', 'endiscussion', 'validees')));
-
 
252
	}
-
 
253
 
-
 
254
 
-
 
255
	// TODO: masque (général)
-
 
256
 
-
 
257
 
-
 
258
	// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
-
 
259
	// TODO: PHP-5.3
-
 
260
	return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
-
 
261
    }
-
 
262
 
-
 
263
 
-
 
264
 
-
 
265
    // SQL helpers
-
 
266
 
-
 
267
    /* Lorsque l'on concatène des champs, un seul NULL prend le dessus,
-
 
268
       Il faut donc utiliser la syntaxe IFNULL(%s, "").
-
 
269
       (Cette fonction effectue aussi l'implode() "final" */
-
 
270
    static function sqlAddIfNullPourConcat($tab) {
-
 
271
	// XXX: PHP-5.3
-
 
272
	return implode(',',array_map(create_function('$a', 'return "IFNULL($a, \"\")";'), $tab));
-
 
273
    }
-
 
274
 
-
 
275
 
-
 
276
 
-
 
277
    /* Converti un tableau associatif et un préfix optionnel en une chaîne de champs adéquate
-
 
278
       à un SELECT MySQL.
-
 
279
       $select (optionnel) restreint les champs mappés aux valeurs de $select.
-
 
280
       Si $select n'est pas fourni, toutes les clefs présentes dans $map seront présentes dans
-
 
281
       le SELECT en sortie */
-
 
282
    static function sqlFieldsToAlias($map, $select = NULL, $prefix = NULL) {
-
 
283
	if($select) $arr = array_intersect_key($map, array_flip($select));
-
 
284
	else $arr = $map;
-
 
285
	$keys = array_keys($arr);
-
 
286
 
-
 
287
	if($prefix) array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
-
 
288
	else array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
-
 
289
 
Line 274... Line 290...
274
 
290
	return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
275
 
291
    }
276
 
292
 
-
 
293
 
-
 
294
 
-
 
295
    /*
277
	/* Converti un tableau associatif et un préfix optionnel en une chaîne de champs adéquate
296
      Retourne une clause where du style:
278
	   à un SELECT MySQL.
297
      CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
279
	   $select (optionnel) restreint les champs mappés aux valeurs de $select.
298
      Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
280
	   Si $select n'est pas fourni, toutes les clefs présentes dans $map seront présentes dans
-
 
281
	   le SELECT en sortie */
-
 
282
	static function sqlFieldsToAlias($map, $select = NULL, $prefix = NULL) {
-
 
283
		if($select) $arr = array_intersect_key($map, array_flip($select));
-
 
284
		else $arr = $map;
-
 
285
		$keys = array_keys($arr);
-
 
286
 
-
 
287
		if($prefix) array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
-
 
288
		else array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
-
 
289
 
-
 
290
		return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
-
 
291
	}
-
 
292
 
-
 
293
 
-
 
294
 
-
 
295
	/*
-
 
296
	  Retourne une clause where du style:
-
 
297
	  CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
-
 
298
	  Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
-
 
299
	  car cette information est redondante dans cel_image et devrait être supprimée.
299
      car cette information est redondante dans cel_image et devrait être supprimée.
300
	*/
300
    */
301
	static function addAuteursConstraint($val, $db, &$where) {
301
    static function addAuteursConstraint($val, $db, &$where) {
302
		@list($a, $b) = explode(' ', $val, 2);
302
	@list($a, $b) = explode(' ', $val, 2);
303
		// un seul terme
303
	// un seul terme
304
		$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
304
	$champs_n = array('du.prenom', // info user authentifié de l'obs depuis l'annuaire
305
		'vdi.prenom_utilisateur', // info user anonyme de l'obs
305
			  'vdi.prenom_utilisateur', // info user anonyme de l'obs
306
		/* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
306
			  /* 'vdi.i_prenom_utilisateur' */ ); // info user anonyme de l'image
307
		$champs_p = array('du.nom', // idem pour le nom
307
	$champs_p = array('du.nom', // idem pour le nom
308
		'vdi.nom_utilisateur',
308
			  'vdi.nom_utilisateur',
309
		/* 'vdi.i_nom_utilisateur' */ );
309
			  /* 'vdi.i_nom_utilisateur' */ );
310
 
-
 
311
		/*
310
 
-
 
311
	/*
Line 312... Line 312...
312
		  Note: pour l'heure, étant donnés:
312
	  Note: pour l'heure, étant donnés:
313
		  - les CONVERT() de la VIEW del_utilisateur
313
	  - les CONVERT() de la VIEW del_utilisateur
314
		  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
314
	  - DEFAULT CHARSET=latin1 pour tela_prod_v4.annuaire_tela
315
		  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
315
	  - DEFAULT CHARSET=utf8 pour tb_cel.cel_obs
316
		  et l'âge du capitaine...
316
	  et l'âge du capitaine...
317
		  - REGEXP est case-sensitive, et collate les caractères accentués
317
	  - REGEXP est case-sensitive, et collate les caractères accentués
318
		  - LIKE est case-insensitive, et collate les caractères accentués
318
	  - LIKE est case-insensitive, et collate les caractères accentués
319
		*/
319
	*/
320
		if(! $b) {
320
	if(! $b) {
321
			$where[] = sprintf('CONCAT(%s,%s) LIKE %s',
321
	    $where[] = sprintf('CONCAT(%s,%s) LIKE %s',
322
			DelTk::sqlAddIfNullPourConcat($champs_n),
322
			       DelTk::sqlAddIfNullPourConcat($champs_n),
323
			DelTk::sqlAddIfNullPourConcat($champs_p),
323
			       DelTk::sqlAddIfNullPourConcat($champs_p),
324
			$db->proteger("%".$val."%"));
324
			       $db->proteger("%".$val."%"));
325
		}
325
	}
326
		else {
326
	else {
327
			$where[] = sprintf('(CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s)',
327
	    $where[] = sprintf('(CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s)',
328
			DelTk::sqlAddIfNullPourConcat($champs_n),
328
			       DelTk::sqlAddIfNullPourConcat($champs_n),
329
			DelTk::sqlAddIfNullPourConcat($champs_p),
329
			       DelTk::sqlAddIfNullPourConcat($champs_p),
330
			$db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
330
			       $db->proteger("%" . $a . "%"), $db->proteger("%" . $b . "%"));
331
		}
331
	}
332
	}
332
    }
333
 
333
 
334
 
334
 
335
 
335
 
336
 
336
 
337
 
337
 
338
	/**
338
    /**
339
	 * - Rempli le tableau des contraintes "where" et "join" nécessaire
339
     * - Rempli le tableau des contraintes "where" et "join" nécessaire
340
	 * à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
340
     * à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
341
	 *
341
     *
342
	 * Attention, cela signifie que toutes les tables ne sont pas *forcément*
342
     * Attention, cela signifie que toutes les tables ne sont pas *forcément*
343
	 * join'ées, par exemple si aucune contrainte ne le nécessite.
343
     * join'ées, par exemple si aucune contrainte ne le nécessite.
344
	 * $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
344
     * $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
345
	 * id d'observation qui match.
345
     * id d'observation qui match.
346
	 * Pour la récupération effective de "toutes" les données correspondante, il faut
346
     * Pour la récupération effective de "toutes" les données correspondante, il faut
347
	 * réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
347
     * réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
348
	 *
-
 
349
	 * Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
-
 
350
	 * les JOIN qui sont utilisés.
-
 
351
	 * le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
-
 
352
	 * le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
-
 
353
	 *
-
 
354
	 * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
-
 
355
	 * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
-
 
356
	 */
-
 
357
	static function sqlAddConstraint($p, $db, &$req) {
-
 
358
		if(!empty($p['masque.auteur'])) {
-
 
359
			// id du poster de l'obs
-
 
360
			$req['join'][] = 'LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = vdi.ce_utilisateur';
-
 
361
			// id du poster de l'image... NON, c'est le même que le posteur de l'obs
-
 
362
			// Cette jointure de table est ignoré ci-dessous pour les recherches d'auteurs
-
 
363
			// $req['join'][] = 'LEFT JOIN del_utilisateur AS dui ON dui.id_utilisateur = vdi.i_ce_utilisateur';
-
 
364
 
-
 
365
			if(is_numeric($p['masque.auteur'])) {
-
 
366
				$req['where'][] = sprintf('(du.id_utilisateur = %1$d OR vdi.id_utilisateur = %1$d)', $p['masque.auteur']);
-
 
367
			}
-
 
368
			elseif(preg_match(';^.{5,}@[a-z0-9-.]{5,}$;i', $p['masque.auteur'])) {
-
 
369
				$req['where'][] = sprintf('(du.courriel LIKE %1$s OR vdi.courriel LIKE %1$s )',
-
 
370
				$db->proteger($p['masque.auteur'] . '%'));
-
 
371
			}
-
 
372
			else {
-
 
373
				DelTk::addAuteursConstraint($p['masque.auteur'], $db, $req['where']);
-
 
374
			}
-
 
375
		}
-
 
376
 
-
 
377
		if(!empty($p['masque.date'])) {
-
 
378
			if(is_integer($p['masque.date']) && $p['masque.date'] < 2030 && $p['masque.date'] > 1600) {
-
 
379
				$req['where'][] = sprintf("YEAR(vdi.date_observation) = %d", $p['masque.date']);
-
 
380
			}
-
 
381
			else {
-
 
382
				$req['where'][] = sprintf("DATE_FORMAT(vdi.date_observation, '%%Y-%%m-%%d') = %s",
-
 
383
				$db->proteger(strftime('%Y-%m-%d', $p['masque.date'])));
-
 
384
			}
-
 
385
		}
-
 
386
 
-
 
387
		// TODO: avoir des champs d'entrée distinct
-
 
388
		if(!empty($p['masque.departement'])) {
-
 
389
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger('INSEE-C:'.$p['masque.departement']));
-
 
390
		}
-
 
391
		if(!empty($p['masque.id_zone_geo'])) {
-
 
392
			$req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger($p['masque.id_zone_geo']));
-
 
393
		}
-
 
394
		if(!empty($p['masque.genre'])) {
-
 
395
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger('%' . $p['masque.genre'].'% %');
-
 
396
		}
-
 
397
		if(!empty($p['masque.famille'])) {
-
 
398
			$req['where'][] = 'vdi.famille = '.$db->proteger($p['masque.famille']);
-
 
399
		}
-
 
400
		if(!empty($p['masque.ns'])) {
-
 
401
			$req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger($p['masque.ns'].'%');
-
 
402
		}
348
     *
403
		if(!empty($p['masque.nn'])) {
-
 
404
			$req['where'][] = sprintf('vdi.nom_sel_nn = %1$d OR vdi.nom_ret_nn = %1$d', $p['masque.nn']);
-
 
405
		}
-
 
406
		if(!empty($p['masque.referentiel'])) {
-
 
407
			$req['where'][] = sprintf('vdi.nom_referentiel LIKE %s', $db->proteger($p['masque.referentiel'].'%'));
-
 
408
		}
-
 
409
		if(!empty($p['masque.commune'])) {
-
 
410
			$req['where'][] = 'vdi.zone_geo LIKE '.$db->proteger($p['masque.commune'].'%');
-
 
411
		}
349
     * Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
412
		if(!empty($p['masque.tag'])) {
-
 
Line -... Line 350...
-
 
350
     * les JOIN qui sont utilisés.
-
 
351
     * le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
-
 
352
     * le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
-
 
353
     *
-
 
354
     * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
-
 
355
     * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
-
 
356
     */
-
 
357
    static function sqlAddConstraint($p, $db, &$req) {
-
 
358
	if(!empty($p['masque.auteur'])) {
Line -... Line 359...
-
 
359
	    // id du poster de l'obs
-
 
360
	    $req['join'][] = 'LEFT JOIN del_utilisateur AS du ON du.id_utilisateur = vdi.ce_utilisateur';
-
 
361
	    // id du poster de l'image... NON, c'est le même que le posteur de l'obs
-
 
362
	    // Cette jointure de table est ignoré ci-dessous pour les recherches d'auteurs
-
 
363
	    // $req['join'][] = 'LEFT JOIN del_utilisateur AS dui ON dui.id_utilisateur = vdi.i_ce_utilisateur';
-
 
364
 
-
 
365
	    if(is_numeric($p['masque.auteur'])) {
-
 
366
		$req['where'][] = sprintf('(du.id_utilisateur = %1$d OR vdi.id_utilisateur = %1$d)', $p['masque.auteur']);
-
 
367
	    }
-
 
368
	    elseif(preg_match(';^.{5,}@[a-z0-9-.]{5,}$;i', $p['masque.auteur'])) {
-
 
369
		$req['where'][] = sprintf('(du.courriel LIKE %1$s OR vdi.courriel LIKE %1$s )',
-
 
370
					  $db->proteger($p['masque.auteur'] . '%'));
-
 
371
	    }
-
 
372
	    else {
-
 
373
		DelTk::addAuteursConstraint($p['masque.auteur'], $db, $req['where']);
-
 
374
	    }
-
 
375
	}
-
 
376
 
-
 
377
	if(!empty($p['masque.date'])) {
-
 
378
	    if(is_integer($p['masque.date']) && $p['masque.date'] < 2030 && $p['masque.date'] > 1600) {
-
 
379
		$req['where'][] = sprintf("YEAR(vdi.date_observation) = %d", $p['masque.date']);
-
 
380
	    }
-
 
381
	    else {
-
 
382
		$req['where'][] = sprintf("DATE_FORMAT(vdi.date_observation, '%%Y-%%m-%%d') = %s",
-
 
383
					  $db->proteger(strftime('%Y-%m-%d', $p['masque.date'])));
-
 
384
	    }
Line -... Line 385...
-
 
385
	}
-
 
386
 
-
 
387
	// TODO: avoir des champs d'entrée distinct
-
 
388
	if(!empty($p['masque.departement'])) {
-
 
389
	    $req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger('INSEE-C:'.$p['masque.departement']));
-
 
390
	}
-
 
391
	if(!empty($p['masque.id_zone_geo'])) {
-
 
392
	    $req['where'][] = sprintf("vdi.ce_zone_geo = %s", $db->proteger($p['masque.id_zone_geo']));
-
 
393
	}
-
 
394
	if(!empty($p['masque.genre'])) {
-
 
395
	    $req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger('%' . $p['masque.genre'].'% %');
-
 
396
	}
Line -... Line 397...
-
 
397
	if(!empty($p['masque.famille'])) {
-
 
398
	    $req['where'][] = 'vdi.famille = '.$db->proteger($p['masque.famille']);
-
 
399
	}
-
 
400
	if(!empty($p['masque.ns'])) {
Line 413... Line -...
413
			// TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
-
 
414
			// i_mots_cles_texte provient de la VIEW v_del_image
-
 
415
			if(isset($p['masque.tag']['AND'])) {
-
 
416
                /* Lorsque nous interprêtons la chaîne provenant du masque général (cf: buildTagsAST($p['masque'], 'OR', ' ') dans sqlAddMasqueConstraint()),
-
 
417
                   nous sommes splittés par espace. Cependant, assurons que si une virgule à été saisie, nous n'aurons pas le motif
-
 
418
                   " AND CONCAT(mots_cles_texte, i_mots_cles_texte) REGEXP ',' " dans notre requête.
-
 
419
                   XXX: Au 12/11/2013, une recherche sur tag depuis le masque général implique un OU, donc le problème ne se pose pas ici */
-
 
420
				$subwhere = array();
-
 
421
				foreach($p['masque.tag']['AND'] as $tag) {
-
 
422
                    if(trim($tag) == ',') continue;
-
 
423
 
-
 
424
					$subwhere[] = sprintf(
-
 
425
						'LOWER(CONCAT(%s)) REGEXP %s',
-
 
426
						DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
-
 
427
						$db->proteger(strtolower($tag)));
-
 
428
				}
-
 
429
				$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
-
 
430
			}
-
 
431
			else {
401
	    $req['where'][] = 'vdi.nom_sel LIKE '.$db->proteger($p['masque.ns'].'%');
432
				$req['where'][] = sprintf(
402
	}
433
					'LOWER(CONCAT(%s)) REGEXP %s',
403
	if(!empty($p['masque.nn'])) {
434
					DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
-
 
435
					$db->proteger(strtolower(implode('|', $p['masque.tag']['OR']))));
-
 
436
			}
-
 
437
		}
-
 
438
    }
-
 
439
 
-
 
440
 
-
 
441
 
-
 
442
 
-
 
443
 
-
 
444
 
404
	    $req['where'][] = sprintf('vdi.nom_sel_nn = %1$d OR vdi.nom_ret_nn = %1$d', $p['masque.nn']);
Line 445... Line -...
445
 
-
 
446
    // formatage de réponse HTTP
405
	}
-
 
406
	if(!empty($p['masque.referentiel'])) {
-
 
407
	    $req['where'][] = sprintf('vdi.nom_referentiel LIKE %s', $db->proteger($p['masque.referentiel'].'%'));
-
 
408
	}
-
 
409
	if(!empty($p['masque.commune'])) {
-
 
410
	    $req['where'][] = 'vdi.zone_geo LIKE '.$db->proteger($p['masque.commune'].'%');
-
 
411
	}
-
 
412
    }
-
 
413
 
-
 
414
 
-
 
415