[SPIP] +2.1.12
[velocampus/web/www.git] / www / ecrire / public / criteres.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2011 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 //
14 // Definition des {criteres} d'une boucle
15 //
16
17 // Une Regexp reperant une chaine produite par le compilateur,
18 // souvent utilisee pour faire de la concatenation lors de la compilation
19 // plutot qu'a l'execution, i.e. pour remplacer 'x'.'y' par 'xy'
20
21 define('_CODE_QUOTE', ",^(\n//[^\n]*\n)? *'(.*)' *$,");
22
23 if (!defined('_ECRIRE_INC_VERSION')) return;
24
25 // {racine}
26 // http://www.spip.net/@racine
27 // http://doc.spip.org/@critere_racine_dist
28 function critere_racine_dist($idb, &$boucles, $crit) {
29 global $exceptions_des_tables;
30 $not = $crit->not;
31 $boucle = &$boucles[$idb];
32 $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
33 $exceptions_des_tables[$boucle->id_table]['id_parent'] :
34 'id_parent';
35
36 if ($not)
37 return (array('zbug_critere_inconnu', array('critere' => $not . $crit->op)));
38
39 $boucle->where[]= array("'='", "'$boucle->id_table." . "$id_parent'", 0);
40 }
41
42 // {exclus}
43 // http://www.spip.net/@exclus
44 // http://doc.spip.org/@critere_exclus_dist
45 function critere_exclus_dist($idb, &$boucles, $crit) {
46 $not = $crit->not;
47 $boucle = &$boucles[$idb];
48 $id = $boucle->primary;
49
50 if ($not OR !$id)
51 return (array('zbug_critere_inconnu', array('critere' => $not . $crit->op)));
52 $arg = kwote(calculer_argument_precedent($idb, $id, $boucles));
53 $boucle->where[]= array("'!='", "'$boucle->id_table." . "$id'", $arg);
54 }
55
56 // {doublons} ou {unique}
57 // http://www.spip.net/@doublons
58 // attention: boucle->doublons designe une variable qu'on affecte
59 // http://doc.spip.org/@critere_doublons_dist
60 function critere_doublons_dist($idb, &$boucles, $crit) {
61 $boucle = &$boucles[$idb];
62 $primary = $boucle->primary;
63
64 if (!$primary OR strpos($primary,',')) {
65 return (array('zbug_doublon_sur_table_sans_cle_primaire'));
66 }
67
68 $not = ($crit->not ? '' : 'NOT');
69
70 $nom = !isset($crit->param[0]) ? "''" : calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
71 // mettre un tableau pour que ce ne soit pas vu comme une constante
72
73 $nom = "'" .
74 $boucle->type_requete .
75 "'" .
76 ($nom == "''" ? '' : " . $nom");
77
78 $debutdoub = '$doublons['
79 . (!$not ? '' : ($boucle->doublons . "[]= "));
80
81 $findoub = "($nom)]";
82
83 $debin = "sql_in('" . $boucle->id_table . '.' . $primary . "', ";
84
85 $suitin = $debin . $debutdoub;
86
87 // si autre critere doublon, fusionner pour avoir un seul In
88 foreach ($boucle->where as $k => $w) {
89 if (strpos($w[0], $suitin) ===0) {
90 $boucle->where[$k][0] = $debin . $debutdoub . $findoub . ' . ' . substr($w[0],strlen($debin));
91 return;
92 }
93 }
94 $boucle->where[]= array($suitin . $findoub . ", '" . $not . "')");
95
96
97
98
99 # la ligne suivante avait l'intention d'eviter une collecte deja faite
100 # mais elle fait planter une boucle a 2 critere doublons:
101 # {!doublons A}{doublons B}
102 # (de http://article.gmane.org/gmane.comp.web.spip.devel/31034)
103 # if ($crit->not) $boucle->doublons = "";
104 }
105
106 // {lang_select}
107 // http://www.spip.net/@lang_select
108 // http://doc.spip.org/@critere_lang_select_dist
109 function critere_lang_select_dist($idb, &$boucles, $crit) {
110 if (!($param = $crit->param[1][0]->texte)) $param = 'oui';
111 if ($crit->not) $param = ($param=='oui') ? 'non' : 'oui';
112 $boucle = &$boucles[$idb];
113 $boucle->lang_select = $param;
114 }
115
116 // {debut_xxx}
117 // http://www.spip.net/@debut_
118 // http://doc.spip.org/@critere_debut_dist
119 function critere_debut_dist($idb, &$boucles, $crit) {
120 list($un, $deux) = $crit->param;
121 $un = $un[0]->texte;
122 $deux = $deux[0]->texte;
123 if ($deux) {
124 $boucles[$idb]->limit = 'intval($Pile[0]["debut' .
125 $un .
126 '"]) . ",' .
127 $deux .
128 '"' ;
129 } else calculer_critere_DEFAUT_dist($idb, $boucles, $crit);
130 }
131
132 // {pagination}
133 // {pagination 20}
134 // {pagination #ENV{pages,5}} etc
135 // {pagination 20 #ENV{truc,chose}} pour utiliser la variable debut_#ENV{truc,chose}
136 // http://www.spip.net/@pagination
137 // http://doc.spip.org/@critere_pagination_dist
138 function critere_pagination_dist($idb, &$boucles, $crit) {
139
140 $boucle = &$boucles[$idb];
141 // definition de la taille de la page
142 $pas = !isset($crit->param[0][0]) ? "''" : calculer_liste(array($crit->param[0][0]), array(), $boucles, $boucle->id_parent);
143
144 if (!preg_match(_CODE_QUOTE, $pas, $r)) {
145 $pas = "((\$a = intval($pas)) ? \$a : 10)";
146 } else {
147 $r = intval($r[2]);
148 $pas = strval($r ? $r : 10);
149 }
150 $type = !isset($crit->param[0][1]) ? "'$idb'" : calculer_liste(array($crit->param[0][1]), array(), $boucles, $boucle->id_parent);
151 $debut = ($type[0]!=="'") ? "'debut'.$type"
152 : ("'debut" .substr($type,1));
153
154 $boucle->modificateur['debut_nom'] = $type;
155 $partie =
156 // tester si le numero de page demande est de la forme '@yyy'
157 'isset($Pile[0]['.$debut.']) ? $Pile[0]['.$debut.'] : _request('.$debut.");\n"
158 ."\tif(substr(\$debut_boucle,0,1)=='@'){\n"
159 ."\t\t".'$debut_boucle = $Pile[0]['. $debut.'] = quete_debut_pagination(\''.$boucle->primary.'\',$Pile[0][\'@'.$boucle->primary.'\'] = substr($debut_boucle,1),'.$pas.',$result,'._q($boucle->sql_serveur).');'."\n"
160 ."\t\t".'if (!sql_seek($result,0,'._q($boucle->sql_serveur).")){\n"
161 ."\t\t\t".'@sql_free($result,'._q($boucle->sql_serveur).");\n"
162 ."\t\t\t".'$result = calculer_select($select, $from, $type, $where, $join, $groupby, $orderby, $limit, $having, $table, $id, $connect);'."\n"
163 ."\t\t}\n"
164 ."\t}\n"
165 ."\t".'$debut_boucle = intval($debut_boucle)';
166
167
168 $boucle->total_parties = $pas;
169 calculer_parties($boucles, $idb, $partie, 'p+');
170 // ajouter la cle primaire dans le select pour pouvoir gerer la pagination referencee par @id
171 // sauf si pas de primaire, ou si primaire composee
172 // dans ce cas, on ne sait pas gerer une pagination indirecte
173 $t = $boucle->id_table . '.' . $boucle->primary;
174 if ($boucle->primary
175 AND !preg_match('/[,\s]/',$boucle->primary)
176 AND !in_array($t, $boucle->select))
177 $boucle->select[]= $t;
178 }
179
180
181 // {recherche} ou {recherche susan}
182 // http://www.spip.net/@recherche
183 // http://doc.spip.org/@critere_recherche_dist
184 function critere_recherche_dist($idb, &$boucles, $crit) {
185
186 $boucle = &$boucles[$idb];
187
188 if (isset($crit->param[0]))
189 $quoi = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
190 else
191 $quoi = '@$Pile[0]["recherche"]';
192
193 // indiquer si l'on est dans une boucle forum avec le critère {plat} ou {tout}
194 $plat = "false" ;
195 if (isset($boucle->modificateur['tout']) OR isset($boucle->modificateur['plat'])) {
196 $plat = "true" ;
197 }
198
199 $boucle->hash .= '
200 // RECHERCHE
201 $prepare_recherche = charger_fonction(\'prepare_recherche\', \'inc\');
202 list($rech_select, $rech_where) = $prepare_recherche('.$quoi.', "'.$boucle->id_table.'", "'.$crit->cond.'","' . $boucle->sql_serveur . '", "'.$plat.'");
203 ';
204
205 $t = $boucle->id_table . '.' . $boucle->primary;
206 if (!in_array($t, $boucles[$idb]->select))
207 $boucle->select[]= $t; # pour postgres, neuneu ici
208 $boucle->join['resultats']=array("'".$boucle->id_table."'","'id'","'".$boucle->primary."'");
209 $boucle->from['resultats']='spip_resultats';
210 $boucle->select[]= '$rech_select';
211 //$boucle->where[]= "\$rech_where?'resultats.id=".$boucle->id_table.".".$boucle->primary."':''";
212
213 // et la recherche trouve
214 $boucle->where[]= '$rech_where?$rech_where:\'\'';
215 }
216
217 // {traduction}
218 // http://www.spip.net/@traduction
219 // (id_trad>0 AND id_trad=id_trad(precedent))
220 // OR id_article=id_article(precedent)
221 // http://doc.spip.org/@critere_traduction_dist
222 function critere_traduction_dist($idb, &$boucles, $crit) {
223 $boucle = &$boucles[$idb];
224 $prim = $boucle->primary;
225 $table = $boucle->id_table;
226 $arg = kwote(calculer_argument_precedent($idb, 'id_trad', $boucles));
227 $dprim = kwote(calculer_argument_precedent($idb, $prim, $boucles));
228 $boucle->where[]=
229 array("'OR'",
230 array("'AND'",
231 array("'='", "'$table.id_trad'", 0),
232 array("'='", "'$table.$prim'", $dprim)
233 ),
234 array("'AND'",
235 array("'>'", "'$table.id_trad'", 0),
236 array("'='", "'$table.id_trad'", $arg)
237 )
238 );
239 }
240
241 // {origine_traduction}
242 // (id_trad>0 AND id_article=id_trad) OR (id_trad=0)
243 // http://www.spip.net/@origine_traduction
244 // http://doc.spip.org/@critere_origine_traduction_dist
245 function critere_origine_traduction_dist($idb, &$boucles, $crit) {
246 $boucle = &$boucles[$idb];
247 $prim = $boucle->primary;
248 $table = $boucle->id_table;
249
250 $c =
251 array("'OR'",
252 array("'='", "'$table." . "id_trad'", "'$table.$prim'"),
253 array("'='", "'$table.id_trad'", "'0'")
254 );
255 $boucle->where[]= ($crit->not ? array("'NOT'", $c) : $c);
256 }
257
258 // {meme_parent}
259 // http://www.spip.net/@meme_parent
260 // http://doc.spip.org/@critere_meme_parent_dist
261 function critere_meme_parent_dist($idb, &$boucles, $crit) {
262 global $exceptions_des_tables;
263 $boucle = &$boucles[$idb];
264 $arg = kwote(calculer_argument_precedent($idb, 'id_parent', $boucles));
265 $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
266 $exceptions_des_tables[$boucle->id_table]['id_parent'] :
267 'id_parent';
268 $mparent = $boucle->id_table . '.' . $id_parent;
269
270 if ($boucle->type_requete == 'rubriques' OR isset($exceptions_des_tables[$boucle->id_table]['id_parent'])) {
271 $boucle->where[]= array("'='", "'$mparent'", $arg);
272
273 } else if ($boucle->type_requete == 'forums') {
274 $boucle->where[]= array("'='", "'$mparent'", $arg);
275 $boucle->where[]= array("'>'", "'$mparent'", 0);
276 $boucle->modificateur['plat'] = true;
277 } else erreur_squelette(_T('zbug_info_erreur_squelette'), "{meme_parent} BOUCLE$idb");
278 }
279
280 // {branche ?}
281 // http://www.spip.net/@branche
282 // http://doc.spip.org/@critere_branche_dist
283 function critere_branche_dist($idb, &$boucles, $crit) {
284
285 $not = $crit->not;
286 $boucle = &$boucles[$idb];
287 $arg = calculer_argument_precedent($idb, 'id_rubrique', $boucles);
288
289 //Trouver une jointure
290 $desc = $boucle->show;
291 //Seulement si necessaire
292 if (!array_key_exists('id_rubrique', $desc['field'])) {
293 $cle = trouver_jointure_champ('id_rubrique', $boucle);
294 } else $cle = $boucle->id_table;
295
296 $c = "sql_in('$cle" . ".id_rubrique', calcul_branche_in($arg)"
297 . ($not ? ", 'NOT'" : '') . ")";
298 $boucle->where[]= !$crit->cond ? $c :
299 ("($arg ? $c : " . ($not ? "'0=1'" : "'1=1'") .')');
300 }
301
302 // {logo} liste les objets qui ont un logo
303 // http://doc.spip.org/@critere_logo_dist
304 function critere_logo_dist($idb, &$boucles, $crit) {
305
306 $not = $crit->not;
307 $boucle = &$boucles[$idb];
308
309 $c = "sql_in('" .
310 $boucle->id_table . '.' . $boucle->primary
311 . "', lister_objets_avec_logos('". $boucle->primary ."'), '')";
312 if ($crit->cond) $c = "($arg ? $c : 1)";
313
314 if ($not)
315 $boucle->where[]= array("'NOT'", $c);
316 else
317 $boucle->where[]= $c;
318 }
319
320 // c'est la commande SQL "GROUP BY"
321 // par exemple <boucle(articles){fusion lang}>
322 // http://doc.spip.org/@critere_fusion_dist
323 function critere_fusion_dist($idb,&$boucles, $crit) {
324 if ($t = isset($crit->param[0])) {
325 $t = $crit->param[0];
326 if ($t[0]->type == 'texte') {
327 $t = $t[0]->texte;
328 if (preg_match("/^(.*)\.(.*)$/", $t, $r)) {
329 $t = table_objet_sql($r[1]);
330 $t = array_search($t, $boucles[$idb]->from);
331 if ($t) $t .= '.' . $r[2];
332 }
333 } else { $t = '".'
334 . calculer_critere_arg_dynamique($idb, $boucles, $t)
335 . '."';
336 }
337 }
338 if ($t) {
339 $boucles[$idb]->group[] = $t;
340 if (!in_array($t, $boucles[$idb]->select))
341 $boucles[$idb]->select[] = $t;
342 } else
343 return (array('zbug_critere_inconnu', array('critere' => $crit->op . ' ?')));
344 }
345
346 // c'est la commande SQL "COLLATE"
347 // qui peut etre appliquee sur les order by, group by, where like ...
348 // http://doc.spip.org/@critere_collecte_dist
349 function critere_collecte_dist($idb,&$boucles, $crit) {
350 if (isset($crit->param[0])) {
351 $_coll = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
352 $boucle = $boucles[$idb];
353 $boucle->modificateur['collate'] = "($_coll ?' COLLATE '.$_coll:'')";
354 $n = count($boucle->order);
355 if ($n && (strpos($boucle->order[$n-1],'COLLATE')===false))
356 $boucle->order[$n-1] .= " . " . $boucle->modificateur['collate'];
357 } else
358 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " $n")));
359 }
360
361 // http://doc.spip.org/@calculer_critere_arg_dynamique
362 function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix='')
363 {
364 $boucle = $boucles[$idb];
365 $alt = "('" . $boucle->id_table . '.\' . $x' . $suffix . ')';
366 $var = '$champs_' . $idb;
367 $desc = (strpos($boucle->in, "static $var =") !== false);
368 if (!$desc) {
369 $desc = $boucle->show['field'];
370 $desc = implode(',',array_map('_q',array_keys($desc)));
371 $boucles[$idb]->in .= "\n\tstatic $var = array(" . $desc .");";
372 }
373 if ($desc) $alt = "(in_array(\$x, $var) ? $alt :(\$x$suffix))";
374 $arg = calculer_liste($crit, array(), $boucles, $boucle->id_parent);
375 return "((\$x = preg_replace(\"/\\W/\",'', $arg)) ? $alt : '')";
376 }
377 // Tri : {par xxxx}
378 // http://www.spip.net/@par
379 // http://doc.spip.org/@critere_par_dist
380 function critere_par_dist($idb, &$boucles, $crit) {
381 return critere_parinverse($idb, $boucles, $crit) ;
382 }
383
384 // http://doc.spip.org/@critere_parinverse
385 function critere_parinverse($idb, &$boucles, $crit, $sens='') {
386 global $exceptions_des_jointures;
387 $boucle = &$boucles[$idb];
388 if ($crit->not) $sens = $sens ? "" : " . ' DESC'";
389 $collecte = (isset($boucle->modificateur['collecte']))?" . ".$boucle->modificateur['collecte']:"";
390
391 foreach ($crit->param as $tri) {
392
393 $order = $fct = ""; // en cas de fonction SQL
394 // tris specifies dynamiquement
395 if ($tri[0]->type != 'texte') {
396 // calculer le order dynamique qui verifie les champs
397 $order = calculer_critere_arg_dynamique($idb, $boucles, $tri, $sens);
398 // et si ce n'est fait, ajouter un champ 'hasard'
399 // pour supporter 'hasard' comme tri dynamique
400 $par = "rand()";
401 $parha = $par . " AS hasard";
402 if (!in_array($parha, $boucle->select))
403 $boucle->select[]= $parha;
404 } else {
405 $par = array_shift($tri);
406 $par = $par->texte;
407 // par multi champ
408 if (preg_match(",^multi[\s]*(.*)$,",$par, $m)) {
409 $texte = $boucle->id_table . '.' . trim($m[1]);
410 $boucle->select[] = "\".sql_multi('".$texte."', \$GLOBALS['spip_lang']).\"" ;
411 $order = "'multi'";
412 // par num champ(, suite)
413 } else if (preg_match(",^num (.*)$,m",$par, $m)) {
414 $texte = '0+' . $boucle->id_table . '.' . trim($m[1]);
415 $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent);
416 if ($suite !== "''")
417 $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
418 $as = 'num' .($boucle->order ? count($boucle->order) : "");
419 $boucle->select[] = $texte . " AS $as";
420 $order = "'$as'";
421 } else {
422 if (!preg_match(",^" . CHAMP_SQL_PLUS_FONC . '$,is', $par, $match)) {
423 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " $par")));
424 } else {
425 if (count($match)>2) { $par = substr($match[2],1,-1); $fct = $match[1]; }
426 // par hasard
427 if ($par == 'hasard') {
428 $par = "rand()";
429 $boucle->select[]= $par . " AS alea";
430 $order = "'alea'";
431 }
432 // par titre_mot ou type_mot voire d'autres
433 else if (isset($exceptions_des_jointures[$par])) {
434 list($table, $champ) = $exceptions_des_jointures[$par];
435 $order = critere_par_joint($table, $champ, $boucle, $idb);
436 if (!$order)
437 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " $par")));
438 }
439 else if ($par == 'date'
440 AND isset($GLOBALS['table_date'][$boucle->type_requete])) {
441 $m = $GLOBALS['table_date'][$boucle->type_requete];
442 $order = "'".$boucle->id_table ."." . $m . "'";
443 }
444 // par champ. Verifier qu'ils sont presents.
445 elseif (preg_match("/^([^,]*)\.(.*)$/", $par, $r)) {
446 // cas du tri sur champ de jointure explicite
447 $t = array_search($r[1], $boucle->from);
448 if (!$t) {
449 $t = trouver_champ_exterieur($r[2], array($r[1]), $boucle);
450 $t = array_search(@$t[0], $boucle->from);
451 }
452 if (!$t) {
453 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " $par")));
454 } else $order = "'" . $t . '.' . $r[2] . "'";
455 } else {
456 $desc = $boucle->show;
457 if ($desc['field'][$par])
458 $par = $boucle->id_table.".".$par;
459 // sinon tant pis, ca doit etre un champ synthetise (cf points)
460 $order = "'$par'";
461 }
462 }
463 }
464 }
465 if (preg_match('/^\'([^"]*)\'$/', $order, $m)) {
466 $t = $m[1];
467 if (strpos($t,'.') AND !in_array($t, $boucle->select)) {
468 $boucle->select[] = $t;
469 }
470 } else $sens ='';
471
472 if ($fct) {
473 if (preg_match("/^\s*'(.*)'\s*$/", $order, $r))
474 $order = "'$fct(" . $r[1] . ")'";
475 else $order = "'$fct(' . $order . ')'";
476 }
477 $t = $order . $collecte . $sens;
478 if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
479 $t = $r[1] . $r[2];
480 $boucle->order[] = $t;
481 }
482 }
483
484 // http://doc.spip.org/@critere_par_joint
485 function critere_par_joint($table, $champ, &$boucle, $idb)
486 {
487 $t = array_search($table, $boucle->from);
488 if (!$t) $t = trouver_jointure_champ($champ, $boucle);
489 return !$t ? '' : ("'" . $t . '.' . $champ . "'");
490 }
491
492 // {inverse}
493 // http://www.spip.net/@inverse
494
495 // http://doc.spip.org/@critere_inverse_dist
496 function critere_inverse_dist($idb, &$boucles, $crit) {
497
498 $boucle = &$boucles[$idb];
499 // Classement par ordre inverse
500 if ($crit->not)
501 critere_parinverse($idb, $boucles, $crit);
502 else
503 {
504 $order = "' DESC'";
505 // Classement par ordre inverse fonction eventuelle de #ENV{...}
506 if (isset($crit->param[0])){
507 $critere = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
508 $order = "(($critere)?' DESC':'')";
509 }
510
511 $n = count($boucle->order);
512 if (!$n) {
513 if (isset($boucle->default_order[0]))
514 $boucle->default_order[0] .= ' . " DESC"';
515 else
516 $boucle->default_order[] = ' DESC';
517 }
518 else {
519 $t = $boucle->order[$n-1] . " . $order";
520 if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
521 $t = $r[1] . $r[2];
522 $boucle->order[$n-1] = $t;
523 }
524 }
525 }
526
527 // http://doc.spip.org/@critere_agenda_dist
528 function critere_agenda_dist($idb, &$boucles, $crit)
529 {
530 $params = $crit->param;
531
532 if (count($params) < 1)
533 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " ?")));
534
535 $parent = $boucles[$idb]->id_parent;
536
537 // les valeurs $date et $type doivent etre connus a la compilation
538 // autrement dit ne pas etre des champs
539
540 $date = array_shift($params);
541 $date = $date[0]->texte;
542
543 $type = array_shift($params);
544 $type = $type[0]->texte;
545
546 $annee = $params ? array_shift($params) : "";
547 $annee = "\n" . 'sprintf("%04d", ($x = ' .
548 calculer_liste($annee, array(), $boucles, $parent) .
549 ') ? $x : date("Y"))';
550
551 $mois = $params ? array_shift($params) : "";
552 $mois = "\n" . 'sprintf("%02d", ($x = ' .
553 calculer_liste($mois, array(), $boucles, $parent) .
554 ') ? $x : date("m"))';
555
556 $jour = $params ? array_shift($params) : "";
557 $jour = "\n" . 'sprintf("%02d", ($x = ' .
558 calculer_liste($jour, array(), $boucles, $parent) .
559 ') ? $x : date("d"))';
560
561 $annee2 = $params ? array_shift($params) : "";
562 $annee2 = "\n" . 'sprintf("%04d", ($x = ' .
563 calculer_liste($annee2, array(), $boucles, $parent) .
564 ') ? $x : date("Y"))';
565
566 $mois2 = $params ? array_shift($params) : "";
567 $mois2 = "\n" . 'sprintf("%02d", ($x = ' .
568 calculer_liste($mois2, array(), $boucles, $parent) .
569 ') ? $x : date("m"))';
570
571 $jour2 = $params ? array_shift($params) : "";
572 $jour2 = "\n" . 'sprintf("%02d", ($x = ' .
573 calculer_liste($jour2, array(), $boucles, $parent) .
574 ') ? $x : date("d"))';
575
576 $boucle = &$boucles[$idb];
577 $date = $boucle->id_table . ".$date";
578
579 if ($type == 'jour')
580 $boucle->where[]= array("'='", "'DATE_FORMAT($date, \'%Y%m%d\')'",
581 ("$annee . $mois . $jour"));
582 elseif ($type == 'mois')
583 $boucle->where[]= array("'='", "'DATE_FORMAT($date, \'%Y%m\')'",
584 ("$annee . $mois"));
585 elseif ($type == 'semaine')
586 $boucle->where[]= array("'AND'",
587 array("'>='",
588 "'DATE_FORMAT($date, \'%Y%m%d\')'",
589 ("date_debut_semaine($annee, $mois, $jour)")),
590 array("'<='",
591 "'DATE_FORMAT($date, \'%Y%m%d\')'",
592 ("date_fin_semaine($annee, $mois, $jour)")));
593 elseif (count($crit->param) > 2)
594 $boucle->where[]= array("'AND'",
595 array("'>='",
596 "'DATE_FORMAT($date, \'%Y%m%d\')'",
597 ("$annee . $mois . $jour")),
598 array("'<='", "'DATE_FORMAT($date, \'%Y%m%d\')'", ("$annee2 . $mois2 . $jour2")));
599 // sinon on prend tout
600 }
601
602 // http://doc.spip.org/@calculer_critere_parties
603 function calculer_critere_parties($idb, &$boucles, $crit) {
604 $boucle = &$boucles[$idb];
605 $a1 = $crit->param[0];
606 $a2 = $crit->param[1];
607 $op = $crit->op;
608
609 list($a11,$a12) = calculer_critere_parties_aux($idb, $boucles, $a1);
610 list($a21,$a22) = calculer_critere_parties_aux($idb, $boucles, $a2);
611 if (($op== ',')&&(is_numeric($a11) && (is_numeric($a21))))
612 $boucle->limit = $a11 .',' . $a21;
613 else {
614 $boucle->total_parties = ($a21 != 'n') ? $a21 : $a22;
615 $partie = ($a11 != 'n') ? $a11 : $a12;
616 $mode = (($op == '/') ? '/' :
617 (($a11=='n') ? '-' : '+').(($a21=='n') ? '-' : '+'));
618 calculer_parties($boucles, $idb, $partie, $mode);
619 }
620 }
621
622 //
623 // Code specifique aux criteres {pagination}, {1,n} {n/m} etc
624 //
625
626 function calculer_parties(&$boucles, $id_boucle, $debut, $mode) {
627
628 $total_parties = $boucles[$id_boucle]->total_parties;
629 preg_match(",([+-/p])([+-/])?,", $mode, $regs);
630 list(,$op1,$op2) = $regs;
631 $nombre_boucle = "\$Numrows['$id_boucle']['total']";
632 // {1/3}
633 if ($op1 == '/') {
634 $pmoins1 = is_numeric($debut) ? ($debut-1) : "($debut-1)";
635 $totpos = is_numeric($total_parties) ? ($total_parties) :
636 "($total_parties ? $total_parties : 1)";
637 $fin = "ceil(($nombre_boucle * $debut )/$totpos) - 1";
638 $debut = !$pmoins1 ? 0 : "ceil(($nombre_boucle * $pmoins1)/$totpos);";
639 } else {
640 // cas {n-1,x}
641 if ($op1 == '-') $debut = "$nombre_boucle - $debut;";
642
643 // cas {x,n-1}
644 if ($op2 == '-') {
645 $fin = '$debut_boucle + '.$nombre_boucle.' - '
646 . (is_numeric($total_parties) ? ($total_parties+1) :
647 ($total_parties . ' - 1'));
648 } else {
649 // {x,1} ou {pagination}
650 $fin = '$debut_boucle'
651 . (is_numeric($total_parties) ?
652 (($total_parties==1) ? "" :(' + ' . ($total_parties-1))):
653 ('+' . $total_parties . ' - 1'));
654 }
655
656 // {pagination}, gerer le debut_xx=-1 pour tout voir
657 if ($op1 == 'p') {
658 $debut .= ";\n \$debut_boucle = ((\$tout=(\$debut_boucle == -1))?0:(\$debut_boucle))";
659 $debut .= ";\n \$debut_boucle = max(0,min(\$debut_boucle,floor(($nombre_boucle-1)/($total_parties))*($total_parties)))";
660 $fin = "(\$tout ? $nombre_boucle : $fin)";
661 }
662 }
663
664 // Notes :
665 // $debut_boucle et $fin_boucle sont les indices SQL du premier
666 // et du dernier demandes dans la boucle : 0 pour le premier,
667 // n-1 pour le dernier ; donc total_boucle = 1 + debut - fin
668 // Utiliser min pour rabattre $fin_boucle sur total_boucle.
669
670 $boucles[$id_boucle]->mode_partie = "\n\t"
671 . '$debut_boucle = ' . $debut . ";\n "
672 . '$fin_boucle = min(' . $fin . ", \$Numrows['$id_boucle']['total'] - 1);\n "
673 . '$Numrows[\''.$id_boucle. "']['grand_total'] = \$Numrows['$id_boucle']['total'];\n "
674 . '$Numrows[\''.$id_boucle.'\']["total"] = max(0,$fin_boucle - $debut_boucle + 1);'
675 . "\n\tif (\$debut_boucle>0 AND \$debut_boucle < \$Numrows['$id_boucle']['grand_total'] AND sql_seek(\$result,\$debut_boucle,"._q($boucles[$id_boucle]->sql_serveur).",'continue'))\n\t\t\$Numrows['$id_boucle']['compteur_boucle'] = \$debut_boucle;\n\t";
676
677 $boucles[$id_boucle]->partie = "
678 if (\$Numrows['$id_boucle']['compteur_boucle'] <= \$debut_boucle) continue;
679 if (\$Numrows['$id_boucle']['compteur_boucle']-1 > \$fin_boucle) break;";
680 }
681
682 // http://doc.spip.org/@calculer_critere_parties_aux
683 function calculer_critere_parties_aux($idb, &$boucles, $param) {
684 if ($param[0]->type != 'texte')
685 {
686 $a1 = calculer_liste(array($param[0]), array('id_mere' => $idb), $boucles, $boucles[$idb]->id_parent);
687 preg_match(',^ *(-([0-9]+))? *$,', $param[1]->texte, $m);
688 return array("intval($a1)", ($m[2] ? $m[2] : 0));
689 } else {
690 preg_match(',^ *(([0-9]+)|n) *(- *([0-9]+)? *)?$,', $param[0]->texte, $m);
691 $a1 = $m[1];
692 if (!@$m[3])
693 return array($a1, 0);
694 elseif ($m[4])
695 return array($a1, $m[4]);
696 else return array($a1,
697 calculer_liste(array($param[1]), array(), $boucles[$idb]->id_parent, $boucles));
698 }
699 }
700
701 //
702 // La fonction d'aiguillage sur le nom du critere dans leur liste
703 // Si l'une au moins des fonctions associees retourne une erreur
704 // (i.e. un tableau), on propage l'information
705 // Sinon, ne retourne rien (affectation directe dans l'arbre)
706
707 // http://doc.spip.org/@calculer_criteres
708 function calculer_criteres ($idb, &$boucles)
709 {
710 $msg = '';
711 $boucle = $boucles[$idb];
712 $table = strtoupper($boucle->id_table);
713 $defaut = charger_fonction('DEFAUT', 'calculer_critere');
714 // s'il y avait une erreur de syntaxe, propager cette info
715 if (!is_array($boucle->criteres)) return array();
716 foreach($boucle->criteres as $crit) {
717 $critere = $crit->op;
718 // critere personnalise ?
719 if ((!function_exists($f="critere_".$table."_".$critere))
720 AND (!function_exists($f=$f."_dist"))
721 AND (!function_exists($f="critere_".$critere))
722 AND (!function_exists($f=$f."_dist")) ) {
723 // fonction critere standard
724 $f = $defaut;
725 }
726 // compile le critere
727 $res = $f($idb, $boucles, $crit);
728
729 // Gestion centralisee des erreurs pour pouvoir propager
730 if (is_array($res)) {
731 $msg = $res;
732 erreur_squelette($msg, $boucle);
733 }
734 }
735 return $msg;
736 }
737
738 // Madeleine de Proust, revision MIT-1958 sqq, revision CERN-1989
739 // hum, c'est kwoi cette fonxion ?
740 // http://doc.spip.org/@kwote
741 function kwote($lisp)
742 {
743 if (preg_match(_CODE_QUOTE, $lisp, $r))
744 return $r[1] . "\"" . sql_quote(str_replace(array("\\'","\\\\"),array("'","\\"),$r[2])) . "\"" ;
745 else
746 return "sql_quote($lisp)";
747 }
748
749 // Si on a une liste de valeurs dans #ENV{x}, utiliser la double etoile
750 // pour faire par exemple {id_article IN #ENV**{liste_articles}}
751 // http://doc.spip.org/@critere_IN_dist
752 function critere_IN_dist ($idb, &$boucles, $crit)
753 {
754 $r = calculer_critere_infixe($idb, $boucles, $crit);
755 if (!$r) {
756 return (array('zbug_critere_inconnu', array('critere' => $crit->op . " ?")));
757 }
758 list($arg, $op, $val, $col, $where_complement) = $r;
759
760 $in = critere_IN_cas($idb, $boucles, $crit->not ? 'NOT' : ($crit->exclus? 'exclus' : ''), $arg, $op, $val, $col);
761 // inserer la condition; exemple: {id_mot ?IN (66, 62, 64)}
762 $where = $in;
763 if ($crit->cond) {
764 $pred = calculer_argument_precedent($idb, $col, $boucles);
765 $where = array("'?'",$pred, $where,"''");
766 if ($where_complement) // condition annexe du type "AND (objet='article')"
767 $where_complement = array("'?'",$pred, $where_complement,"''");
768 }
769 if ($crit->exclus)
770 if (!preg_match(",^L[0-9]+[.],",$arg))
771 $where = array("'NOT'", $where);
772 else
773 // un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
774 $where = array("'NOT'",array("'IN'","'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'" ,array("'SELF'","'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'",$where)));
775
776 $boucles[$idb]->where[] = $where;
777 if ($where_complement) // condition annexe du type "AND (objet='article')"
778 $boucles[$idb]->where[]= $where_complement;
779 }
780
781 // http://doc.spip.org/@critere_IN_cas
782 function critere_IN_cas ($idb, &$boucles, $crit2, $arg, $op, $val, $col)
783 {
784 static $num = array();
785 $descr = $boucles[$idb]->descr;
786 $cpt = &$num[$descr['nom']][$descr['gram']][$idb];
787
788 $var = '$in' . $cpt++;
789 $x= "\n\t$var = array();";
790 foreach ($val as $k => $v) {
791 if (preg_match(",^(\n//.*\n)?'(.*)'$,", $v, $r)) {
792 // optimiser le traitement des constantes
793 if (is_numeric($r[2]))
794 $x .= "\n\t$var" . "[]= $r[2];";
795 else
796 $x .= "\n\t$var" . "[]= " . sql_quote($r[2]) . ";";
797 } else {
798 // Pour permettre de passer des tableaux de valeurs
799 // on repere l'utilisation brute de #ENV**{X},
800 // c'est-a-dire sa traduction en ($PILE[0][X]).
801 // et on deballe mais en rajoutant l'anti XSS
802 $x .= "\n\tif (!(is_array(\$a = ($v))))\n\t\t$var" ."[]= \$a;\n\telse $var = array_merge($var, \$a);";
803 }
804 }
805
806 $boucles[$idb]->in .= $x;
807
808 // inserer le tri par defaut selon les ordres du IN ...
809 // avec une ecriture de type FIELD qui degrade les performances (du meme ordre qu'un rexgexp)
810 // et que l'on limite donc strictement aux cas necessaires :
811 // si ce n'est pas un !IN, et si il n'y a pas d'autre order dans la boucle
812 if (!$crit2){
813 $boucles[$idb]->default_order[] = "((!sql_quote($var) OR sql_quote($var)===\"''\") ? 0 : ('FIELD($arg,' . sql_quote($var) . ')'))";
814 }
815
816 return "sql_in('$arg',sql_quote($var)".($crit2=='NOT'?",'NOT'":"").")";
817 }
818
819 # Criteres de comparaison
820
821 // http://doc.spip.org/@calculer_critere_DEFAUT
822 function calculer_critere_DEFAUT_dist($idb, &$boucles, $crit)
823 {
824 // double cas particulier {0,1} et {1/2} repere a l'analyse lexicale
825 if (($crit->op == ",") OR ($crit->op == '/'))
826 return calculer_critere_parties($idb, $boucles, $crit);
827
828 $r = calculer_critere_infixe($idb, $boucles, $crit);
829
830 if (!$r) {
831 return (array('zbug_critere_inconnu', array('critere' => $crit->op )));
832 } else calculer_critere_DEFAUT_args($idb, $boucles, $crit, $r);
833 }
834
835 function calculer_critere_DEFAUT_args($idb, &$boucles, $crit, $args)
836 {
837 list($arg, $op, $val, $col, $where_complement) = $args;
838
839 $where = array("'$op'", "'$arg'", $val[0]);
840
841 // inserer la negation (cf !...)
842
843 if ($crit->not) $where = array("'NOT'", $where);
844 if ($crit->exclus)
845 if (!preg_match(",^L[0-9]+[.],",$arg))
846 $where = array("'NOT'", $where);
847 else
848 // un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
849 $where = array("'NOT'",array("'IN'","'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'" ,array("'SELF'","'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'",$where)));
850
851 // inserer la condition (cf {lang?})
852 // traiter a part la date, elle est mise d'office par SPIP,
853 if ($crit->cond) {
854 $pred = calculer_argument_precedent($idb, $col, $boucles);
855 if ($col == "date" OR $col == "date_redac") {
856 if($pred == "\$Pile[0]['".$col."']") {
857 $pred = "(\$Pile[0]['{$col}_default']?'':$pred)";
858 }
859 }
860
861 if ($op == '=' AND !$crit->not)
862 $where = array("'?'", "(is_array($pred))",
863 critere_IN_cas ($idb, $boucles, 'COND', $arg, $op, array($pred), $col),
864 $where);
865 $where = array("'?'", "!(is_array($pred)?count($pred):strlen($pred))","''", $where);
866 if ($where_complement) // condition annexe du type "AND (objet='article')"
867 $where_complement = array("'?'", "!$pred","''", $where_complement);
868 }
869
870 $boucles[$idb]->where[]= $where;
871 if ($where_complement) // condition annexe du type "AND (objet='article')"
872 $boucles[$idb]->where[]= $where_complement;
873 }
874
875 // http://doc.spip.org/@calculer_critere_infixe
876 function calculer_critere_infixe($idb, &$boucles, $crit) {
877
878 global $table_criteres_infixes;
879 global $exceptions_des_jointures, $exceptions_des_tables;
880
881 $boucle = &$boucles[$idb];
882 $type = $boucle->type_requete;
883 $table = $boucle->id_table;
884 $desc = $boucle->show;
885 $date = array();
886
887 list($fct, $col, $op, $val, $args_sql) =
888 calculer_critere_infixe_ops($idb, $boucles, $crit);
889
890 $col_alias = $col;
891 $where_complement =false;
892
893 // Cas particulier : id_enfant => utiliser la colonne id_objet
894 if ($col == 'id_enfant')
895 $col = $boucle->primary;
896
897 // Cas particulier : id_parent => verifier les exceptions de tables
898 if ($col == 'id_parent')
899 $col = isset($exceptions_des_tables[$table]['id_parent']) ?
900 $exceptions_des_tables[$table]['id_parent'] :
901 'id_parent';
902
903 // Cas particulier : id_secteur pour certaines tables
904 else if (($col == 'id_secteur')&&($type == 'breves')) {
905 $col = 'id_rubrique';
906 }
907 // et possibilite de gerer un critere secteur sur des tables de plugins (ie forums)
908 else if (($col == 'id_secteur') AND ($critere_secteur = charger_fonction("critere_secteur_$type","public",true))) {
909 $table = $critere_secteur($idb, $boucles, $val, $crit);
910 }
911
912 // cas id_article=xx qui se mappe en id_objet=xx AND objet=article
913 else if (count(trouver_champs_decomposes($col,$desc))>1){
914 $e = decompose_champ_id_objet($col);
915 $col = array_shift($e);
916 $where_complement = primary_doublee($e, $table);
917
918 }
919 // Cas particulier : expressions de date
920 else if ($date = tester_param_date($boucle->type_requete, $col)) {
921 $col = calculer_critere_infixe_date($idb, $boucles, $date);
922 $table = '';
923 }
924 else if (preg_match('/^(.*)\.(.*)$/', $col, $r)) {
925 list(,$table, $col) = $r;
926 $col_alias = $col;
927 $table = calculer_critere_externe_init($boucle, array($table), $col, $desc, ($crit->cond OR $op !='='), true);
928 if (!$table) return '';
929 }
930 elseif (@!array_key_exists($col, $desc['field'])) {
931 $r = calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table);
932 if (!$r) return '';
933 list($col, $col_alias, $table, $where_complement) = $r;
934 }
935 // Si la colonne SQL est numerique ou le critere est une date relative
936 // virer les guillemets eventuels qui sont refuses par certains SQL
937 // Ne pas utiliser intval, PHP tronquant les Bigint de SQL
938
939 if (($op == '=' OR in_array($op, $table_criteres_infixes))
940 AND (($desc AND isset($desc['field'][$col]) AND sql_test_int($desc['field'][$col]))
941 OR ($date AND strpos($date[0], '_relatif')))) {
942 if (preg_match("/^\"'(-?\d+)'\"$/", $val[0], $r))
943 $val[0] = $r[1];
944 elseif (preg_match('/^sql_quote[(](.*?)(,[^)]*)?[)]\s*$/', $val[0], $r)) {
945 $r = $r[1] . ($r[2] ? $r[2] : ",''") . ",'int'";
946 $val[0] = "sql_quote($r)";
947 }
948 }
949 // Indicateur pour permettre aux fonctionx boucle_X de modifier
950 // leurs requetes par defaut, notamment le champ statut
951 // Ne pas confondre champs de la table principale et des jointures
952 if ($table === $boucle->id_table) {
953 $boucles[$idb]->modificateur['criteres'][$col] = true;
954 if ($col_alias!=$col)
955 $boucles[$idb]->modificateur['criteres'][$col_alias] = true;
956 }
957
958 // ajout pour le cas special d'une condition sur le champ statut:
959 // il faut alors interdire a la fonction de boucle
960 // de mettre ses propres criteres de statut
961 // http://www.spip.net/@statut (a documenter)
962 // garde pour compatibilite avec code des plugins anterieurs, mais redondant avec la ligne precedente
963 if ($col == 'statut') $boucles[$idb]->statut = true;
964
965 // ajout pour le cas special des forums
966 // il faut alors interdire a la fonction de boucle sur forum
967 // de selectionner uniquement les forums sans pere
968
969 elseif ($boucles[$idb]->type_requete == 'forums' AND
970 ($col == 'id_parent' OR $col == 'id_forum'))
971 $boucles[$idb]->modificateur['plat'] = true;
972 // inserer le nom de la table SQL devant le nom du champ
973 if ($table) {
974 if ($col[0] == "`")
975 $arg = "$table." . substr($col,1,-1);
976 else $arg = "$table.$col";
977 } else $arg = $col;
978
979 // inserer la fonction SQL
980 if ($fct) $arg = "$fct($arg$args_sql)";
981
982 return array($arg, $op, $val, $col_alias, $where_complement);
983 }
984
985 function calculer_critere_infixe_externe(&$boucle, $crit, $op, $desc, $col, $col_alias, $table)
986 {
987 global $exceptions_des_jointures;
988 $where = '';
989
990 $calculer_critere_externe = 'calculer_critere_externe_init';
991 // gestion par les plugins des jointures tordues
992 // pas automatiques mais necessaires
993 if (is_array($exceptions_des_jointures[$table])) {
994
995 $t = $exceptions_des_jointures[$table];
996 $index = isset($t[$col])
997 ? $t[$col] : (isset($t['']) ? $t[''] : array());
998
999 if (count($index)==3)
1000 list($t, $col, $calculer_critere_externe) = $index;
1001 elseif (count($index)==2)
1002 list($t, $col) = $t[$col];
1003 elseif (count($index)==1){
1004 list($calculer_critere_externe) = $index;
1005 $t = $table;
1006 }
1007 else
1008 $t=''; // jointure non declaree. La trouver.
1009 }
1010 elseif (isset($exceptions_des_jointures[$col]))
1011 list($t, $col) = $exceptions_des_jointures[$col];
1012 else $t =''; // jointure non declaree. La trouver.
1013
1014 $table = $calculer_critere_externe($boucle, $boucle->jointures, $col, $desc, ($crit->cond OR $op !='='), $t);
1015
1016 if (!$table) return '';
1017
1018 list($nom, $desc) = trouver_champ_exterieur($col, $boucle->jointures, $boucle);
1019 if (count(trouver_champs_decomposes($col,$desc))>1){
1020 $col_alias = $col; // id_article devient juste le nom d'origine
1021 $e = decompose_champ_id_objet($col);
1022 $col = array_shift($e);
1023 $where = primary_doublee($e, $table);
1024 }
1025
1026 return array($col, $col_alias, $table, $where);
1027 }
1028
1029 // Ne pas appliquer sql_quote lors de la compilation,
1030 // car on ne connait pas le serveur SQL, donc s'il faut \' ou ''
1031
1032 // http://doc.spip.org/@primary_doublee
1033 function primary_doublee($decompose, $table)
1034 {
1035 $e1 = reset($decompose);
1036 $e2 = "sql_quote('" . end($decompose) ."')";
1037 return array("'='","'$table.". $e1 ."'",$e2);
1038 }
1039
1040 // Faute de copie du champ id_secteur dans la table des forums,
1041 // faut le retrouver par jointure
1042 // Pour chaque Row il faudrait tester si le forum est
1043 // d'article, de breve, de rubrique, ou de syndication.
1044 // Pour le moment on ne traite que les articles,
1045 // les 3 autres cas ne marcheront donc pas: ca ferait 4 jointures
1046 // qu'il faut traiter optimalement ou alors pas du tout.
1047
1048 // http://doc.spip.org/@critere_secteur_forum
1049 function critere_secteur_forum($idb, &$boucles, $val, $crit)
1050 {
1051 static $trouver_table;
1052 if (!$trouver_table)
1053 $trouver_table = charger_fonction('trouver_table', 'base');
1054
1055 $desc = $trouver_table('articles', $boucles[$idb]->sql_serveur);
1056 return calculer_critere_externe_init($boucles[$idb], array($desc['table']), 'id_secteur', $desc, $crit->cond, true);
1057 }
1058
1059 // Champ hors table, ca ne peut etre qu'une jointure.
1060 // On cherche la table du champ et on regarde si elle est deja jointe
1061 // Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire
1062 // Exemple: criteres {titre_mot=...}{type_mot=...}
1063 // Dans les 2 autres cas ==> jointure
1064 // (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures
1065 // pour selectioner ce qui a exactement ces 2 mots-cles.
1066
1067 // http://doc.spip.org/@calculer_critere_externe_init
1068 function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $eg, $checkarrivee = false)
1069 {
1070 $cle = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee);
1071 if (!$cle) return '';
1072 $t = array_search($cle[0], $boucle->from);
1073 // transformer eventuellement id_xx en (id_objet,objet)
1074 $cols = trouver_champs_decomposes($col,$cle[1]);
1075 if ($t) {
1076 $joindre = false;
1077 foreach($cols as $col){
1078 $c = '/\b' . $t . ".$col" . '\b/';
1079 if (trouver_champ($c, $boucle->where)) $joindre = true;
1080 else {
1081 // mais ca peut etre dans le FIELD pour le Having
1082 $c = "/FIELD.$t" .".$col,/";
1083 if (trouver_champ($c, $boucle->select)) $joindre = true;
1084 }
1085 }
1086 if (!$joindre) return $t;
1087 }
1088 return calculer_jointure($boucle, array($boucle->id_table, $desc), $cle, $cols, $eg);
1089
1090 }
1091
1092 // http://doc.spip.org/@trouver_champ
1093 function trouver_champ($champ, $where)
1094 {
1095 if (!is_array($where))
1096 return preg_match($champ,$where);
1097 else {
1098 foreach ($where as $clause) {
1099 if (trouver_champ($champ, $clause)) return true;
1100 }
1101 return false;
1102 }
1103 }
1104
1105
1106 // determine l'operateur et les operandes
1107
1108 // http://doc.spip.org/@calculer_critere_infixe_ops
1109 function calculer_critere_infixe_ops($idb, &$boucles, $crit)
1110 {
1111 // cas d'une valeur comparee a elle-meme ou son referent
1112 if (count($crit->param) == 0)
1113 { $op = '=';
1114 $col = $val = $crit->op;
1115 if (preg_match('/^(.*)\.(.*)$/', $col, $r)) $val = $r[2];
1116 // Cas special {lang} : aller chercher $GLOBALS['spip_lang']
1117 if ($val == 'lang')
1118 $val = array(kwote('$GLOBALS[\'spip_lang\']'));
1119 else {
1120 // Si id_parent, comparer l'id_parent avec l'id_objet
1121 // de la boucle superieure.... faudrait verifier qu'il existe
1122 // pour eviter l'erreur SQL
1123 if ($val == 'id_parent')
1124 $val = $boucles[$idb]->primary;
1125 // Si id_enfant, comparer l'id_objet avec l'id_parent
1126 // de la boucle superieure
1127 else if ($val == 'id_enfant')
1128 $val = 'id_parent';
1129 // un critere conditionnel sur date est traite a part
1130 // car la date est mise d'office par SPIP,
1131 $val = calculer_argument_precedent($idb, $val, $boucles);
1132 if ($crit->cond AND ($col == "date" OR $col == "date_redac")) {
1133 if($val == "\$Pile[0]['".$col."']") {
1134 $val = "(\$Pile[0]['{$col}_default']?'':$val)";
1135 }
1136 }
1137 $val = array(kwote($val));
1138 }
1139 } else {
1140 // comparaison explicite
1141 // le phraseur impose que le premier param soit du texte
1142 $params = $crit->param;
1143 $op = $crit->op;
1144 if ($op == '==') $op = 'REGEXP';
1145 $col = array_shift($params);
1146 $col = $col[0]->texte;
1147
1148 $val = array();
1149 $desc = array('id_mere' => $idb);
1150 $parent = $boucles[$idb]->id_parent;
1151
1152 // Dans le cas {x=='#DATE'} etc, defaire le travail du phraseur,
1153 // celui ne sachant pas ce qu'est un critere infixe
1154 // et a fortiori son 2e operande qu'entoure " ou '
1155 if (count($params)==1
1156 AND count($params[0]==3)
1157 AND $params[0][0]->type == 'texte'
1158 AND @$params[0][2]->type == 'texte'
1159 AND ($p=$params[0][0]->texte) == $params[0][2]->texte
1160 AND (($p == "'") OR ($p == '"'))
1161 AND $params[0][1]->type == 'champ' ) {
1162 $val[]= "$p\\$p#" . $params[0][1]->nom_champ . "\\$p$p";
1163 } else
1164 foreach ((($op != 'IN') ? $params : calculer_vieux_in($params)) as $p) {
1165 $a = calculer_liste($p, $desc, $boucles, $parent);
1166 if ($op == 'IN') $val[]= $a;
1167 else $val[]=kwote($a);
1168 }
1169 }
1170
1171 $fct = $args_sql = '';
1172 // fonction SQL ?
1173 if (preg_match('/^(.*)' . SQL_ARGS . '$/', $col, $m)) {
1174 $fct = $m[1];
1175 preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a);
1176 $col = $a[1];
1177 if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) {
1178 $col=$m[1];
1179 $args_sql = $m[2];
1180 }
1181 $args_sql .= $a[2];;
1182 }
1183
1184 return array($fct, $col, $op, $val, $args_sql);
1185 }
1186
1187 // compatibilite ancienne version
1188
1189 // http://doc.spip.org/@calculer_vieux_in
1190 function calculer_vieux_in($params)
1191 {
1192 $deb = $params[0][0];
1193 $k = count($params)-1;
1194 $last = $params[$k];
1195 $j = count($last)-1;
1196 $last = $last[$j];
1197 $n = strlen($last->texte);
1198
1199 if (!(($deb->texte[0] == '(') && ($last->texte[$n-1] == ')')))
1200 return $params;
1201 $params[0][0]->texte = substr($deb->texte,1);
1202 // attention, on peut avoir k=0,j=0 ==> recalculer
1203 $last = $params[$k][$j];
1204 $n = strlen($last->texte);
1205 $params[$k][$j]->texte = substr($last->texte,0,$n-1);
1206 $newp = array();
1207 foreach($params as $v) {
1208 if ($v[0]->type != 'texte')
1209 $newp[] = $v;
1210 else {
1211 foreach(explode(',', $v[0]->texte) as $x) {
1212 $t = new Texte;
1213 $t->texte = $x;
1214 $newp[] = array($t);
1215 }
1216 }
1217 }
1218 return $newp;
1219 }
1220
1221 // http://doc.spip.org/@calculer_critere_infixe_date
1222 function calculer_critere_infixe_date($idb, &$boucles, $regs)
1223 {
1224 global $table_date;
1225 $boucle = $boucles[$idb];
1226 $col = $regs[1];
1227 $date_orig = $pred = isset($table_date[$boucle->type_requete])?$table_date[$boucle->type_requete]:'date';
1228 if (isset($regs[3]) AND $suite=$regs[3]) {
1229 # Recherche de l'existence du champ date_xxxx,
1230 # si oui choisir ce champ, sinon choisir xxxx
1231 $t = $boucle->show;
1232 if ($t['field']["date$suite"])
1233 $date_orig = 'date'.$suite;
1234 else
1235 $date_orig = substr($suite, 1);
1236 $pred = $date_orig;
1237 }
1238 else
1239 if (isset($regs[2]) AND $rel=$regs[2]) $pred = 'date';
1240
1241 $date_compare = "\"' . normaliser_date(" .
1242 calculer_argument_precedent($idb, $pred, $boucles) .
1243 ") . '\"";
1244 $date_orig = $boucle->id_table . '.' . $date_orig;
1245
1246 switch ($col) {
1247 case 'date':
1248 $col = $date_orig;
1249 break;
1250 case 'jour':
1251 $col = "DAYOFMONTH($date_orig)";
1252 break;
1253 case 'mois':
1254 $col = "MONTH($date_orig)";
1255 break;
1256 case 'annee':
1257 $col = "YEAR($date_orig)";
1258 break;
1259 case 'heure':
1260 $col = "DATE_FORMAT($date_orig, '%H:%i')";
1261 break;
1262 case 'age':
1263 $col = calculer_param_date("NOW()", $date_orig);
1264 break;
1265 case 'age_relatif':
1266 $col = calculer_param_date($date_compare, $date_orig);
1267 break;
1268 case 'jour_relatif':
1269 $col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
1270 $date_orig . "), DAYOFMONTH(" . $date_compare .
1271 ")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
1272 $date_compare . ")-MONTH(" . $date_orig .
1273 "))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
1274 $date_orig . ")))";
1275 break;
1276 case 'mois_relatif':
1277 $col = "MONTH(" . $date_compare . ")-MONTH(" .
1278 $date_orig . ")+12*(YEAR(" . $date_compare .
1279 ")-YEAR(" . $date_orig . "))";
1280 break;
1281 case 'annee_relatif':
1282 $col = "YEAR(" . $date_compare . ")-YEAR(" .
1283 $date_orig . ")";
1284 break;
1285 }
1286 return $col;
1287 }
1288
1289 // http://doc.spip.org/@calculer_param_date
1290 function calculer_param_date($date_compare, $date_orig) {
1291 if (preg_match(",'\" *\.(.*)\. *\"',", $date_compare, $r)) {
1292 $init = "'\" . (\$x = $r[1]) . \"'";
1293 $date_compare = '\'$x\'';
1294 }
1295 else
1296 $init = $date_compare;
1297
1298 return
1299 "LEAST((UNIX_TIMESTAMP(" .
1300 $init .
1301 ")-UNIX_TIMESTAMP(" .
1302 $date_orig .
1303 "))/86400,\n\tTO_DAYS(" .
1304 $date_compare .
1305 ")-TO_DAYS(" .
1306 $date_orig .
1307 "),\n\tDAYOFMONTH(" .
1308 $date_compare .
1309 ")-DAYOFMONTH(" .
1310 $date_orig .
1311 ")+30.4368*(MONTH(" .
1312 $date_compare .
1313 ")-MONTH(" .
1314 $date_orig .
1315 "))+365.2422*(YEAR(" .
1316 $date_compare .
1317 ")-YEAR(" .
1318 $date_orig .
1319 ")))";
1320 }
1321
1322 // http://doc.spip.org/@tester_param_date
1323 function tester_param_date($type, $col)
1324 {
1325 global $table_date;
1326 if (isset($table_date[$type])
1327 AND preg_match(",^((age|jour|mois|annee)_relatif|date|mois|annee|jour|heure|age)(_[a-z]+)?$,", $col, $regs))
1328 return $regs;
1329 else return false;
1330 }
1331
1332 ?>