09a4b1d93d3d69eb58c6d5bc1476198a8cf487b8
[lhc/web/www.git] / www / ecrire / public / quete.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
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 * Fonctions d'appel aux serveurs SQL presentes dans le code compile
15 *
16 * NB : à l'exception des fonctions pour les balises dynamiques
17 *
18 * @package SPIP\Core\Compilateur\Quetes
19 **/
20
21 if (!defined('_ECRIRE_INC_VERSION')) {
22 return;
23 }
24
25
26 include_spip('base/abstract_sql');
27
28 /**
29 * Retourne l'URL de redirection d'un article virtuel, seulement si il est publié
30 *
31 * @param $id_article
32 * @param $connect
33 * @return array|bool|null
34 */
35 function quete_virtuel($id_article, $connect) {
36 return sql_getfetsel(
37 'virtuel',
38 'spip_articles',
39 array('id_article=' . intval($id_article), "statut='publie'"),
40 '',
41 '',
42 '',
43 '',
44 $connect
45 );
46 }
47
48 /**
49 * Retourne le couple `parent,lang` pour toute table
50 *
51 * En pratique `id_rubrique` si présent (ou `id_parent` pour table rubriques)
52 * et champ `lang` si présent
53 *
54 * @param string $table
55 * @param int $id
56 * @param string $connect
57 * @return array
58 */
59 function quete_parent_lang($table, $id, $connect = '') {
60 static $cache_quete = array();
61
62 if (!isset($cache_quete[$connect][$table][$id])) {
63 if (!isset($cache_quete[$connect][$table]['_select'])) {
64 $trouver_table = charger_fonction('trouver_table', 'base');
65 if (!$desc = $trouver_table($table,
66 $connect) or !isset($desc['field']['id_rubrique'])
67 ) {
68 // pas de parent rubrique, on passe
69 $cache_quete[$connect][$table]['_select'] = false;
70 } else {
71 $select = ($table == 'spip_rubriques' ? 'id_parent' : 'id_rubrique');
72 $select .= isset($desc['field']['lang']) ? ', lang' : '';
73 $cache_quete[$connect][$table]['_select'] = $select;
74 $cache_quete[$connect][$table]['_id'] = id_table_objet(objet_type($table));
75 }
76 }
77 if ($cache_quete[$connect][$table]['_select']) {
78 $cache_quete[$connect][$table][$id] = sql_fetsel(
79 $cache_quete[$connect][$table]['_select'],
80 $table,
81 $cache_quete[$connect][$table]['_id'] . '=' . intval($id),
82 '',
83 '',
84 '',
85 '',
86 $connect
87 );
88 }
89 }
90
91 return isset($cache_quete[$connect][$table][$id]) ? $cache_quete[$connect][$table][$id] : null;
92 }
93
94
95 /**
96 * Retourne le parent d'une rubrique
97 *
98 * Repose sur la fonction quete_parent_lang pour la mutualisation
99 * +mise en cache SQL des requêtes
100 *
101 * @uses quete_parent_lang()
102 *
103 * @param int $id_rubrique
104 * @param string $connect
105 * @return int
106 */
107 function quete_parent($id_rubrique, $connect = '') {
108 if (!$id_rubrique = intval($id_rubrique)) {
109 return 0;
110 }
111 $id_parent = quete_parent_lang('spip_rubriques', $id_rubrique, $connect);
112
113 return $id_parent['id_parent'];
114 }
115
116 /**
117 * Retourne la rubrique d'un article
118 *
119 * Repose sur la fonction quete_parent_lang pour la mutualisation
120 * +mise en cache SQL des requêtes
121 *
122 * @uses quete_parent_lang()
123 *
124 * @param int $id_article
125 * @param $serveur
126 * @return int
127 */
128 function quete_rubrique($id_article, $serveur) {
129 $id_parent = quete_parent_lang('spip_articles', $id_article, $serveur);
130
131 return $id_parent['id_rubrique'];
132 }
133
134
135 /**
136 * Retourne la profondeur d'une rubrique
137 *
138 * @uses quete_parent()
139 *
140 * @param int $id
141 * @param string $connect
142 * @return int
143 */
144 function quete_profondeur($id, $connect = '') {
145 $n = 0;
146 while ($id) {
147 $n++;
148 $id = quete_parent($id, $connect);
149 }
150
151 return $n;
152 }
153
154
155 /**
156 * Retourne la condition sur la date lorsqu'il y a des post-dates
157 *
158 * @param string $champ_date
159 * Nom de la colonne de date dans la table SQL
160 * @param string $serveur
161 * @param bool $ignore_previsu
162 * true pour forcer le test même en prévisu
163 * @return string
164 * Morceau de la requête SQL testant la date
165 */
166 function quete_condition_postdates($champ_date, $serveur = '', $ignore_previsu = false) {
167 if (defined('_VAR_PREVIEW') and _VAR_PREVIEW and !$ignore_previsu) {
168 return '1=1';
169 }
170
171 return
172 (isset($GLOBALS['meta']['date_prochain_postdate'])
173 and $GLOBALS['meta']['date_prochain_postdate'] > time())
174 ? "$champ_date<" . sql_quote(date('Y-m-d H:i:s', $GLOBALS['meta']['date_prochain_postdate']), $serveur)
175 : '1=1';
176 }
177
178
179 /**
180 * Calculer la condition pour filtrer les status,
181 *
182 * @param string $mstatut
183 * Le champ de la table sur lequel porte la condition
184 * @param string $previsu
185 * Mode previsu : statut ou liste des statuts séparés par une virgule
186 * @param string $publie
187 * Mode publie : statut ou liste des statuts séparés par une virgule
188 * @param string $serveur
189 * Serveur de BDD
190 * @param bool $ignore_previsu
191 * true pour forcer le test même en prévisu
192 * @return array|string
193 */
194 function quete_condition_statut($mstatut, $previsu, $publie, $serveur = '', $ignore_previsu = false) {
195 static $cond = array();
196 $key = func_get_args();
197 $key = implode('-', $key);
198 if (isset($cond[$key])) {
199 return $cond[$key];
200 }
201
202 $liste_statuts = $publie;
203 if (defined('_VAR_PREVIEW') and _VAR_PREVIEW and !$ignore_previsu) {
204 $liste_statuts = $previsu;
205 }
206 $not = false;
207 if (strncmp($liste_statuts, '!', 1) == 0) {
208 $not = true;
209 $liste_statuts = substr($liste_statuts, 1);
210 }
211 // '' => ne rien afficher, '!'=> ne rien filtrer
212 if (!strlen($liste_statuts)) {
213 return $cond[$key] = ($not ? '1=1' : '0=1');
214 }
215
216 $liste_statuts = explode(',', $liste_statuts);
217 $where = array();
218 foreach ($liste_statuts as $k => $v) {
219 // filtrage /auteur pour limiter les objets d'un statut (prepa en general)
220 // a ceux de l'auteur identifie
221 if (strpos($v, '/') !== false) {
222 $v = explode('/', $v);
223 $filtre = end($v);
224 $v = reset($v);
225 $v = preg_replace(',\W,', '', $v);
226 if ($filtre == 'auteur'
227 and (strpos($mstatut, '.') !== false)
228 and $objet = explode('.', $mstatut)
229 and $id_table = reset($objet)
230 and $objet = objet_type($id_table)
231 ) {
232 $w = "$mstatut<>" . sql_quote($v);
233
234 // retrouver l’id_auteur qui a filé un lien de prévisu éventuellement,
235 // sinon l’auteur en session
236 include_spip('inc/securiser_action');
237 if ($desc = decrire_token_previsu()) {
238 $id_auteur = $desc['id_auteur'];
239 } elseif (isset($GLOBALS['visiteur_session']['id_auteur'])) {
240 $id_auteur = intval($GLOBALS['visiteur_session']['id_auteur']);
241 } else {
242 $id_auteur = null;
243 }
244
245 // dans ce cas (admin en general), pas de filtrage sur ce statut
246 if (!autoriser('previsualiser' . $v, $objet, '', $id_auteur)) {
247 // si pas d'auteur identifie pas de sous-requete car pas d'article qui matche
248 if (!$id_auteur) {
249 $where[] = $w;
250 } else {
251 $primary = id_table_objet($objet);
252 $where[] = "($w OR $id_table.$primary IN (" . sql_get_select(
253 'ssss.id_objet',
254 'spip_auteurs_liens AS ssss',
255 'ssss.objet=' . sql_quote($objet) . ' AND ssss.id_auteur=' . intval($id_auteur),
256 '',
257 '',
258 '',
259 '',
260 $serveur
261 ) . '))';
262 }
263 }
264 } // ignorer ce statut si on ne sait pas comment le filtrer
265 else {
266 $v = '';
267 }
268 }
269 // securite
270 $liste_statuts[$k] = preg_replace(',\W,', '', $v);
271 }
272 $liste_statuts = array_filter($liste_statuts);
273 if (count($liste_statuts) == 1) {
274 $where[] = array('=', $mstatut, sql_quote(reset($liste_statuts), $serveur));
275 } else {
276 $where[] = sql_in($mstatut, $liste_statuts, $not, $serveur);
277 }
278
279 while (count($where) > 1) {
280 $and = array('AND', array_pop($where), array_pop($where));
281 $where[] = $and;
282 }
283 $cond[$key] = reset($where);
284 if ($not) {
285 $cond[$key] = array('NOT', $cond[$key]);
286 }
287
288 return $cond[$key];
289 }
290
291 /**
292 * Retourne le fichier d'un document
293 *
294 * @param int $id_document
295 * @param string $serveur
296 * @return array|bool|null
297 */
298 function quete_fichier($id_document, $serveur = '') {
299 return sql_getfetsel('fichier', 'spip_documents', ('id_document=' . intval($id_document)), '', array(), '', '', $serveur);
300 }
301
302 /**
303 * Toute les infos sur un document
304 *
305 * @param int $id_document
306 * @param string $serveur
307 * @return array|bool
308 */
309 function quete_document($id_document, $serveur = '') {
310 return sql_fetsel('*', 'spip_documents', ('id_document=' . intval($id_document)), '', array(), '', '', $serveur);
311 }
312
313 /**
314 * Récuperer une meta sur un site (spip) distant (en local il y a plus simple)
315 *
316 * @param string $nom Nom de la méta
317 * @param string $serveur Connecteur
318 * @return array|bool|null
319 */
320 function quete_meta($nom, $serveur) {
321 return sql_getfetsel('valeur', 'spip_meta', 'nom=' . sql_quote($nom), '', '', '', '', $serveur);
322 }
323
324 /**
325 * Retourne le logo d'un objet, éventuellement par héritage
326 *
327 * Si flag != false, retourne le chemin du fichier, sinon retourne un tableau
328 * de 3 elements :
329 * le chemin du fichier, celui du logo de survol, l'attribut style=w/h.
330 *
331 * @param string $cle_objet
332 * Nom de la clé de l'objet dont on veut chercher le logo.
333 * @param string $onoff
334 * Sélectionne quel(s) logo(s) : "on" pour le logo normal, "off" pour le logo de survol, ou "ON" pour l'ensemble.
335 * @param int $id
336 * Identifiant de l'objet dont on veut chercher le logo.
337 * @param int $id_rubrique
338 * Identifiant de la rubrique parente si l'on veut aller chercher son logo
339 * dans le cas où l'objet demandé n'en a pas.
340 * @param bool $flag
341 * Lorsque le drapeau est évalué comme "true", la fonction ne renvoie
342 * que le chemin du fichier, sinon elle renvoie le tableau plus complet.
343 * @return array|string
344 * Retourne soit un tableau, soit le chemin du fichier.
345 */
346 function quete_logo($cle_objet, $onoff, $id, $id_rubrique, $flag) {
347 include_spip('base/objets');
348 $nom = strtolower($onoff);
349
350 while (1) {
351 $objet = objet_type($cle_objet);
352
353 $on = quete_logo_objet($id, $objet, $nom);
354
355 if ($on) {
356 if ($flag) {
357 return basename($on['chemin']);
358 } else {
359 $taille = @getimagesize($on['chemin']);
360
361 // Si on a déjà demandé un survol directement ($onoff = off)
362 // ou qu'on a demandé uniquement le normal ($onoff = on)
363 // alors on ne cherche pas du tout le survol ici
364 if ($onoff != 'ON') {
365 $off = '';
366 } else {
367 // Sinon, c'est qu'on demande normal ET survol à la fois, donc on cherche maintenant le survol
368 $off = quete_logo_objet($id, $objet, 'off');
369 }
370
371 // on retourne une url du type IMG/artonXX?timestamp
372 // qui permet de distinguer le changement de logo
373 // et placer un expire sur le dossier IMG/
374 $res = array(
375 $on['chemin'] . ($on['timestamp'] ? "?{$on['timestamp']}" : ''),
376 ($off ? $off['chemin'] . ($off['timestamp'] ? "?{$off['timestamp']}" : '') : ''),
377 (!$taille ? '' : (' ' . $taille[3]))
378 );
379 $res['src'] = $res[0];
380 $res['logo_on'] = $res[0];
381 $res['logo_off'] = $res[1];
382 $res['width'] = ($taille ? $taille[0] : '');
383 $res['height'] = ($taille ? $taille[1] : '');
384
385 return $res;
386 }
387 } else {
388 if (defined('_LOGO_RUBRIQUE_DESACTIVER_HERITAGE')) {
389 return '';
390 } else {
391 if ($id_rubrique) {
392 $cle_objet = 'id_rubrique';
393 $id = $id_rubrique;
394 $id_rubrique = 0;
395 } else {
396 if ($id and $cle_objet == 'id_rubrique') {
397 $id = quete_parent($id);
398 } else {
399 return '';
400 }
401 }
402 }
403 }
404 }
405 }
406
407 /**
408 * Chercher le logo d'un contenu précis
409 *
410 * @param int $id_objet
411 * Idenfiant de l'objet dont on cherche le logo
412 * @param string $objet
413 * Type de l'objet dont on cherche le logo
414 * @param string $mode
415 * "on" ou "off" suivant le logo normal ou survol
416 **/
417 function quete_logo_objet($id_objet, $objet, $mode) {
418 static $chercher_logo;
419 if (is_null($chercher_logo)) {
420 $chercher_logo = charger_fonction('chercher_logo', 'inc');
421 }
422 $cle_objet = id_table_objet($objet);
423
424 // On cherche pas la méthode classique
425 $infos_logo = $chercher_logo($id_objet, $cle_objet, $mode);
426 // Si la méthode classique a trouvé quelque chose, on utilise le nouveau format
427 if (!empty($infos_logo)) {
428 $infos_logo = array(
429 'chemin' => $infos_logo[0],
430 'timestamp' => $infos_logo[4],
431 );
432 }
433
434 // On passe cette recherche de logo dans un pipeline
435 $infos_logo = pipeline(
436 'quete_logo_objet',
437 array(
438 'args' => array(
439 'id_objet' => $id_objet,
440 'objet' => $objet,
441 'cle_objet' => $cle_objet,
442 'mode' => $mode,
443 ),
444 'data' => $infos_logo,
445 )
446 );
447
448 return $infos_logo;
449 }
450
451 /**
452 * Retourne le logo d’un fichier (document spip) sinon la vignette du type du fichier
453 *
454 * Fonction appeleé par la balise `#LOGO_DOCUMENT`
455 *
456 * @param array $row
457 * @param string $connect
458 * @return bool|string
459 */
460 function quete_logo_file($row, $connect = null) {
461 include_spip('inc/documents');
462 $logo = vignette_logo_document($row, $connect);
463 if (!$logo) {
464 $logo = image_du_document($row, $connect);
465 }
466 if (!$logo) {
467 $f = charger_fonction('vignette', 'inc');
468 $logo = $f($row['extension'], false);
469 }
470 // si c'est une vignette type doc, la renvoyer direct
471 if (strcmp($logo, _DIR_PLUGINS) == 0
472 or strcmp($logo, _DIR_PLUGINS_DIST) == 0
473 or strcmp($logo, _DIR_RACINE . 'prive/') == 0
474 ) {
475 return $logo;
476 }
477
478 return get_spip_doc($logo);
479 }
480
481 /**
482 * Trouver l'image logo d'un document
483 *
484 * @param $row
485 * description du document, issue de la base
486 * @param $lien
487 * url de lien
488 * @param $align
489 * alignement left/right
490 * @param $mode_logo
491 * mode du logo :
492 * '' => automatique (vignette sinon apercu sinon icone)
493 * icone => icone du type du fichier
494 * apercu => apercu de l'image exclusivement, meme si une vignette existe
495 * vignette => vignette exclusivement, ou rien si elle n'existe pas
496 * @param $x
497 * largeur maxi
498 * @param $y
499 * hauteur maxi
500 * @param string $connect
501 * serveur
502 * @return string
503 */
504 function quete_logo_document($row, $lien, $align, $mode_logo, $x, $y, $connect = null) {
505
506 include_spip('inc/documents');
507 $logo = '';
508 if (!in_array($mode_logo, array('icone', 'apercu'))) {
509 $logo = vignette_logo_document($row, $connect);
510 }
511 // si on veut explicitement la vignette, ne rien renvoyer si il n'y en a pas
512 if ($mode_logo == 'vignette' and !$logo) {
513 return '';
514 }
515 if ($mode_logo == 'icone') {
516 $row['fichier'] = '';
517 }
518
519 return vignette_automatique($logo, $row, $lien, $x, $y, $align, null, $connect);
520 }
521
522 /**
523 * Retourne le chemin d’un document lorsque le connect est précisé
524 *
525 * Sur un connecteur distant, voir si on connait l’adresse du site (spip distant)
526 * et l’utiliser le cas échéant.
527 *
528 * @param string $fichier Chemin
529 * @param string $connect Nom du connecteur
530 * @return string|false
531 */
532 function document_spip_externe($fichier, $connect) {
533 if ($connect) {
534 $site = quete_meta('adresse_site', $connect);
535 if ($site) {
536 $dir = quete_meta('dir_img', $connect);
537 return "$site/$dir$fichier";
538 }
539 }
540 return false;
541 }
542
543 /**
544 * Retourne la vignette explicitement attachee a un document
545 * le resutat est un fichier local existant, ou une URL
546 * ou vide si pas de vignette
547 *
548 * @param array $row
549 * @param string $connect
550 * @return string
551 */
552 function vignette_logo_document($row, $connect = '') {
553 if (!$row['id_vignette']) {
554 return '';
555 }
556 $fichier = quete_fichier($row['id_vignette'], $connect);
557 if ($url = document_spip_externe($fichier, $connect)) {
558 return $url;
559 }
560
561 $f = get_spip_doc($fichier);
562 if ($f and @file_exists($f)) {
563 return $f;
564 }
565 if ($row['mode'] !== 'vignette') {
566 return '';
567 }
568
569 return generer_url_entite($row['id_document'], 'document', '', '', $connect);
570 }
571
572 /**
573 * Calcul pour savoir si un objet est expose dans le contexte
574 * fournit par $reference
575 *
576 * @param int $id
577 * @param string $prim
578 * @param array $reference
579 * @param int $parent
580 * @param string $type
581 * @param string $connect
582 * @return bool|string
583 */
584 function calcul_exposer($id, $prim, $reference, $parent, $type, $connect = '') {
585 static $exposer = array();
586
587 // Que faut-il exposer ? Tous les elements de $reference
588 // ainsi que leur hierarchie ; on ne fait donc ce calcul
589 // qu'une fois (par squelette) et on conserve le resultat
590 // en static.
591 if (!isset($exposer[$m = md5(serialize($reference))][$prim])) {
592 $principal = isset($reference[$type]) ? $reference[$type] :
593 // cas de la pagination indecte @xx qui positionne la page avec l'id xx
594 // et donne la reference dynamique @type=xx dans le contexte
595 (isset($reference["@$type"]) ? $reference["@$type"] : '');
596 // le parent fournit en argument est le parent de $id, pas celui de $principal
597 // il n'est donc pas utile
598 $parent = 0;
599 if (!$principal) { // regarder si un enfant est dans le contexte, auquel cas il expose peut etre le parent courant
600 $enfants = array('id_rubrique' => array('id_article'), 'id_groupe' => array('id_mot'));
601 if (isset($enfants[$type])) {
602 foreach ($enfants[$type] as $t) {
603 if (isset($reference[$t])
604 // cas de la reference donnee dynamiquement par la pagination
605 or isset($reference["@$t"])
606 ) {
607 $type = $t;
608 $principal = isset($reference[$type]) ? $reference[$type] : $reference["@$type"];
609 continue;
610 }
611 }
612 }
613 }
614 $exposer[$m][$type] = array();
615 if ($principal) {
616 $principaux = is_array($principal) ? $principal : array($principal);
617 foreach ($principaux as $principal) {
618 $exposer[$m][$type][$principal] = true;
619 if ($type == 'id_mot') {
620 if (!$parent) {
621 $parent = sql_getfetsel('id_groupe', 'spip_mots', 'id_mot=' . intval($principal), '', '', '', '', $connect);
622 }
623 if ($parent) {
624 $exposer[$m]['id_groupe'][$parent] = true;
625 }
626 } else {
627 if ($type != 'id_groupe') {
628 if (!$parent) {
629 if ($type == 'id_rubrique') {
630 $parent = $principal;
631 }
632 if ($type == 'id_article') {
633 $parent = quete_rubrique($principal, $connect);
634 }
635 }
636 do {
637 $exposer[$m]['id_rubrique'][$parent] = true;
638 } while ($parent = quete_parent($parent, $connect));
639 }
640 }
641 }
642 }
643 }
644
645 // And the winner is...
646 return isset($exposer[$m][$prim]) ? isset($exposer[$m][$prim][$id]) : '';
647 }
648
649 /**
650 * Trouver le numero de page d'une pagination indirecte
651 * lorsque debut_xxx=@123
652 * on cherche la page qui contient l'item dont la cle primaire vaut 123
653 *
654 * @param string $primary
655 * @param int|string $valeur
656 * @param int $pas
657 * @param objetc $iter
658 * @return int
659 */
660 function quete_debut_pagination($primary, $valeur, $pas, $iter) {
661 // on ne devrait pas arriver ici si la cle primaire est inexistante
662 // ou composee, mais verifions
663 if (!$primary or preg_match('/[,\s]/', $primary)) {
664 return 0;
665 }
666
667 $pos = 0;
668 while ($row = $iter->fetch() and $row[$primary] != $valeur) {
669 $pos++;
670 }
671 // si on a pas trouve
672 if ($row[$primary] != $valeur) {
673 return 0;
674 }
675
676 // sinon, calculer le bon numero de page
677 return floor($pos / $pas) * $pas;
678 }