[SPIP] ~version 3.0.10-->3.0.11
[lhc/web/www.git] / www / ecrire / inc / filtres.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2012 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 include_spip('inc/charsets');
16 include_spip('inc/filtres_mini');
17 include_spip('base/objets');
18 include_spip('public/parametrer'); // charger les fichiers fonctions
19
20 /**
21 * Charger un filtre depuis le php :
22 * - on inclue tous les fichiers fonctions des plugins et du skel
23 * - on appelle chercher_filtre
24 *
25 * @param string $fonc
26 * @param string $default
27 * @return string
28 */
29 function charger_filtre($fonc, $default='filtre_identite_dist') {
30 include_spip('public/parametrer'); // inclure les fichiers fonctions
31 return chercher_filtre($fonc, $default);
32 }
33
34 function filtre_identite_dist($texte){return $texte;}
35
36 /**
37 * http://doc.spip.org/@chercher_filtre
38 *
39 * @param string $fonc
40 * @param null $default
41 * @return string
42 */
43 function chercher_filtre($fonc, $default=NULL) {
44 if (!$fonc) return $default;
45 // Cas des types mime, sans confondre avec les appels de fonction de classe
46 // Foo::Bar
47 // qui peuvent etre avec un namespace : space\Foo::Bar
48 if (preg_match(',^[\w]+/,',$fonc)){
49 $nom = preg_replace(',\W,','_', $fonc);
50 $f = chercher_filtre($nom);
51 // cas du sous-type MIME sans filtre associe, passer au type:
52 // si filtre_text_plain pas defini, passe a filtre_text
53 if (!$f AND $nom!==$fonc)
54 $f = chercher_filtre(preg_replace(',\W.*$,','', $fonc));
55 return $f;
56 }
57 foreach (
58 array('filtre_'.$fonc, 'filtre_'.$fonc.'_dist', $fonc) as $f){
59 if (isset( $GLOBALS['spip_matrice'][$f]) AND is_string($g = $GLOBALS['spip_matrice'][$f]))
60 find_in_path($g,'', true);
61 if (function_exists($f)
62 OR (preg_match("/^(\w*)::(\w*)$/", $f, $regs)
63 AND is_callable(array($regs[1], $regs[2]))
64 )) {
65 return $f;
66 }
67 }
68 return $default;
69 }
70
71 /**
72 * Applique un filtre
73 *
74 * Fonction générique qui prend en argument l’objet (texte, etc) à modifier
75 * et le nom du filtre. Retrouve les arguments du filtre demandé dans les arguments
76 * transmis à cette fonction, via func_get_args().
77 *
78 * @see filtrer() Assez proche
79 *
80 * @param string $arg
81 * Texte sur lequel appliquer le filtre
82 * @param string $filtre
83 * Nom du filtre a appliquer
84 * @param string $force
85 * La fonction doit-elle retourner le texte ou rien ?
86 * @return string
87 * Texte avec le filtre appliqué s'il a été trouvé,
88 * Texte sans le filtre appliqué s'il n'a pas été trouvé et que $force n'a
89 * pas été fourni,
90 * Chaîne vide si le filtre n'a pas été trouvé et que $force a été fourni.
91 **/
92 function appliquer_filtre($arg, $filtre, $force=NULL) {
93 $f = chercher_filtre($filtre);
94 if (!$f) {
95 if (!$force) return '';
96 else return $arg;
97 }
98
99 $args = func_get_args();
100 array_shift($args); // enlever $arg
101 array_shift($args); // enlever $filtre
102 array_unshift($args, $arg); // remettre $arg
103 return call_user_func_array($f,$args);
104 }
105
106 // http://doc.spip.org/@spip_version
107 function spip_version() {
108 $version = $GLOBALS['spip_version_affichee'];
109 if ($svn_revision = version_svn_courante(_DIR_RACINE))
110 $version .= ($svn_revision<0 ? ' SVN':'').' ['.abs($svn_revision).']';
111 return $version;
112 }
113
114
115 //
116 // Mention de la revision SVN courante de l'espace restreint standard
117 // (numero non garanti pour l'espace public et en cas de mutualisation)
118 // on est negatif si on est sur .svn, et positif si on utilise svn.revision
119 // http://doc.spip.org/@version_svn_courante
120 function version_svn_courante($dir) {
121 if (!$dir) $dir = '.';
122
123 // version installee par paquet ZIP
124 if (lire_fichier($dir.'/svn.revision', $c)
125 AND preg_match(',Revision: (\d+),', $c, $d))
126 return intval($d[1]);
127
128 // version installee par SVN
129 if (lire_fichier($dir . '/.svn/entries', $c)
130 AND (
131 (preg_match_all(
132 ',committed-rev="([0-9]+)",', $c, $r1, PREG_PATTERN_ORDER)
133 AND $v = max($r1[1])
134 )
135 OR
136 (preg_match(',^\d.*dir[\r\n]+(\d+),ms', $c, $r1) # svn >= 1.4
137 AND $v = $r1[1]
138 )))
139 return -$v;
140
141 // Bug ou paquet fait main
142 return 0;
143 }
144
145 // La matrice est necessaire pour ne filtrer _que_ des fonctions definies dans filtres_images
146 // et laisser passer les fonctions personnelles baptisees image_...
147 $GLOBALS['spip_matrice']['image_graver'] = true;//'inc/filtres_images_mini.php';
148 $GLOBALS['spip_matrice']['image_select'] = true;//'inc/filtres_images_mini.php';
149 $GLOBALS['spip_matrice']['image_reduire'] = true;//'inc/filtres_images_mini.php';
150 $GLOBALS['spip_matrice']['image_reduire_par'] = true;//'inc/filtres_images_mini.php';
151 $GLOBALS['spip_matrice']['image_passe_partout'] = true;//'inc/filtres_images_mini.php';
152
153 $GLOBALS['spip_matrice']['couleur_html_to_hex'] = 'inc/filtres_images_mini.php';
154 $GLOBALS['spip_matrice']['couleur_foncer'] = 'inc/filtres_images_mini.php';
155 $GLOBALS['spip_matrice']['couleur_eclaircir'] = 'inc/filtres_images_mini.php';
156
157 // ou pour inclure un script au moment ou l'on cherche le filtre
158 $GLOBALS['spip_matrice']['filtre_image_dist'] = 'inc/filtres_mime.php';
159 $GLOBALS['spip_matrice']['filtre_audio_dist'] = 'inc/filtres_mime.php';
160 $GLOBALS['spip_matrice']['filtre_video_dist'] = 'inc/filtres_mime.php';
161 $GLOBALS['spip_matrice']['filtre_application_dist'] = 'inc/filtres_mime.php';
162 $GLOBALS['spip_matrice']['filtre_message_dist'] = 'inc/filtres_mime.php';
163 $GLOBALS['spip_matrice']['filtre_multipart_dist'] = 'inc/filtres_mime.php';
164 $GLOBALS['spip_matrice']['filtre_text_dist'] = 'inc/filtres_mime.php';
165 $GLOBALS['spip_matrice']['filtre_text_csv_dist'] = 'inc/filtres_mime.php';
166 $GLOBALS['spip_matrice']['filtre_text_html_dist'] = 'inc/filtres_mime.php';
167 $GLOBALS['spip_matrice']['filtre_audio_x_pn_realaudio'] = 'inc/filtres_mime.php';
168
169
170 // charge les fonctions graphiques et applique celle demandee
171 // http://doc.spip.org/@filtrer
172 function filtrer($filtre) {
173 if (isset($GLOBALS['spip_matrice'][$filtre]) and is_string($f = $GLOBALS['spip_matrice'][$filtre])){
174 find_in_path($f,'', true);
175 $GLOBALS['spip_matrice'][$filtre] = true;
176 }
177 $tous = func_get_args();
178 if (substr($filtre,0,6)=='image_' && $GLOBALS['spip_matrice'][$filtre])
179 return image_filtrer($tous);
180 elseif($f = chercher_filtre($filtre)) {
181 array_shift($tous);
182 return call_user_func_array($f, $tous);
183 }
184 else {
185 // le filtre n'existe pas, on provoque une erreur
186 $msg = array('zbug_erreur_filtre', array('filtre'=>texte_script($filtre)));
187 erreur_squelette($msg);
188 return '';
189 }
190 }
191
192 // fonction generique d'entree des filtres images
193 // accepte en entree un texte complet, un img-log (produit par #LOGO_XX),
194 // un tag <img ...> complet, ou encore un nom de fichier *local* (passer
195 // le filtre |copie_locale si on veut l'appliquer a un document)
196 // applique le filtre demande a chacune des occurrences
197
198 // http://doc.spip.org/@image_filtrer
199 function image_filtrer($args){
200 $filtre = array_shift($args); # enlever $filtre
201 $texte = array_shift($args);
202 if (!strlen($texte)) return;
203 find_in_path('filtres_images_mini.php','inc/', true);
204 statut_effacer_images_temporaires(true); // activer la suppression des images temporaires car le compilo finit la chaine par un image_graver
205 // Cas du nom de fichier local
206 if ( strpos(substr($texte,strlen(_DIR_RACINE)),'..')===FALSE
207 AND !preg_match(',^/|[<>]|\s,S', $texte)
208 AND (
209 file_exists(preg_replace(',[?].*$,','',$texte))
210 OR preg_match(';^(\w{3,7}://);', $texte)
211 )) {
212 array_unshift($args,"<img src='$texte' />");
213 $res = call_user_func_array($filtre, $args);
214 statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
215 return $res;
216 }
217
218 // Cas general : trier toutes les images, avec eventuellement leur <span>
219 if (preg_match_all(
220 ',(<([a-z]+) [^<>]*spip_documents[^<>]*>)?\s*(<img\s.*>),UimsS',
221 $texte, $tags, PREG_SET_ORDER)) {
222 foreach ($tags as $tag) {
223 $class = extraire_attribut($tag[3],'class');
224 if (!$class || (strpos($class,'no_image_filtrer')===FALSE)){
225 array_unshift($args,$tag[3]);
226 if ($reduit = call_user_func_array($filtre, $args)) {
227 // En cas de span spip_documents, modifier le style=...width:
228 if($tag[1]){
229 $w = extraire_attribut($reduit, 'width');
230 if (!$w AND preg_match(",width:\s*(\d+)px,S",extraire_attribut($reduit,'style'),$regs))
231 $w = $regs[1];
232 if ($w AND ($style = extraire_attribut($tag[1], 'style'))){
233 $style = preg_replace(",width:\s*\d+px,S", "width:${w}px", $style);
234 $replace = inserer_attribut($tag[1], 'style', $style);
235 $texte = str_replace($tag[1], $replace, $texte);
236 }
237 }
238 // traiter aussi un eventuel mouseover
239 if ($mouseover = extraire_attribut($reduit,'onmouseover')){
240 if (preg_match(",this[.]src=['\"]([^'\"]+)['\"],ims", $mouseover, $match)){
241 $srcover = $match[1];
242 array_shift($args);
243 array_unshift($args,"<img src='".$match[1]."' />");
244 $srcover_filter = call_user_func_array($filtre, $args);
245 $srcover_filter = extraire_attribut($srcover_filter,'src');
246 $reduit = str_replace($srcover,$srcover_filter,$reduit);
247 }
248 }
249 $texte = str_replace($tag[3], $reduit, $texte);
250 }
251 array_shift($args);
252 }
253 }
254 }
255 statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
256 return $texte;
257 }
258
259 //
260 // Retourner taille d'une image
261 // pour les filtres |largeur et |hauteur
262 //
263 // http://doc.spip.org/@taille_image
264 function taille_image($img) {
265
266 static $largeur_img =array(), $hauteur_img= array();
267 $srcWidth = 0;
268 $srcHeight = 0;
269
270 $logo = extraire_attribut($img,'src');
271
272 if (!$logo) $logo = $img;
273 else {
274 $srcWidth = extraire_attribut($img,'width');
275 $srcHeight = extraire_attribut($img,'height');
276 }
277
278 // ne jamais operer directement sur une image distante pour des raisons de perfo
279 // la copie locale a toutes les chances d'etre la ou de resservir
280 if (preg_match(';^(\w{3,7}://);', $logo)){
281 include_spip('inc/distant');
282 $fichier = copie_locale($logo);
283 $logo = $fichier ? _DIR_RACINE . $fichier : $logo;
284 }
285 if (($p=strpos($logo,'?'))!==FALSE)
286 $logo=substr($logo,0,$p);
287
288 $srcsize = false;
289 if (isset($largeur_img[$logo]))
290 $srcWidth = $largeur_img[$logo];
291 if (isset($hauteur_img[$logo]))
292 $srcHeight = $hauteur_img[$logo];
293 if (!$srcWidth OR !$srcHeight){
294 if (file_exists($logo)
295 AND $srcsize = @getimagesize($logo)){
296 if (!$srcWidth) $largeur_img[$logo] = $srcWidth = $srcsize[0];
297 if (!$srcHeight) $hauteur_img[$logo] = $srcHeight = $srcsize[1];
298 }
299 // $logo peut etre une reference a une image temporaire dont a n'a que le log .src
300 // on s'y refere, l'image sera reconstruite en temps utile si necessaire
301 elseif(@file_exists($f = "$logo.src")
302 AND lire_fichier($f,$valeurs)
303 AND $valeurs=unserialize($valeurs)) {
304 if (!$srcWidth) $largeur_img[$logo] = $srcWidth = $valeurs["largeur_dest"];
305 if (!$srcHeight) $hauteur_img[$logo] = $srcHeight = $valeurs["hauteur_dest"];
306 }
307 }
308 return array($srcHeight, $srcWidth);
309 }
310 // http://doc.spip.org/@largeur
311 function largeur($img) {
312 if (!$img) return;
313 list ($h,$l) = taille_image($img);
314 return $l;
315 }
316 // http://doc.spip.org/@hauteur
317 function hauteur($img) {
318 if (!$img) return;
319 list ($h,$l) = taille_image($img);
320 return $h;
321 }
322
323
324 // Echappement des entites HTML avec correction des entites "brutes"
325 // (generees par les butineurs lorsqu'on rentre des caracteres n'appartenant
326 // pas au charset de la page [iso-8859-1 par defaut])
327 //
328 // Attention on limite cette correction aux caracteres "hauts" (en fait > 99
329 // pour aller plus vite que le > 127 qui serait logique), de maniere a
330 // preserver des echappements de caracteres "bas" (par exemple [ ou ")
331 // et au cas particulier de &amp; qui devient &amp;amp; dans les url
332 // http://doc.spip.org/@corriger_entites_html
333 function corriger_entites_html($texte) {
334 if (strpos($texte,'&amp;') === false) return $texte;
335 return preg_replace(',&amp;(#[0-9][0-9][0-9]+;|amp;),iS', '&\1', $texte);
336 }
337 // idem mais corriger aussi les &amp;eacute; en &eacute;
338 // http://doc.spip.org/@corriger_toutes_entites_html
339 function corriger_toutes_entites_html($texte) {
340 if (strpos($texte,'&amp;') === false) return $texte;
341 return preg_replace(',&amp;(#?[a-z0-9]+;),iS', '&\1', $texte);
342 }
343
344 // http://doc.spip.org/@proteger_amp
345 function proteger_amp($texte){
346 return str_replace('&','&amp;',$texte);
347 }
348
349 //
350 /**
351 * http://doc.spip.org/@entites_html
352 *
353 * @param string $texte
354 * chaine a echapper
355 * @param bool $tout
356 * corriger toutes les &amp;xx; en &xx;
357 * @param bool $quote
358 * echapper aussi les simples quotes en &#039;
359 * @return mixed|string
360 */
361 function entites_html($texte, $tout=false, $quote=true) {
362 if (!is_string($texte) OR !$texte
363 OR strpbrk($texte, "&\"'<>")==false
364 ) return $texte;
365 include_spip('inc/texte');
366 $texte = htmlspecialchars(echappe_retour(echappe_html($texte,'',true),'','proteger_amp'),$quote?ENT_QUOTES:(ENT_COMPAT|ENT_HTML401));
367 if ($tout)
368 return corriger_toutes_entites_html($texte);
369 else
370 return corriger_entites_html($texte);
371 }
372
373 // Transformer les &eacute; dans le charset local
374 // http://doc.spip.org/@filtrer_entites
375 function filtrer_entites($texte) {
376 if (strpos($texte,'&') === false) return $texte;
377 // filtrer
378 $texte = html2unicode($texte);
379 // remettre le tout dans le charset cible
380 return unicode2charset($texte);
381 }
382
383 // caracteres de controle - http://www.w3.org/TR/REC-xml/#charsets
384 // http://doc.spip.org/@supprimer_caracteres_illegaux
385 function supprimer_caracteres_illegaux($texte) {
386 static $from = "\x0\x1\x2\x3\x4\x5\x6\x7\x8\xB\xC\xE\xF\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
387 static $to = null;
388
389 if (is_array($texte)) {
390 return array_map('corriger_caracteres_windows', $texte);
391 }
392
393 if (!$to) $to = str_repeat('-', strlen($from));
394 return strtr($texte, $from, $to);
395 }
396
397 // Supprimer caracteres windows et les caracteres de controle ILLEGAUX
398 // http://doc.spip.org/@corriger_caracteres
399 function corriger_caracteres ($texte) {
400 $texte = corriger_caracteres_windows($texte);
401 $texte = supprimer_caracteres_illegaux($texte);
402 return $texte;
403 }
404
405 /**
406 * Encode du HTML pour transmission XML
407 * notamment dans les flux RSS
408 *
409 * http://doc.spip.org/@texte_backend
410 *
411 * @param $texte
412 * @return mixed
413 */
414 function texte_backend($texte) {
415
416 static $apostrophe = array("&#8217;", "'"); # n'allouer qu'une fois
417
418 // si on a des liens ou des images, les passer en absolu
419 $texte = liens_absolus($texte);
420
421 // echapper les tags &gt; &lt;
422 $texte = preg_replace(',&(gt|lt);,S', '&amp;\1;', $texte);
423
424 // importer les &eacute;
425 $texte = filtrer_entites($texte);
426
427 // " -> &quot; et tout ce genre de choses
428 $u = $GLOBALS['meta']['pcre_u'];
429 $texte = str_replace("&nbsp;", " ", $texte);
430 $texte = preg_replace('/\s{2,}/S'.$u, " ", $texte);
431 // ne pas echapper les sinqle quotes car certains outils de syndication gerent mal
432 $texte = entites_html($texte, false, false);
433
434 // verifier le charset
435 $texte = charset2unicode($texte);
436
437 // Caracteres problematiques en iso-latin 1
438 if ($GLOBALS['meta']['charset'] == 'iso-8859-1') {
439 $texte = str_replace(chr(156), '&#156;', $texte);
440 $texte = str_replace(chr(140), '&#140;', $texte);
441 $texte = str_replace(chr(159), '&#159;', $texte);
442 }
443
444 // l'apostrophe curly pose probleme a certains lecteure de RSS
445 // et le caractere apostrophe alourdit les squelettes avec PHP
446 // ==> on les remplace par l'entite HTML
447 return str_replace($apostrophe, "'", $texte);
448 }
449
450 // Comme ci-dessus, mais avec addslashes final pour squelettes avec PHP (rss)
451
452 function texte_backendq($texte) {
453 return addslashes(texte_backend($texte));
454 }
455
456 // Enleve le numero des titres numerotes ("1. Titre" -> "Titre")
457 // http://doc.spip.org/@supprimer_numero
458 function supprimer_numero($texte) {
459 return preg_replace(
460 ",^[[:space:]]*([0-9]+)([.)]|".chr(194).'?'.chr(176).")[[:space:]]+,S",
461 "", $texte);
462 }
463
464 // et la fonction inverse
465 // http://doc.spip.org/@recuperer_numero
466 function recuperer_numero($texte) {
467 if (preg_match(
468 ",^[[:space:]]*([0-9]+)([.)]|".chr(194).'?'.chr(176).")[[:space:]]+,S",
469 $texte, $regs))
470 return intval($regs[1]);
471 else
472 return '';
473 }
474
475 // Suppression basique et brutale de tous les <...>
476 // http://doc.spip.org/@supprimer_tags
477 function supprimer_tags($texte, $rempl = "") {
478 $texte = preg_replace(",<[^>]*>,US", $rempl, $texte);
479 // ne pas oublier un < final non ferme
480 // mais qui peut aussi etre un simple signe plus petit que
481 $texte = str_replace('<', ' ', $texte);
482 return $texte;
483 }
484
485 // Convertit les <...> en la version lisible en HTML
486 // http://doc.spip.org/@echapper_tags
487 function echapper_tags($texte, $rempl = "") {
488 $texte = preg_replace("/<([^>]*)>/", "&lt;\\1&gt;", $texte);
489 return $texte;
490 }
491
492 // Convertit un texte HTML en texte brut
493 // http://doc.spip.org/@textebrut
494 function textebrut($texte) {
495 $u = $GLOBALS['meta']['pcre_u'];
496 $texte = preg_replace('/\s+/S'.$u, " ", $texte);
497 $texte = preg_replace("/<(p|br)( [^>]*)?".">/iS", "\n\n", $texte);
498 $texte = preg_replace("/^\n+/", "", $texte);
499 $texte = preg_replace("/\n+$/", "", $texte);
500 $texte = preg_replace("/\n +/", "\n", $texte);
501 $texte = supprimer_tags($texte);
502 $texte = preg_replace("/(&nbsp;| )+/S", " ", $texte);
503 // nettoyer l'apostrophe curly qui pose probleme a certains rss-readers, lecteurs de mail...
504 $texte = str_replace("&#8217;","'",$texte);
505 return $texte;
506 }
507
508 // Remplace les liens SPIP en liens ouvrant dans une nouvelle fenetre (target=blank)
509 // http://doc.spip.org/@liens_ouvrants
510 function liens_ouvrants ($texte) {
511 return preg_replace(",<a\s+([^>]*https?://[^>]*class=[\"']spip_(out|url)\b[^>]+)>,",
512 "<a \\1 target=\"_blank\">", $texte);
513 }
514
515 /**
516 * Ajouter un attribut rel="nofollow" sur tous les liens d'un texte
517 * @param string $texte
518 * @return string
519 */
520 function liens_nofollow($texte) {
521 if (stripos($texte,"<a")===false)
522 return $texte;
523
524 if (preg_match_all(",<a\b[^>]*>,UimsS",$texte, $regs, PREG_PATTERN_ORDER)){
525 foreach($regs[0] as $a){
526 $rel = extraire_attribut($a,"rel");
527 if (strpos($rel,"nofollow")===false){
528 $rel = "nofollow" . ($rel?" $rel":"");
529 $anofollow = inserer_attribut($a,"rel",$rel);
530 $texte = str_replace($a,$anofollow,$texte);
531 }
532 }
533 }
534
535 return $texte;
536 }
537
538 // Transformer les sauts de paragraphe en simples passages a la ligne
539 // http://doc.spip.org/@PtoBR
540 function PtoBR($texte){
541 $u = $GLOBALS['meta']['pcre_u'];
542 $texte = preg_replace("@</p>@iS", "\n", $texte);
543 $texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
544 $texte = preg_replace("@^\s*<br />@S".$u, "", $texte);
545 return $texte;
546 }
547
548
549 /**
550 * lignes_longues assure qu'un texte ne vas pas deborder d'un bloc
551 * par la faute d'un mot trop long (souvent des URLs)
552 * Ne devrait plus etre utilise et fait directement en CSS par un style
553 * word-wrap:break-word;
554 * cf http://www.alsacreations.com/tuto/lire/1038-gerer-debordement-contenu-css.html
555 *
556 * Pour assurer la compatibilite du filtre, on encapsule le contenu par
557 * un div ou span portant ce style inline.
558 *
559 * http://doc.spip.org/@lignes_longues
560 *
561 * @param string $texte
562 * @return string
563 */
564 function lignes_longues($texte) {
565 if (!strlen(trim($texte))) return $texte;
566 include_spip('inc/texte');
567 $tag = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $texte) ?
568 'div' : 'span';
569
570 return "<$tag style='word-wrap:break-word;'>$texte</$tag>";
571 }
572
573 // Majuscules y compris accents, en HTML
574 // http://doc.spip.org/@majuscules
575 function majuscules($texte) {
576 if (!strlen($texte)) return '';
577
578 // Cas du turc
579 if ($GLOBALS['spip_lang'] == 'tr') {
580 # remplacer hors des tags et des entites
581 if (preg_match_all(',<[^<>]+>|&[^;]+;,S', $texte, $regs, PREG_SET_ORDER))
582 foreach ($regs as $n => $match)
583 $texte = str_replace($match[0], "@@SPIP_TURC$n@@", $texte);
584
585 $texte = str_replace('i', '&#304;', $texte);
586
587 if ($regs)
588 foreach ($regs as $n => $match)
589 $texte = str_replace("@@SPIP_TURC$n@@", $match[0], $texte);
590 }
591
592 // Cas general
593 return "<span style='text-transform: uppercase;'>$texte</span>";
594 }
595
596 // "127.4 ko" ou "3.1 Mo"
597 // http://doc.spip.org/@taille_en_octets
598 function taille_en_octets ($taille) {
599 if ($taille < 1) return '';
600 if ($taille < 1024) {$taille = _T('taille_octets', array('taille' => $taille));}
601 else if ($taille < 1024*1024) {
602 $taille = _T('taille_ko', array('taille' => round($taille/1024, 1)));
603 } else if ($taille < 1024*1024*1024) {
604 $taille = _T('taille_mo', array('taille' => round($taille/1024/1024, 1)));
605 } else {
606 $taille = _T('taille_go', array('taille' => round($taille/1024/1024/1024, 2)));
607 }
608 return $taille;
609 }
610
611
612 // Rend une chaine utilisable sans dommage comme attribut HTML
613 // http://doc.spip.org/@attribut_html
614 function attribut_html($texte,$textebrut = true) {
615 $u = $GLOBALS['meta']['pcre_u'];
616 if ($textebrut)
617 $texte = preg_replace(array(",\n,",",\s(?=\s),msS".$u),array(" ",""),textebrut($texte));
618 $texte = texte_backend($texte);
619 $texte = str_replace(array("'",'"'),array('&#039;', '&#034;'), $texte);
620
621 return preg_replace(array("/&(amp;|#38;)/","/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/"),array("&","&#38;") , $texte);
622 }
623
624 // Vider les url nulles comme 'http://' ou 'mailto:'
625 // et leur appliquer un htmlspecialchars() + gerer les &amp;
626 // http://doc.spip.org/@vider_url
627 function vider_url($url, $entites = true) {
628 # un message pour abs_url
629 $GLOBALS['mode_abs_url'] = 'url';
630
631 $url = trim($url);
632 if (preg_match(",^(http:?/?/?|mailto:?)$,iS", $url))
633 return '';
634
635 if ($entites) $url = entites_html($url);
636
637 return $url;
638 }
639
640 // Extraire une date de n'importe quel champ (a completer...)
641 // http://doc.spip.org/@extraire_date
642 function extraire_date($texte) {
643 // format = 2001-08
644 if (preg_match(",([1-2][0-9]{3})[^0-9]*(1[0-2]|0?[1-9]),",$texte,$regs))
645 return $regs[1]."-".sprintf("%02d", $regs[2])."-01";
646 }
647
648 // Maquiller une adresse e-mail
649 // http://doc.spip.org/@antispam
650 function antispam($texte) {
651 include_spip('inc/acces');
652 $masque = creer_pass_aleatoire(3);
653 return preg_replace("/@/", " $masque ", $texte);
654 }
655
656 // http://doc.spip.org/@securiser_acces
657 function securiser_acces($id_auteur, $cle, $dir, $op='', $args='')
658 {
659 include_spip('inc/acces');
660 if ($op) $dir .= " $op $args";
661 return verifier_low_sec($id_auteur, $cle, $dir);
662 }
663
664 /**
665 * La fonction sinon retourne le second parametre lorsque
666 * le premier est considere vide, sinon retourne le premier parametre.
667 *
668 * En php sinon($a, 'rien') retourne $a ou 'rien' si $a est vide.
669 * En filtre spip |sinon{#TEXTE, rien} : affiche #TEXTE ou "rien" si #TEXTE est vide,
670 *
671 * Note : l'utilisation de |sinon en tant que filtre de squelette
672 * est directement compile dans public/references par la fonction filtre_logique()
673 *
674 * @param mixed $texte
675 * Contenu de reference a tester
676 * @param mixed $sinon
677 * Contenu a retourner si le contenu de reference est vide
678 * @return mixed
679 * Retourne $texte, sinon $sinon.
680 **/
681 function sinon ($texte, $sinon='') {
682 if ($texte OR (!is_array($texte) AND strlen($texte)))
683 return $texte;
684 else
685 return $sinon;
686 }
687
688 // |choixsivide{vide,pasvide} affiche pasvide si la chaine n'est pas vide...
689 // http://doc.spip.org/@choixsivide
690 function choixsivide($a, $vide, $pasvide) {
691 return $a ? $pasvide : $vide;
692 }
693
694 // |choixsiegal{aquoi,oui,non} affiche oui si la chaine est egal a aquoi ...
695 // http://doc.spip.org/@choixsiegal
696 function choixsiegal($a1,$a2,$v,$f) {
697 return ($a1 == $a2) ? $v : $f;
698 }
699
700
701 //
702 // Date, heure, saisons
703 //
704
705 // on normalise la date, si elle vient du contexte (public/parametrer.php), on force le jour
706 // http://doc.spip.org/@normaliser_date
707 function normaliser_date($date, $forcer_jour = false) {
708 $date = vider_date($date);
709 if ($date) {
710 if (preg_match("/^[0-9]{8,10}$/", $date))
711 $date = date("Y-m-d H:i:s", $date);
712 if (preg_match("#^([12][0-9]{3})([-/]00)?( [-0-9:]+)?$#", $date, $regs))
713 $date = $regs[1]."-00-00".$regs[3];
714 else if (preg_match("#^([12][0-9]{3}[-/][01]?[0-9])([-/]00)?( [-0-9:]+)?$#", $date, $regs))
715 $date = preg_replace("@/@","-",$regs[1])."-00".$regs[3];
716 else
717 $date = date("Y-m-d H:i:s", strtotime($date));
718
719 if ($forcer_jour)
720 $date = str_replace('-00', '-01', $date);
721 }
722 return $date;
723 }
724
725 // http://doc.spip.org/@vider_date
726 function vider_date($letexte) {
727 if (strncmp("0000-00-00", $letexte,10)==0) return '';
728 if (strncmp("0001-01-01", $letexte,10)==0) return '';
729 if (strncmp("1970-01-01", $letexte,10)==0) return ''; // eviter le bug GMT-1
730 return $letexte;
731 }
732
733 // http://doc.spip.org/@recup_heure
734 function recup_heure($date){
735
736 static $d = array(0,0,0);
737 if (!preg_match('#([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $date, $r))
738 return $d;
739
740 array_shift($r);
741 return $r;
742 }
743
744 // http://doc.spip.org/@heures
745 function heures($numdate) {
746 $date_array = recup_heure($numdate);
747 if ($date_array)
748 list($heures, $minutes, $secondes) = $date_array;
749 return $heures;
750 }
751
752 // http://doc.spip.org/@minutes
753 function minutes($numdate) {
754 $date_array = recup_heure($numdate);
755 if ($date_array)
756 list($heures, $minutes, $secondes) = $date_array;
757 return $minutes;
758 }
759
760 // http://doc.spip.org/@secondes
761 function secondes($numdate) {
762 $date_array = recup_heure($numdate);
763 if ($date_array)
764 list($heures,$minutes,$secondes) = $date_array;
765 return $secondes;
766 }
767
768 // http://doc.spip.org/@heures_minutes
769 function heures_minutes($numdate) {
770 return _T('date_fmt_heures_minutes', array('h'=> heures($numdate), 'm'=> minutes($numdate)));
771 }
772
773 // http://doc.spip.org/@recup_date
774 function recup_date($numdate, $forcer_jour = true){
775 if (!$numdate) return '';
776 $heures = $minutes = $secondes = 0;
777 if (preg_match('#([0-9]{1,2})/([0-9]{1,2})/([0-9]{4}|[0-9]{1,2})#', $numdate, $regs)) {
778 $jour = $regs[1];
779 $mois = $regs[2];
780 $annee = $regs[3];
781 if ($annee < 90){
782 $annee = 2000 + $annee;
783 } elseif ($annee<100) {
784 $annee = 1900 + $annee ;
785 }
786 list($heures, $minutes, $secondes) = recup_heure($numdate);
787
788 }
789 elseif (preg_match('#([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})#',$numdate, $regs)) {
790 $annee = $regs[1];
791 $mois = $regs[2];
792 $jour = $regs[3];
793 list($heures, $minutes, $secondes) = recup_heure($numdate);
794 }
795 elseif (preg_match('#([0-9]{4})-([0-9]{2})#', $numdate, $regs)){
796 $annee = $regs[1];
797 $mois = $regs[2];
798 $jour ='';
799 list($heures, $minutes, $secondes) = recup_heure($numdate);
800 }
801 elseif (preg_match('#^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$#', $numdate, $regs)){
802 $annee = $regs[1];
803 $mois = $regs[2];
804 $jour = $regs[3];
805 $heures = $regs[4];
806 $minutes = $regs[5];
807 $secondes = $regs[6];
808 } else $annee = $mois = $jour ='';
809 if ($annee > 4000) $annee -= 9000;
810 if (substr($jour, 0, 1) == '0') $jour = substr($jour, 1);
811
812 if ($forcer_jour AND $jour == '0') $jour = '1';
813 if ($forcer_jour AND $mois == '0') $mois = '1';
814 if ($annee OR $mois OR $jour OR $heures OR $minutes OR $secondes)
815 return array($annee, $mois, $jour, $heures, $minutes, $secondes);
816 }
817
818 // une date pour l'interface : utilise date_relative si le decalage
819 // avec time() est de moins de douze heures, sinon la date complete
820 // http://doc.spip.org/@date_interface
821 function date_interface($date, $decalage_maxi = 43200/* 12*3600 */) {
822 return sinon(
823 date_relative($date, $decalage_maxi),
824 affdate_heure($date)
825 );
826 }
827
828 // http://doc.spip.org/@date_relative
829 function date_relative($date, $decalage_maxi=0,$ref_date=null) {
830
831 if (is_null($ref_date))
832 $ref_time = time();
833 else
834 $ref_time = strtotime($ref_date);
835
836 if (!$date) return;
837 $decal = date("U",$ref_time) - date("U", strtotime($date));
838
839 if ($decalage_maxi AND ($decal > $decalage_maxi OR $decal < 0))
840 return '';
841
842 if ($decal < 0) {
843 $il_y_a = "date_dans";
844 $decal = -1 * $decal;
845 } else {
846 $il_y_a = "date_il_y_a";
847 }
848
849 if ($decal > 3600 * 24 * 30 * 6)
850 return affdate_court($date);
851
852 if ($decal > 3600 * 24 * 30) {
853 $mois = floor ($decal / (3600 * 24 * 30));
854 if ($mois < 2)
855 $delai = "$mois "._T("date_un_mois");
856 else
857 $delai = "$mois "._T("date_mois");
858 }
859 else if ($decal > 3600 * 24 * 7) {
860 $semaines = floor ($decal / (3600 * 24 * 7));
861 if ($semaines < 2)
862 $delai = "$semaines "._T("date_une_semaine");
863 else
864 $delai = "$semaines "._T("date_semaines");
865 }
866 else if ($decal > 3600 * 24) {
867 $jours = floor ($decal / (3600 * 24));
868 if ($jours < 2)
869 return $il_y_a=="date_dans"?_T("date_demain"):_T("date_hier");
870 else
871 $delai = "$jours "._T("date_jours");
872 }
873 else if ($decal >= 3600) {
874 $heures = floor ($decal / 3600);
875 if ($heures < 2)
876 $delai = "$heures "._T("date_une_heure");
877 else
878 $delai = "$heures "._T("date_heures");
879 }
880 else if ($decal >= 60) {
881 $minutes = floor($decal / 60);
882 if ($minutes < 2)
883 $delai = "$minutes "._T("date_une_minute");
884 else
885 $delai = "$minutes "._T("date_minutes");
886 } else {
887 $secondes = ceil($decal);
888 if ($secondes < 2)
889 $delai = "$secondes "._T("date_une_seconde");
890 else
891 $delai = "$secondes "._T("date_secondes");
892 }
893
894 return _T($il_y_a, array("delai"=> $delai));
895 }
896
897
898 // http://doc.spip.org/@date_relativecourt
899 function date_relativecourt($date, $decalage_maxi=0) {
900
901 if (!$date) return;
902 $decal = date("U",strtotime(date('Y-m-d'))-strtotime(date('Y-m-d',strtotime($date))));
903
904 if ($decalage_maxi AND ($decal > $decalage_maxi OR $decal < 0))
905 return '';
906
907 if ($decal < -24*3600) {
908 $retour = date_relative($date, $decalage_maxi);
909 }
910 elseif ($decal < 0) {
911 $retour = _T("date_demain");
912 }
913 else if ($decal < (3600 * 24) ) {
914 $retour = _T("date_aujourdhui");
915 }
916 else if ($decal < (3600 * 24 *2) ) {
917 $retour = _T("date_hier");
918 }
919 else {
920 $retour = date_relative($date, $decalage_maxi);
921 }
922
923
924
925 return $retour;
926 }
927
928 /**
929 * Formatage humain de la date $numdate selon le format $vue
930 * http://doc.spip.org/@affdate_base
931 *
932 * @param $numdate
933 * @param $vue
934 * @param array $options
935 * param : 'abbr' ou 'initiale' permet d'afficher les jours au format court ou initiale
936 * annee_courante : permet de definir l'annee de reference pour l'affichage des dates courtes
937 * @return mixed|string
938 */
939 function affdate_base($numdate, $vue, $options = array()) {
940 if (is_string($options))
941 $options = array('param'=>$options);
942 $date_array = recup_date($numdate, false);
943 if (!$date_array) return;
944 list($annee, $mois, $jour, $heures, $minutes, $secondes)= $date_array;
945
946 // 1er, 21st, etc.
947 $journum = $jour;
948
949 if ($jour == 0) {
950 $jour = '';
951 } else {
952 $njour = intval($jour);
953 if ($jourth = _T('date_jnum'.$jour))
954 $jour = $jourth;
955 }
956
957 $mois = intval($mois);
958 if ($mois > 0 AND $mois < 13) {
959 $nommois = _T('date_mois_'.$mois);
960 if ($jour)
961 $jourmois = _T('date_de_mois_'.$mois, array('j'=>$jour, 'nommois'=>$nommois));
962 else
963 $jourmois = $nommois;
964 } else $nommois = '';
965
966 if ($annee < 0) {
967 $annee = -$annee." "._T('date_avant_jc');
968 $avjc = true;
969 }
970 else $avjc = false;
971
972 switch ($vue) {
973 case 'saison':
974 $saison = '';
975 if ($mois > 0){
976 $saison = 1;
977 if (($mois == 3 AND $jour >= 21) OR $mois > 3) $saison = 2;
978 if (($mois == 6 AND $jour >= 21) OR $mois > 6) $saison = 3;
979 if (($mois == 9 AND $jour >= 21) OR $mois > 9) $saison = 4;
980 if (($mois == 12 AND $jour >= 21) OR $mois > 12) $saison = 1;
981 }
982 return $saison?_T('date_saison_'.$saison):'';
983
984 case 'court':
985 if ($avjc) return $annee;
986 $a = ((isset($options['annee_courante']) AND $options['annee_courante'])?$options['annee_courante']:date('Y'));
987 if ($annee < ($a - 100) OR $annee > ($a + 100)) return $annee;
988 if ($annee != $a) return _T('date_fmt_mois_annee', array ('mois'=>$mois, 'nommois'=>spip_ucfirst($nommois), 'annee'=>$annee));
989 return _T('date_fmt_jour_mois', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
990
991 case 'jourcourt':
992 if ($avjc) return $annee;
993 $a = ((isset($options['annee_courante']) AND $options['annee_courante'])?$options['annee_courante']:date('Y'));
994 if ($annee < ($a - 100) OR $annee > ($a + 100)) return $annee;
995 if ($annee != $a) return _T('date_fmt_jour_mois_annee', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
996 return _T('date_fmt_jour_mois', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
997
998 case 'entier':
999 if ($avjc) return $annee;
1000 if ($jour)
1001 return _T('date_fmt_jour_mois_annee', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
1002 elseif ($mois)
1003 return trim(_T('date_fmt_mois_annee', array ('mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee)));
1004 else
1005 return $annee;
1006
1007 case 'nom_mois':
1008 $param = ((isset($options['param']) AND $options['param']) ? '_'.$options['param'] : '');
1009 if ($param and $mois) {
1010 return _T('date_mois_'.$mois.$param);
1011 }
1012 return $nommois;
1013
1014 case 'mois':
1015 return sprintf("%02s",$mois);
1016
1017 case 'jour':
1018 return $jour;
1019
1020 case 'journum':
1021 return $journum;
1022
1023 case 'nom_jour':
1024 if (!$mois OR !$njour)
1025 return '';
1026 $nom = mktime(1,1,1,$mois,$njour,$annee);
1027 $nom = 1+date('w',$nom);
1028 $param = ((isset($options['param']) AND $options['param']) ? '_'.$options['param'] : '');
1029 return _T('date_jour_'.$nom.$param);
1030
1031 case 'mois_annee':
1032 if ($avjc) return $annee;
1033 return trim(_T('date_fmt_mois_annee', array('mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee)));
1034
1035 case 'annee':
1036 return $annee;
1037
1038 // Cas d'une vue non definie : retomber sur le format
1039 // de date propose par http://www.php.net/date
1040 default:
1041 return date($vue, strtotime($numdate));
1042 }
1043 }
1044
1045 // http://doc.spip.org/@nom_jour
1046 function nom_jour($numdate, $forme = '') {
1047 if(!($forme == 'abbr' OR $forme == 'initiale')) $forme = '';
1048 return affdate_base($numdate, 'nom_jour', $forme);
1049 }
1050
1051 // http://doc.spip.org/@jour
1052 function jour($numdate) {
1053 return affdate_base($numdate, 'jour');
1054 }
1055
1056 // http://doc.spip.org/@journum
1057 function journum($numdate) {
1058 return affdate_base($numdate, 'journum');
1059 }
1060
1061 // http://doc.spip.org/@mois
1062 function mois($numdate) {
1063 return affdate_base($numdate, 'mois');
1064 }
1065
1066 // http://doc.spip.org/@nom_mois
1067 function nom_mois($numdate, $forme='') {
1068 if(!($forme == 'abbr')) $forme = '';
1069 return affdate_base($numdate, 'nom_mois', $forme);
1070 }
1071
1072 // http://doc.spip.org/@annee
1073 function annee($numdate) {
1074 return affdate_base($numdate, 'annee');
1075 }
1076
1077 // http://doc.spip.org/@saison
1078 function saison($numdate) {
1079 return affdate_base($numdate, 'saison');
1080 }
1081
1082 // http://doc.spip.org/@affdate
1083 function affdate($numdate, $format='entier') {
1084 return affdate_base($numdate, $format);
1085 }
1086
1087 // http://doc.spip.org/@affdate_court
1088 function affdate_court($numdate, $annee_courante=null) {
1089 return affdate_base($numdate, 'court', array('annee_courante'=>$annee_courante));
1090 }
1091
1092 // http://doc.spip.org/@affdate_jourcourt
1093 function affdate_jourcourt($numdate, $annee_courante=null) {
1094 return affdate_base($numdate, 'jourcourt', array('annee_courante'=>$annee_courante));
1095 }
1096
1097 // http://doc.spip.org/@affdate_mois_annee
1098 function affdate_mois_annee($numdate) {
1099 return affdate_base($numdate, 'mois_annee');
1100 }
1101
1102 // http://doc.spip.org/@affdate_heure
1103 function affdate_heure($numdate) {
1104 $date_array = recup_date($numdate);
1105 if (!$date_array) return;
1106 list($annee, $mois, $jour, $heures, $minutes, $sec)= $date_array;
1107 return _T('date_fmt_jour_heure', array('jour' => affdate($numdate), 'heure' => _T('date_fmt_heures_minutes', array('h'=> $heures, 'm'=> $minutes))));
1108 }
1109
1110 /**
1111 * Afficher de facon textuelle les dates de debut et fin en fonction des cas
1112 * - Lundi 20 fevrier a 18h
1113 * - Le 20 fevrier de 18h a 20h
1114 * - Du 20 au 23 fevrier
1115 * - Du 20 fevrier au 30 mars
1116 * - Du 20 fevrier 2007 au 30 mars 2008
1117 * $horaire='oui' ou true permet d'afficher l'horaire, toute autre valeur n'indique que le jour
1118 * $forme peut contenir une ou plusieurs valeurs parmi
1119 * - abbr (afficher le nom des jours en abbrege)
1120 * - hcal (generer une date au format hcal)
1121 * - jour (forcer l'affichage des jours)
1122 * - annee (forcer l'affichage de l'annee)
1123 *
1124 * @param string $date_debut
1125 * @param string $date_fin
1126 * @param string $horaire
1127 * @param string $forme
1128 * @return string
1129 */
1130 function affdate_debut_fin($date_debut, $date_fin, $horaire = 'oui', $forme=''){
1131 $abbr = $jour = '';
1132 $affdate = "affdate_jourcourt";
1133 if (strpos($forme,'abbr') !==false) $abbr = 'abbr';
1134 if (strpos($forme,'annee')!==false) $affdate = 'affdate';
1135 if (strpos($forme,'jour') !==false) $jour = 'jour';
1136
1137 $dtstart = $dtend = $dtabbr = "";
1138 if (strpos($forme,'hcal')!==false) {
1139 $dtstart = "<abbr class='dtstart' title='".date_iso($date_debut)."'>";
1140 $dtend = "<abbr class='dtend' title='".date_iso($date_fin)."'>";
1141 $dtabbr = "</abbr>";
1142 }
1143
1144 $date_debut = strtotime($date_debut);
1145 $date_fin = strtotime($date_fin);
1146 $d = date("Y-m-d", $date_debut);
1147 $f = date("Y-m-d", $date_fin);
1148 $h = ($horaire==='oui' OR $horaire===true);
1149 $hd = _T('date_fmt_heures_minutes_court', array('h'=> date("H",$date_debut), 'm'=> date("i",$date_debut)));
1150 $hf = _T('date_fmt_heures_minutes_court', array('h'=> date("H",$date_fin), 'm'=> date("i",$date_fin)));
1151
1152 if ($d==$f)
1153 { // meme jour
1154 $nomjour = nom_jour($d,$abbr);
1155 $s = $affdate($d);
1156 $s = _T('date_fmt_jour',array('nomjour'=>$nomjour,'jour' => $s));
1157 if ($h){
1158 if ($hd==$hf){
1159 // Lundi 20 fevrier a 18h25
1160 $s = spip_ucfirst(_T('date_fmt_jour_heure',array('jour'=>$s,'heure'=>$hd)));
1161 $s = "$dtstart$s$dtabbr";
1162 }else{
1163 // Le <abbr...>lundi 20 fevrier de 18h00</abbr> a <abbr...>20h00</abbr>
1164 if($dtabbr && $dtstart && $dtend)
1165 $s = spip_ucfirst(_T('date_fmt_jour_heure_debut_fin_abbr',array('jour'=>$s,'heure_debut'=>$hd,'heure_fin'=>$hf,'dtstart'=>$dtstart,'dtend'=>$dtend,'dtabbr'=>$dtabbr)));
1166 // Le lundi 20 fevrier de 18h00 a 20h00
1167 else
1168 $s = spip_ucfirst(_T('date_fmt_jour_heure_debut_fin',array('jour'=>$s,'heure_debut'=>$hd,'heure_fin'=>$hf)));
1169 }
1170 }else{
1171 if($dtabbr && $dtstart)
1172 $s = $dtstart.spip_ucfirst($s).$dabbr;
1173 else
1174 $s = spip_ucfirst($s);
1175 }
1176 }
1177 else if ((date("Y-m",$date_debut))==date("Y-m",$date_fin))
1178 { // meme annee et mois, jours differents
1179 if(!$h)
1180 $date_debut = jour($d);
1181 else
1182 $date_debut = $affdate($d);
1183 $date_fin = $affdate($f);
1184 if($jour){
1185 $nomjour_debut = nom_jour($d,$abbr);
1186 $date_debut = _T('date_fmt_jour',array('nomjour'=>$nomjour_debut,'jour' => $date_debut));
1187 $nomjour_fin = nom_jour($f,$abbr);
1188 $date_fin = _T('date_fmt_jour',array('nomjour'=>$nomjour_fin,'jour' => $date_fin));
1189 }
1190 if ($h){
1191 $date_debut = _T('date_fmt_jour_heure',array('jour'=>$date_debut,'heure'=>$hd));
1192 $date_fin = _T('date_fmt_jour_heure',array('jour'=>$date_fin,'heure'=>$hf));
1193 }
1194 $date_debut = $dtstart.$date_debut.$dtabbr;
1195 $date_fin = $dtend.$date_fin.$dtabbr;
1196
1197 $s = _T('date_fmt_periode',array('date_debut' => $date_debut,'date_fin'=>$date_fin));
1198 }
1199 else {
1200 $date_debut = affdate($d);
1201 $date_fin = affdate($f);
1202 if($jour){
1203 $nomjour_debut = nom_jour($d,$abbr);
1204 $date_debut = _T('date_fmt_jour_periode',array('nomjour'=>$nomjour_debut,'jour' => $date_debut));
1205 $nomjour_fin = nom_jour($f,$abbr);
1206 $date_fin = _T('date_fmt_jour_periode',array('nomjour'=>$nomjour_fin,'jour' => $date_fin));
1207 }
1208 if ($h){
1209 $date_debut = _T('date_fmt_jour_heure',array('jour'=>$date_debut,'heure'=>$hd));
1210 $date_fin = _T('date_fmt_jour_heure',array('jour'=>$date_fin,'heure'=>$hf));
1211 }
1212
1213 $date_debut = $dtstart.$date_debut.$dtabbr;
1214 $date_fin=$dtend.$date_fin.$dtabbr;
1215 $s = _T('date_fmt_periode',array('date_debut' => $date_debut,'date_fin'=>$date_fin));
1216
1217 }
1218 return $s;
1219 }
1220
1221 /**
1222 * Alignements en HTML (Old-style, preferer CSS)
1223 * Cette fonction ne cree pas de paragraphe
1224 *
1225 * http://doc.spip.org/@aligner
1226 *
1227 * @param $letexte
1228 * @param string $justif
1229 * @return string
1230 */
1231 function aligner($letexte, $justif='') {
1232 $letexte = trim($letexte);
1233 if (!strlen($letexte)) return '';
1234
1235 // Paragrapher rapidement
1236 $letexte = "<div style='text-align:$justif'>"
1237 . $letexte
1238 ."</div>";
1239
1240 return $letexte;
1241 }
1242 // http://doc.spip.org/@justifier
1243 function justifier($letexte) { return aligner($letexte,'justify');}
1244 // http://doc.spip.org/@aligner_droite
1245 function aligner_droite($letexte) { return aligner($letexte,'right');}
1246 // http://doc.spip.org/@aligner_gauche
1247 function aligner_gauche($letexte) {return aligner($letexte,'left');}
1248 // http://doc.spip.org/@centrer
1249 function centrer($letexte) {return aligner($letexte,'center');}
1250
1251 // http://doc.spip.org/@style_align
1252 function style_align($bof) {
1253 global $spip_lang_left;
1254 return "text-align: $spip_lang_left";
1255 }
1256
1257 //
1258 // Export iCal
1259 //
1260
1261 // http://doc.spip.org/@filtrer_ical
1262 function filtrer_ical($texte) {
1263 #include_spip('inc/charsets');
1264 $texte = html2unicode($texte);
1265 $texte = unicode2charset(charset2unicode($texte, $GLOBALS['meta']['charset'], 1), 'utf-8');
1266 $texte = preg_replace("/\n/", " ", $texte);
1267 $texte = preg_replace("/,/", "\,", $texte);
1268
1269 return $texte;
1270 }
1271
1272 // http://doc.spip.org/@date_ical
1273 function date_ical($date, $addminutes = 0) {
1274 list($heures, $minutes, $secondes) = recup_heure($date);
1275 list($annee, $mois, $jour) = recup_date($date);
1276 return date("Ymd\THis",
1277 mktime($heures, $minutes+$addminutes,$secondes,$mois,$jour,$annee));
1278 }
1279
1280 // date_iso retourne la date au format "RFC 3339" / "ISO 8601"
1281 // voir http://www.php.net/manual/fr/ref.datetime.php#datetime.constants
1282 // http://doc.spip.org/@date_iso
1283 function date_iso($date_heure) {
1284 list($annee, $mois, $jour) = recup_date($date_heure);
1285 list($heures, $minutes, $secondes) = recup_heure($date_heure);
1286 $time = @mktime($heures, $minutes, $secondes, $mois, $jour, $annee);
1287 return gmdate('Y-m-d\TH:i:s\Z', $time);
1288 }
1289
1290 // date_822 retourne la date au format "RFC 822"
1291 // utilise pour <pubdate> dans certains feeds RSS
1292 // http://doc.spip.org/@date_822
1293 function date_822($date_heure) {
1294 list($annee, $mois, $jour) = recup_date($date_heure);
1295 list($heures, $minutes, $secondes) = recup_heure($date_heure);
1296 $time = mktime($heures, $minutes, $secondes, $mois, $jour, $annee);
1297 return date('r', $time);
1298 }
1299
1300 // http://doc.spip.org/@date_anneemoisjour
1301 function date_anneemoisjour($d) {
1302 if (!$d) $d = date("Y-m-d");
1303 return substr($d, 0, 4) . substr($d, 5, 2) .substr($d, 8, 2);
1304 }
1305
1306 // http://doc.spip.org/@date_anneemois
1307 function date_anneemois($d) {
1308 if (!$d) $d = date("Y-m-d");
1309 return substr($d, 0, 4) . substr($d, 5, 2);
1310 }
1311
1312 // http://doc.spip.org/@date_debut_semaine
1313 function date_debut_semaine($annee, $mois, $jour) {
1314 $w_day = date("w", mktime(0,0,0,$mois, $jour, $annee));
1315 if ($w_day == 0) $w_day = 7; // Gaffe: le dimanche est zero
1316 $debut = $jour-$w_day+1;
1317 return date("Ymd", mktime(0,0,0,$mois,$debut,$annee));
1318 }
1319
1320 // http://doc.spip.org/@date_fin_semaine
1321 function date_fin_semaine($annee, $mois, $jour) {
1322 $w_day = date("w", mktime(0,0,0,$mois, $jour, $annee));
1323 if ($w_day == 0) $w_day = 7; // Gaffe: le dimanche est zero
1324 $debut = $jour-$w_day+1;
1325 return date("Ymd", mktime(0,0,0,$mois,$debut+6,$annee));
1326 }
1327
1328
1329 //
1330 // Recuperation de donnees dans le champ extra
1331 // Ce filtre n'a de sens qu'avec la balise #EXTRA
1332 //
1333 // http://doc.spip.org/@extra
1334 function extra($letexte, $champ) {
1335 $champs = unserialize($letexte);
1336 return $champs[$champ];
1337 }
1338
1339 // postautobr : transforme les sauts de ligne en _
1340 // http://doc.spip.org/@post_autobr
1341 function post_autobr($texte, $delim="\n_ ") {
1342 if (!function_exists('echappe_html'))
1343 include_spip('inc/texte_mini');
1344 $texte = str_replace("\r\n", "\r", $texte);
1345 $texte = str_replace("\r", "\n", $texte);
1346
1347 if (preg_match(",\n+$,", $texte, $fin))
1348 $texte = substr($texte, 0, -strlen($fin = $fin[0]));
1349 else
1350 $fin = '';
1351
1352 $texte = echappe_html($texte, '', true);
1353
1354 // echapper les modeles
1355 if (strpos($texte,"<")!==false){
1356 include_spip('inc/lien');
1357 if (defined('_PREG_MODELE')){
1358 $preg_modeles = "@"._PREG_MODELE."@imsS";
1359 $texte = echappe_html($texte, '', true, $preg_modeles);
1360 }
1361 }
1362
1363 $debut = '';
1364 $suite = $texte;
1365 while ($t = strpos('-'.$suite, "\n", 1)) {
1366 $debut .= substr($suite, 0, $t-1);
1367 $suite = substr($suite, $t);
1368 $car = substr($suite, 0, 1);
1369 if (($car<>'-') AND ($car<>'_') AND ($car<>"\n") AND ($car<>"|") AND ($car<>"}")
1370 AND !preg_match(',^\s*(\n|</?(quote|div|dl|dt|dd)|$),S',($suite))
1371 AND !preg_match(',</?(quote|div|dl|dt|dd)> *$,iS', $debut)) {
1372 $debut .= $delim;
1373 } else
1374 $debut .= "\n";
1375 if (preg_match(",^\n+,", $suite, $regs)) {
1376 $debut.=$regs[0];
1377 $suite = substr($suite, strlen($regs[0]));
1378 }
1379 }
1380 $texte = $debut.$suite;
1381
1382 $texte = echappe_retour($texte);
1383 return $texte.$fin;
1384 }
1385
1386
1387 define('_EXTRAIRE_MULTI', "@<multi>(.*?)</multi>@sS");
1388
1389 // Extraire et transformer les blocs multi ; on indique la langue courante
1390 // pour ne pas mettre de span@lang=fr si on est deja en fr
1391 // http://doc.spip.org/@extraire_multi
1392 function extraire_multi($letexte, $lang=null, $echappe_span=false) {
1393 if (preg_match_all(_EXTRAIRE_MULTI, $letexte, $regs, PREG_SET_ORDER)) {
1394 if (!$lang) $lang = $GLOBALS['spip_lang'];
1395
1396 foreach ($regs as $reg) {
1397 // chercher la version de la langue courante
1398 $trads = extraire_trads($reg[1]);
1399 if ($l = approcher_langue($trads, $lang)) {
1400 $trad = $trads[$l];
1401 } else {
1402 include_spip('inc/texte');
1403 // langue absente, prendre la premiere dispo
1404 // mais typographier le texte selon les regles de celle-ci
1405 // Attention aux blocs multi sur plusieurs lignes
1406 $l = key($trads);
1407 $trad = $trads[$l];
1408 $typographie = charger_fonction(lang_typo($l), 'typographie');
1409 $trad = $typographie($trad);
1410 include_spip('inc/texte');
1411 // Tester si on echappe en span ou en div
1412 // il ne faut pas echapper en div si propre produit un seul paragraphe
1413 $trad_propre = preg_replace(",(^<p[^>]*>|</p>$),Uims","",propre($trad));
1414 $mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $trad_propre) ? 'div' : 'span';
1415 $trad = code_echappement($trad, 'multi', false, $mode);
1416 $trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1417 if (lang_dir($l) !== lang_dir($lang))
1418 $trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1419 if (!$echappe_span)
1420 $trad = echappe_retour($trad, 'multi');
1421 }
1422 $letexte = str_replace($reg[0], $trad, $letexte);
1423 }
1424 }
1425
1426 return $letexte;
1427 }
1428
1429 // convertit le contenu d'une balise multi en un tableau
1430 // http://doc.spip.org/@extraire_trad
1431 function extraire_trads($bloc) {
1432 $lang = '';
1433 // ce reg fait planter l'analyse multi s'il y a de l'{italique} dans le champ
1434 // while (preg_match("/^(.*?)[{\[]([a-z_]+)[}\]]/siS", $bloc, $regs)) {
1435 while (preg_match("/^(.*?)[\[]([a-z_]+)[\]]/siS", $bloc, $regs)) {
1436 $texte = trim($regs[1]);
1437 if ($texte OR $lang)
1438 $trads[$lang] = $texte;
1439 $bloc = substr($bloc, strlen($regs[0]));
1440 $lang = $regs[2];
1441 }
1442 $trads[$lang] = $bloc;
1443
1444 return $trads;
1445 }
1446
1447 //
1448 // Ce filtre retourne la donnee si c'est la premiere fois qu'il la voit ;
1449 // possibilite de gerer differentes "familles" de donnees |unique{famille}
1450 # |unique{famille,1} affiche le nombre d'elements affiches (preferer toutefois #TOTAL_UNIQUE)
1451 # ameliorations possibles :
1452 # 1) si la donnee est grosse, mettre son md5 comme cle
1453 # 2) purger $mem quand on change de squelette (sinon bug inclusions)
1454 //
1455 // http://www.spip.net/@unique
1456 // http://doc.spip.org/@unique
1457 function unique($donnee, $famille='', $cpt = false) {
1458 static $mem = array();
1459 // permettre de vider la pile et de la restaurer
1460 // pour le calcul de introduction...
1461 if ($famille=='_spip_raz_'){
1462 $tmp = $mem;
1463 $mem = array();
1464 return $tmp;
1465 } elseif ($famille=='_spip_set_'){
1466 $mem = $donnee;
1467 return;
1468 }
1469 // eviter une notice
1470 if (!isset($mem[$famille])) {
1471 $mem[$famille] = array();
1472 }
1473 if ($cpt) {
1474 return count($mem[$famille]);
1475 }
1476 // eviter une notice
1477 if (!isset($mem[$famille][$donnee])) {
1478 $mem[$famille][$donnee] = 0;
1479 }
1480 if (!($mem[$famille][$donnee]++)) {
1481 return $donnee;
1482 }
1483 }
1484
1485 //
1486 // Filtre |alterner
1487 //
1488 // Exemple [(#COMPTEUR_BOUCLE|alterner{'bleu','vert','rouge'})]
1489 //
1490 // http://doc.spip.org/@alterner
1491 function alterner($i) {
1492 // recuperer les arguments (attention fonctions un peu space)
1493 $num = func_num_args();
1494 $args = func_get_args();
1495
1496 if($num == 2 && is_array($args[1])) {
1497 $args = $args[1];
1498 array_unshift($args,'');
1499 $num = count($args);
1500 }
1501
1502 // renvoyer le i-ieme argument, modulo le nombre d'arguments
1503 return $args[(intval($i)-1)%($num-1)+1];
1504 }
1505
1506 // recuperer un attribut d'une balise html
1507 // ($complet demande de retourner $r)
1508 // la regexp est mortelle : cf. tests/filtres/extraire_attribut.php
1509 // Si on a passe un tableau de balises, renvoyer un tableau de resultats
1510 // (dans ce cas l'option $complet n'est pas disponible)
1511 // http://doc.spip.org/@extraire_attribut
1512 function extraire_attribut($balise, $attribut, $complet = false) {
1513 if (is_array($balise)) {
1514 array_walk($balise,
1515 create_function('&$a,$key,$t',
1516 '$a = extraire_attribut($a,$t);'
1517 ),
1518 $attribut);
1519 return $balise;
1520 }
1521 if (preg_match(
1522 ',(^.*?<(?:(?>\s*)(?>[\w:.-]+)(?>(?:=(?:"[^"]*"|\'[^\']*\'|[^\'"]\S*))?))*?)(\s+'
1523 .$attribut
1524 .'(?:=\s*("[^"]*"|\'[^\']*\'|[^\'"]\S*))?)()([^>]*>.*),isS',
1525
1526 $balise, $r)) {
1527 if ($r[3][0] == '"' || $r[3][0] == "'") {
1528 $r[4] = substr($r[3], 1, -1);
1529 $r[3] = $r[3][0];
1530 } elseif ($r[3]!=='') {
1531 $r[4] = $r[3];
1532 $r[3] = '';
1533 } else {
1534 $r[4] = trim($r[2]);
1535 }
1536 $att = filtrer_entites(str_replace("&#39;", "'", $r[4]));
1537 }
1538 else
1539 $att = NULL;
1540
1541 if ($complet)
1542 return array($att, $r);
1543 else
1544 return $att;
1545 }
1546
1547 /**
1548 * modifier (ou inserer) un attribut html dans une balise
1549 *
1550 * http://doc.spip.org/@inserer_attribut
1551 *
1552 * @param string $balise
1553 * @param string $attribut
1554 * @param string $val
1555 * @param bool $proteger
1556 * @param bool $vider
1557 * @return string
1558 */
1559 function inserer_attribut($balise, $attribut, $val, $proteger=true, $vider=false) {
1560 // preparer l'attribut
1561 // supprimer les &nbsp; etc mais pas les balises html
1562 // qui ont un sens dans un attribut value d'un input
1563 if ($proteger) $val = attribut_html($val,false);
1564
1565 // echapper les ' pour eviter tout bug
1566 $val = str_replace("'", "&#039;", $val);
1567 if ($vider AND strlen($val)==0)
1568 $insert = '';
1569 else
1570 $insert = " $attribut='$val'";
1571
1572 list($old, $r) = extraire_attribut($balise, $attribut, true);
1573
1574 if ($old !== NULL) {
1575 // Remplacer l'ancien attribut du meme nom
1576 $balise = $r[1].$insert.$r[5];
1577 }
1578 else {
1579 // preferer une balise " />" (comme <img />)
1580 if (preg_match(',/>,', $balise))
1581 $balise = preg_replace(",\s?/>,S", $insert." />", $balise, 1);
1582 // sinon une balise <a ...> ... </a>
1583 else
1584 $balise = preg_replace(",\s?>,S", $insert.">", $balise, 1);
1585 }
1586
1587 return $balise;
1588 }
1589
1590 // http://doc.spip.org/@vider_attribut
1591 function vider_attribut ($balise, $attribut) {
1592 return inserer_attribut($balise, $attribut, '', false, true);
1593 }
1594
1595
1596 /**
1597 * Un filtre pour determiner le nom du satut des inscrits
1598 *
1599 * @param void|int $id
1600 * @param string $mode
1601 * @return string
1602 */
1603 function tester_config($id, $mode='') {
1604 include_spip('action/inscrire_auteur');
1605 return tester_statut_inscription($mode, $id);
1606 }
1607
1608 //
1609 // Quelques fonctions de calcul arithmetique
1610 //
1611 // http://doc.spip.org/@plus
1612 function plus($a,$b) {
1613 return $a+$b;
1614 }
1615 // http://doc.spip.org/@moins
1616 function moins($a,$b) {
1617 return $a-$b;
1618 }
1619 // http://doc.spip.org/@mult
1620 function mult($a,$b) {
1621 return $a*$b;
1622 }
1623 // http://doc.spip.org/@div
1624 function div($a,$b) {
1625 return $b?$a/$b:0;
1626 }
1627 // http://doc.spip.org/@modulo
1628 function modulo($nb, $mod, $add=0) {
1629 return ($mod?$nb%$mod:0)+$add;
1630 }
1631
1632
1633 // Verifier la conformite d'une ou plusieurs adresses email
1634 // retourne false ou la normalisation de la derniere adresse donnee
1635 // http://doc.spip.org/@email_valide
1636 function email_valide($adresses) {
1637 // eviter d'injecter n'importe quoi dans preg_match
1638 if (!is_string($adresses))
1639 return false;
1640
1641 // Si c'est un spammeur autant arreter tout de suite
1642 if (preg_match(",[\n\r].*(MIME|multipart|Content-),i", $adresses)) {
1643 spip_log("Tentative d'injection de mail : $adresses");
1644 return false;
1645 }
1646
1647 foreach (explode(',', $adresses) as $v) {
1648 // nettoyer certains formats
1649 // "Marie Toto <Marie@toto.com>"
1650 $adresse = trim(preg_replace(",^[^<>\"]*<([^<>\"]+)>$,i", "\\1", $v));
1651 // RFC 822
1652 if (!preg_match('#^[^()<>@,;:\\"/[:space:]]+(@([-_0-9a-z]+\.)*[-_0-9a-z]+)$#i', $adresse))
1653 return false;
1654 }
1655 return $adresse;
1656 }
1657
1658 // http://doc.spip.org/@afficher_enclosures
1659 function afficher_enclosures($tags) {
1660 $s = array();
1661 foreach (extraire_balises($tags, 'a') as $tag) {
1662 if (extraire_attribut($tag, 'rel') == 'enclosure'
1663 AND $t = extraire_attribut($tag, 'href')) {
1664 $s[] = preg_replace(',>[^<]+</a>,S',
1665 '>'
1666 .http_img_pack('attachment-16.png', $t,
1667 'title="'.attribut_html($t).'"')
1668 .'</a>', $tag);
1669 }
1670 }
1671 return join('&nbsp;', $s);
1672 }
1673 // http://doc.spip.org/@afficher_tags
1674 function afficher_tags($tags, $rels='tag,directory') {
1675 $s = array();
1676 foreach (extraire_balises($tags, 'a') as $tag) {
1677 $rel = extraire_attribut($tag, 'rel');
1678 if (strstr(",$rels,", ",$rel,"))
1679 $s[] = $tag;
1680 }
1681 return join(', ', $s);
1682 }
1683
1684 // Passe un <enclosure url="fichier" length="5588242" type="audio/mpeg"/>
1685 // au format microformat <a rel="enclosure" href="fichier" ...>fichier</a>
1686 // attention length="zz" devient title="zz", pour rester conforme
1687 // http://doc.spip.org/@enclosure2microformat
1688 function enclosure2microformat($e) {
1689 if (!$url = filtrer_entites(extraire_attribut($e, 'url')))
1690 $url = filtrer_entites(extraire_attribut($e, 'href'));
1691 $type = extraire_attribut($e, 'type');
1692 if (!$length = extraire_attribut($e, 'length')) {
1693 # <media:content : longeur dans fileSize. On tente.
1694 $length = extraire_attribut($e, 'fileSize');
1695 }
1696 $fichier = basename($url);
1697 return '<a rel="enclosure"'
1698 . ($url? ' href="'.htmlspecialchars($url).'"' : '')
1699 . ($type? ' type="'.htmlspecialchars($type).'"' : '')
1700 . ($length? ' title="'.htmlspecialchars($length).'"' : '')
1701 . '>'.$fichier.'</a>';
1702 }
1703 // La fonction inverse
1704 // http://doc.spip.org/@microformat2enclosure
1705 function microformat2enclosure($tags) {
1706 $enclosures = array();
1707 foreach (extraire_balises($tags, 'a') as $e)
1708 if (extraire_attribut($e, 'rel') == 'enclosure') {
1709 $url = filtrer_entites(extraire_attribut($e, 'href'));
1710 $type = extraire_attribut($e, 'type');
1711 if (!$length = intval(extraire_attribut($e, 'title')))
1712 $length = intval(extraire_attribut($e, 'length')); # vieux data
1713 $fichier = basename($url);
1714 $enclosures[] = '<enclosure'
1715 . ($url? ' url="'.htmlspecialchars($url).'"' : '')
1716 . ($type? ' type="'.htmlspecialchars($type).'"' : '')
1717 . ($length? ' length="'.$length.'"' : '')
1718 . ' />';
1719 }
1720 return join("\n", $enclosures);
1721 }
1722 // Creer les elements ATOM <dc:subject> a partir des tags
1723 // http://doc.spip.org/@tags2dcsubject
1724 function tags2dcsubject($tags) {
1725 $subjects = '';
1726 foreach (extraire_balises($tags, 'a') as $e) {
1727 if (extraire_attribut($e, rel) == 'tag') {
1728 $subjects .= '<dc:subject>'
1729 . texte_backend(textebrut($e))
1730 . '</dc:subject>'."\n";
1731 }
1732 }
1733 return $subjects;
1734 }
1735
1736 // retourne la premiere balise du type demande
1737 // ex: [(#DESCRIPTIF|extraire_balise{img})]
1738 // Si on a passe un tableau de textes, renvoyer un tableau de resultats
1739 // http://doc.spip.org/@extraire_balise
1740 function extraire_balise($texte, $tag='a') {
1741 if (is_array($texte)) {
1742 array_walk($texte,
1743 create_function('&$a,$key,$t', '$a = extraire_balise($a,$t);'),
1744 $tag);
1745 return $texte;
1746 }
1747
1748 if (preg_match(
1749 ",<$tag\b[^>]*(/>|>.*</$tag\b[^>]*>|>),UimsS",
1750 $texte, $regs))
1751 return $regs[0];
1752 }
1753
1754 // extraire toutes les balises du type demande, sous forme de tableau
1755 // Si on a passe un tableau de textes, renvoyer un tableau de resultats
1756 // http://doc.spip.org/@extraire_balises
1757 function extraire_balises($texte, $tag='a') {
1758 if (is_array($texte)) {
1759 array_walk($texte,
1760 create_function('&$a,$key,$t', '$a = extraire_balises($a,$t);'),
1761 $tag);
1762 return $texte;
1763 }
1764
1765 if (preg_match_all(
1766 ",<${tag}\b[^>]*(/>|>.*</${tag}\b[^>]*>|>),UimsS",
1767 $texte, $regs, PREG_PATTERN_ORDER))
1768 return $regs[0];
1769 else
1770 return array();
1771 }
1772
1773 // comme in_array mais renvoie son 3e arg si le 2er arg n'est pas un tableau
1774 // prend ' ' comme representant de vrai et '' de faux
1775
1776 // http://doc.spip.org/@in_any
1777 function in_any($val, $vals, $def='') {
1778 if (!is_array($vals) AND $v=unserialize($vals)) $vals = $v;
1779 return (!is_array($vals) ? $def : (in_array($val, $vals) ? ' ' : ''));
1780 }
1781
1782 // valeur_numerique("3*2") => 6
1783 // n'accepte que les *, + et - (a ameliorer si on l'utilise vraiment)
1784 // http://doc.spip.org/@valeur_numerique
1785 function valeur_numerique($expr) {
1786 $a = 0;
1787 if (preg_match(',^[0-9]+(\s*[+*-]\s*[0-9]+)*$,S', trim($expr)))
1788 eval("\$a = $expr;");
1789 return intval($a);
1790 }
1791
1792 // http://doc.spip.org/@regledetrois
1793 function regledetrois($a,$b,$c)
1794 {
1795 return round($a*$b/$c);
1796 }
1797
1798 // Fournit la suite de Input-Hidden correspondant aux parametres de
1799 // l'URL donnee en argument, compatible avec les types_urls depuis [14447].
1800 // cf. tests/filtres/form_hidden.html
1801 // http://doc.spip.org/@form_hidden
1802 function form_hidden($action) {
1803
1804 $contexte = array();
1805 include_spip('inc/urls');
1806 if ($p = urls_decoder_url($action, '')
1807 AND reset($p)) {
1808 $fond = array_shift($p);
1809 if ($fond!='404'){
1810 $contexte = array_shift($p);
1811 $contexte['page'] = $fond;
1812 $action = preg_replace('/([?]'.preg_quote($fond).'[^&=]*[0-9]+)(&|$)/', '?&', $action);
1813 }
1814 }
1815 // defaire ce qu'a injecte urls_decoder_url : a revoir en modifiant la signature de urls_decoder_url
1816 if (defined('_DEFINIR_CONTEXTE_TYPE') AND _DEFINIR_CONTEXTE_TYPE)
1817 unset($contexte['type']);
1818 if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') AND _DEFINIR_CONTEXTE_TYPE_PAGE)
1819 unset($contexte['type-page']);
1820
1821 // on va remplir un tableau de valeurs en prenant bien soin de ne pas
1822 // ecraser les elements de la forme mots[]=1&mots[]=2
1823 $values = array();
1824
1825 // d'abord avec celles de l'url
1826 if (false !== ($p = strpos($action, '?'))) {
1827 foreach(preg_split('/&(amp;)?/S',substr($action,$p+1)) as $c){
1828 list($var,$val) = explode('=', $c, 2);
1829 if ($var) {
1830 $val = rawurldecode($val);
1831 $var = rawurldecode($var); // decoder les [] eventuels
1832 if (preg_match(',\[\]$,S', $var))
1833 $values[] = array($var, $val);
1834 else if (!isset($values[$var]))
1835 $values[$var] = array($var, $val);
1836 }
1837 }
1838 }
1839
1840 // ensuite avec celles du contexte, sans doublonner !
1841 foreach($contexte as $var=>$val)
1842 if (preg_match(',\[\]$,S', $var))
1843 $values[] = array($var, $val);
1844 else if (!isset($values[$var]))
1845 $values[$var] = array($var, $val);
1846
1847 // puis on rassemble le tout
1848 $hidden = array();
1849 foreach($values as $value) {
1850 list($var,$val) = $value;
1851 $hidden[] = '<input name="'
1852 . entites_html($var)
1853 .'"'
1854 . (is_null($val)
1855 ? ''
1856 : ' value="'.entites_html($val).'"'
1857 )
1858 . ' type="hidden"'."\n/>";
1859 }
1860 return join("", $hidden);
1861 }
1862
1863 // http://doc.spip.org/@filtre_bornes_pagination_dist
1864 function filtre_bornes_pagination_dist($courante, $nombre, $max = 10) {
1865 if($max<=0 OR $max>=$nombre)
1866 return array(1, $nombre);
1867
1868 $premiere = max(1, $courante-floor(($max-1)/2));
1869 $derniere = min($nombre, $premiere+$max-2);
1870 $premiere = $derniere == $nombre ? $derniere-$max+1 : $premiere;
1871 return array($premiere, $derniere);
1872 }
1873
1874
1875 // Ces trois fonctions permettent de simuler les filtres |reset et |end
1876 // pour extraire la premiere ou la derniere valeur d'un tableau ; utile
1877 // pour la pagination (mais peut-etre a refaire plus simplement)
1878 // http://doc.spip.org/@filtre_valeur_tableau
1879 function filtre_valeur_tableau($array, $index) {
1880 if (!is_array($array)
1881 OR !isset($array[$index]))
1882 return null;
1883 return $array[$index];
1884 }
1885 // http://doc.spip.org/@filtre_reset
1886 function filtre_reset($array) {
1887 return !is_array($array) ? null : reset($array);
1888 }
1889 // http://doc.spip.org/@filtre_end
1890 function filtre_end($array) {
1891 return !is_array($array) ? null : end($array);
1892 }
1893
1894 // http://doc.spip.org/@filtre_push
1895 function filtre_push($array, $val) {
1896 if($array == '' OR !array_push($array, $val)) return '';
1897 return $array;
1898 }
1899
1900 // http://doc.spip.org/@filtre_find
1901 function filtre_find($array, $val) {
1902 return (is_array($array) AND in_array($val, $array));
1903 }
1904
1905
1906 //
1907 // fonction standard de calcul de la balise #PAGINATION
1908 // on peut la surcharger en definissant filtre_pagination dans mes_fonctions
1909 //
1910
1911 // http://doc.spip.org/@filtre_pagination_dist
1912 function filtre_pagination_dist($total, $nom, $position, $pas, $liste = true, $modele='', $connect='', $env=array()) {
1913 static $ancres = array();
1914 if ($pas<1) return '';
1915 $ancre = 'pagination'.$nom; // #pagination_articles
1916 $debut = 'debut'.$nom; // 'debut_articles'
1917
1918 // n'afficher l'ancre qu'une fois
1919 if (!isset($ancres[$ancre]))
1920 $bloc_ancre = $ancres[$ancre] = "<a name='".$ancre."' id='".$ancre."'></a>";
1921 else $bloc_ancre = '';
1922 // liste = false : on ne veut que l'ancre
1923 if (!$liste)
1924 return $ancres[$ancre];
1925
1926 $pagination = array(
1927 'debut' => $debut,
1928 'url' => parametre_url(self(),'fragment',''), // nettoyer l'id ahah eventuel
1929 'total' => $total,
1930 'position' => intval($position),
1931 'pas' => $pas,
1932 'nombre_pages' => floor(($total-1)/$pas)+1,
1933 'page_courante' => floor(intval($position)/$pas)+1,
1934 'ancre' => $ancre,
1935 'bloc_ancre' => $bloc_ancre
1936 );
1937 if (is_array($env))
1938 $pagination = array_merge($env,$pagination);
1939
1940 // Pas de pagination
1941 if ($pagination['nombre_pages']<=1)
1942 return '';
1943
1944 if ($modele) $modele = '_'.$modele;
1945
1946 return recuperer_fond("modeles/pagination$modele", $pagination, array('trim'=>true), $connect);
1947 }
1948
1949 // passer les url relatives a la css d'origine en url absolues
1950 // http://doc.spip.org/@urls_absolues_css
1951 function urls_absolues_css($contenu, $source) {
1952 $path = suivre_lien(url_absolue($source),'./');
1953
1954 return preg_replace_callback(
1955 ",url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims",
1956 create_function('$x',
1957 'return "url(\"".suivre_lien("'.$path.'",$x[1])."\")";'
1958 ), $contenu);
1959 }
1960
1961 // recuperere le chemin d'une css existante et :
1962 // 1. regarde si une css inversee droite-gauche existe dans le meme repertoire
1963 // 2. sinon la cree (ou la recree) dans _DIR_VAR/cache_css/
1964 // SI on lui donne a manger une feuille nommee _rtl.css il va faire l'inverse
1965 // http://doc.spip.org/@direction_css
1966 function direction_css ($css, $voulue='') {
1967 if (!preg_match(',(_rtl)?\.css$,i', $css, $r)) return $css;
1968
1969 // si on a precise le sens voulu en argument, le prendre en compte
1970 if ($voulue = strtolower($voulue)) {
1971 if ($voulue != 'rtl' AND $voulue != 'ltr')
1972 $voulue = lang_dir($voulue);
1973 }
1974 else
1975 $voulue = lang_dir();
1976
1977 $r = count($r) > 1;
1978 $right = $r ? 'left' : 'right'; // 'right' de la css lue en entree
1979 $dir = $r ? 'rtl' : 'ltr';
1980 $ndir = $r ? 'ltr' : 'rtl';
1981
1982 if ($voulue == $dir)
1983 return $css;
1984
1985 if (
1986 // url absolue
1987 preg_match(",^http:,i",$css)
1988 // ou qui contient un ?
1989 OR (($p=strpos($css,'?'))!==FALSE)) {
1990 $distant = true;
1991 $cssf = parse_url($css);
1992 $cssf = $cssf['path'].($cssf['query']?"?".$cssf['query']:"");
1993 $cssf = preg_replace(',[?:&=],', "_", $cssf);
1994 }
1995 else {
1996 $distant = false;
1997 $cssf = $css;
1998 // 1. regarder d'abord si un fichier avec la bonne direction n'est pas aussi
1999 //propose (rien a faire dans ce cas)
2000 $f = preg_replace(',(_rtl)?\.css$,i', '_'.$ndir.'.css', $css);
2001 if (@file_exists($f))
2002 return $f;
2003 }
2004
2005 // 2.
2006 $dir_var = sous_repertoire (_DIR_VAR, 'cache-css');
2007 $f = $dir_var
2008 . preg_replace(',.*/(.*?)(_rtl)?\.css,', '\1', $cssf)
2009 . '.' . substr(md5($cssf), 0,4) . '_' . $ndir . '.css';
2010
2011 // la css peut etre distante (url absolue !)
2012 if ($distant){
2013 include_spip('inc/distant');
2014 $contenu = recuperer_page($css);
2015 if (!$contenu) return $css;
2016 }
2017 else {
2018 if ((@filemtime($f) > @filemtime($css))
2019 AND (_VAR_MODE != 'recalcul'))
2020 return $f;
2021 if (!lire_fichier($css, $contenu))
2022 return $css;
2023 }
2024
2025 $contenu = str_replace(
2026 array('right', 'left', '@@@@L E F T@@@@'),
2027 array('@@@@L E F T@@@@', 'right', 'left'),
2028 $contenu);
2029
2030 // reperer les @import auxquels il faut propager le direction_css
2031 preg_match_all(",\@import\s*url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims",$contenu,$regs);
2032 $src = array();$src_direction_css = array();$src_faux_abs=array();
2033 $d = dirname($css);
2034 foreach($regs[1] as $k=>$import_css){
2035 $css_direction = direction_css("$d/$import_css",$voulue);
2036 // si la css_direction est dans le meme path que la css d'origine, on tronque le path, elle sera passee en absolue
2037 if (substr($css_direction,0,strlen($d)+1)=="$d/") $css_direction = substr($css_direction,strlen($d)+1);
2038 // si la css_direction commence par $dir_var on la fait passer pour une absolue
2039 elseif (substr($css_direction,0,strlen($dir_var))==$dir_var) {
2040 $css_direction = substr($css_direction,strlen($dir_var));
2041 $src_faux_abs["/@@@@@@/".$css_direction] = $css_direction;
2042 $css_direction = "/@@@@@@/".$css_direction;
2043 }
2044 $src[] = $regs[0][$k];
2045 $src_direction_css[] = str_replace($import_css,$css_direction,$regs[0][$k]);
2046 }
2047 $contenu = str_replace($src,$src_direction_css,$contenu);
2048
2049 $contenu = urls_absolues_css($contenu, $css);
2050
2051 // virer les fausses url absolues que l'on a mis dans les import
2052 if (count($src_faux_abs))
2053 $contenu = str_replace(array_keys($src_faux_abs),$src_faux_abs,$contenu);
2054
2055 if (!ecrire_fichier($f, $contenu))
2056 return $css;
2057
2058 return $f;
2059 }
2060
2061 // recuperere le chemin d'une css existante et :
2062 // cree (ou recree) dans _DIR_VAR/cache_css/ une css dont les url relatives sont passees en url absolues
2063 // http://doc.spip.org/@url_absolue_css
2064 function url_absolue_css ($css) {
2065 if (!preg_match(',\.css$,i', $css, $r)) return $css;
2066
2067 $url_absolue_css = url_absolue($css);
2068
2069 $f = basename($css,'.css');
2070 $f = sous_repertoire (_DIR_VAR, 'cache-css')
2071 . preg_replace(",(.*?)(_rtl|_ltr)?$,","\\1-urlabs-" . substr(md5("$css-urlabs"), 0,4) . "\\2",$f)
2072 . '.css';
2073
2074 if ((@filemtime($f) > @filemtime($css))
2075 AND (_VAR_MODE != 'recalcul'))
2076 return $f;
2077
2078 if ($url_absolue_css==$css){
2079 if (strncmp($GLOBALS['meta']['adresse_site'],$css,$l=strlen($GLOBALS['meta']['adresse_site']))!=0
2080 OR !lire_fichier(_DIR_RACINE . substr($css,$l), $contenu)){
2081 include_spip('inc/distant');
2082 if (!$contenu = recuperer_page($css))
2083 return $css;
2084 }
2085 }
2086 elseif (!lire_fichier($css, $contenu))
2087 return $css;
2088
2089 // passer les url relatives a la css d'origine en url absolues
2090 $contenu = urls_absolues_css($contenu, $css);
2091
2092 // ecrire la css
2093 if (!ecrire_fichier($f, $contenu))
2094 return $css;
2095
2096 return $f;
2097 }
2098
2099
2100
2101 /**
2102 * Le filtre table_valeur
2103 * permet de recuperer la valeur d'une cle donnee
2104 * dans un tableau (ou un objet).
2105 *
2106 * @param mixed $table
2107 * Tableau ou objet
2108 * (ou chaine serialisee de tableau, ce qui permet d'enchainer le filtre)
2109 *
2110 * @param string $cle
2111 * Cle du tableau (ou parametre public de l'objet)
2112 * Cette cle peut contenir des caracteres / pour selectionner
2113 * des sous elements dans le tableau, tel que "sous/element/ici"
2114 * pour obtenir la valeur de $tableau['sous']['element']['ici']
2115 *
2116 * @param mixed $defaut
2117 * Valeur par defaut retournee si la cle demandee n'existe pas
2118 *
2119 * @return mixed Valeur trouvee ou valeur par defaut.
2120 **/
2121 function table_valeur($table, $cle, $defaut='') {
2122 foreach (explode('/', $cle) as $k) {
2123
2124 $table = is_string($table) ? @unserialize($table) : $table;
2125
2126 if (is_object($table)) {
2127 $table = (($k !== "") and isset($table->$k)) ? $table->$k : $defaut;
2128 } elseif (is_array($table)) {
2129 $table = isset($table[$k]) ? $table[$k] : $defaut;
2130 } else {
2131 $table = $defaut;
2132 }
2133 }
2134 return $table;
2135 }
2136
2137 // filtre match pour faire des tests avec expression reguliere
2138 // [(#TEXTE|match{^ceci$,Uims})]
2139 // retourne le fragment de chaine qui "matche"
2140 // il est possible de passer en 3eme argument optionnel le numero de parenthese capturante
2141 // accepte egalement la syntaxe #TRUC|match{truc(...)$,1} ou le modificateur n'est pas passe en second argument
2142 // http://doc.spip.org/@match
2143 function match($texte, $expression, $modif="UimsS",$capte=0) {
2144 if (intval($modif) AND $capte==0){
2145 $capte = $modif;
2146 $modif = "UimsS";
2147 }
2148 $expression=str_replace("\/","/",$expression);
2149 $expression=str_replace("/","\/",$expression);
2150
2151 if (preg_match('/' . $expression . '/' . $modif,$texte, $r)) {
2152 if (isset($r[$capte]))
2153 return $r[$capte];
2154 else
2155 return true;
2156 }
2157 return false;
2158 }
2159
2160 // filtre replace pour faire des operations avec expression reguliere
2161 // [(#TEXTE|replace{^ceci$,cela,UimsS})]
2162 // http://doc.spip.org/@replace
2163 function replace($texte, $expression, $replace='', $modif="UimsS") {
2164 $expression=str_replace("\/","/", $expression);
2165 $expression=str_replace("/","\/",$expression);
2166 return preg_replace('/' . $expression . '/' . $modif, $replace, $texte);
2167 }
2168
2169
2170 // cherche les documents numerotes dans un texte traite par propre()
2171 // et affecte les doublons['documents']
2172 // http://doc.spip.org/@traiter_doublons_documents
2173 // http://doc.spip.org/@traiter_doublons_documents
2174 function traiter_doublons_documents(&$doublons, $letexte) {
2175
2176 // Verifier dans le texte & les notes (pas beau, helas)
2177 $t = $letexte.$GLOBALS['les_notes'];
2178
2179 if (strstr($t, 'spip_document_') // evite le preg_match_all si inutile
2180 AND preg_match_all(
2181 ',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
2182 $t, $matches, PREG_PATTERN_ORDER))
2183 $doublons['documents'] .= "," . join(',', $matches[1]);
2184
2185 return $letexte;
2186 }
2187
2188 // filtre vide qui ne renvoie rien
2189 // http://doc.spip.org/@vide
2190 function vide($texte){
2191 return "";
2192 }
2193
2194 //
2195 // Filtres pour le modele/emb (embed document)
2196 //
2197
2198 // A partir d'un #ENV, retourne des <param ...>
2199 // http://doc.spip.org/@env_to_params
2200 function env_to_params ($texte, $ignore_params=array()) {
2201 $ignore_params = array_merge (
2202 array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
2203 $ignore_params);
2204 $tableau = unserialize($texte);
2205 $texte = "";
2206 foreach ($tableau as $i => $j)
2207 if (is_string($j) AND !in_array($i,$ignore_params))
2208 $texte .= "<param name='".$i."'\n\tvalue='".$j."' />";
2209 return $texte;
2210 }
2211 // A partir d'un #ENV, retourne des attributs
2212 // http://doc.spip.org/@env_to_attributs
2213 function env_to_attributs ($texte, $ignore_params=array()) {
2214 $ignore_params = array_merge (
2215 array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
2216 $ignore_params);
2217 $tableau = unserialize($texte);
2218 $texte = "";
2219 foreach ($tableau as $i => $j)
2220 if (is_string($j) AND !in_array($i,$ignore_params))
2221 $texte .= $i."='".$j."' ";
2222 return $texte;
2223 }
2224
2225 // Concatener des chaines
2226 // #TEXTE|concat{texte1,texte2,...}
2227 // http://doc.spip.org/@concat
2228 function concat(){
2229 $args = func_get_args();
2230 return join('', $args);
2231 }
2232
2233
2234 // http://doc.spip.org/@charge_scripts
2235 function charge_scripts($scripts) {
2236 $flux = "";
2237 $args = is_array($scripts)?$scripts:explode("|",$scripts);
2238 foreach($args as $script) {
2239 if(preg_match(",^\w+$,",$script)) {
2240 $path = find_in_path("javascript/$script.js");
2241 if($path) $flux .= spip_file_get_contents($path);
2242 }
2243 }
2244 return $flux;
2245 }
2246
2247
2248
2249 /**
2250 * produit une balise img avec un champ alt d'office si vide
2251 * attention le htmlentities et la traduction doivent etre appliques avant.
2252 *
2253 * http://doc.spip.org/@http_img_pack
2254 *
2255 * @param $img
2256 * @param $alt
2257 * @param string $atts
2258 * @param string $title
2259 * @param array $options
2260 * chemin_image : utiliser chemin_image sur $img fourni, ou non (oui par dafaut)
2261 * utiliser_suffixe_size : utiliser ou non le suffixe de taille dans le nom de fichier de l'image
2262 * sous forme -xx.png (pour les icones essentiellement) (oui par defaut)
2263 * @return string
2264 */
2265 function http_img_pack($img, $alt, $atts='', $title='', $options = array()) {
2266 if (!isset($options['chemin_image']) OR $options['chemin_image']==true)
2267 $img = chemin_image($img);
2268 if (stripos($atts, 'width')===false){
2269 // utiliser directement l'info de taille presente dans le nom
2270 if ((!isset($options['utiliser_suffixe_size']) OR $options['utiliser_suffixe_size']==true)
2271 AND preg_match(',-([0-9]+)[.](png|gif)$,',$img,$regs)){
2272 $largeur = $hauteur = intval($regs[1]);
2273 }
2274 else{
2275 $taille = taille_image($img);
2276 list($hauteur,$largeur) = $taille;
2277 if (!$hauteur OR !$largeur)
2278 return "";
2279 }
2280 $atts.=" width='".$largeur."' height='".$hauteur."'";
2281 }
2282 return "<img src='$img' alt='" . attribut_html($alt ? $alt : $title) . "'"
2283 . ($title ? ' title="'.attribut_html($title).'"' : '')
2284 . " ".ltrim($atts)
2285 . " />";
2286 }
2287
2288 /**
2289 * generer une directive style='background:url()' a partir d'un fichier image
2290 *
2291 * http://doc.spip.org/@http_style_background
2292 *
2293 * @param string $img
2294 * @param string $att
2295 * @return string
2296 */
2297 function http_style_background($img, $att=''){
2298 return " style='background".($att?"":"-image").": url(\"".chemin_image($img)."\")" . ($att ? (' ' . $att) : '') . ";'";
2299 }
2300
2301 /**
2302 * une fonction pour generer une balise img a partir d'un nom de fichier
2303 *
2304 * @param string $img
2305 * @param string $alt
2306 * @param string $class
2307 * @return string
2308 */
2309 function filtre_balise_img_dist($img,$alt="",$class=""){
2310 return http_img_pack($img, $alt, $class?" class='".attribut_html($class)."'":'', '', array('chemin_image'=>false,'utiliser_suffixe_size'=>false));
2311 }
2312
2313
2314 //[(#ENV*|unserialize|foreach)]
2315 // http://doc.spip.org/@filtre_foreach_dist
2316 function filtre_foreach_dist($balise_deserializee, $modele = 'foreach') {
2317 $texte = '';
2318 if(is_array($balise_deserializee))
2319 foreach($balise_deserializee as $k => $v) {
2320 $res = recuperer_fond('modeles/'.$modele,
2321 array_merge(array('cle' => $k), (is_array($v) ? $v : array('valeur' => $v)))
2322 );
2323 $texte .= $res;
2324 }
2325 return $texte;
2326 }
2327
2328 // renvoie la liste des plugins actifs du site
2329 // si le premier parametre est un prefix de cette liste, renvoie vrai, faux sinon
2330 // la valeur du second parametre si celui-ci renvoie a une information connue
2331 // cf liste_plugin_actifs() pour connaitre les informations affichables
2332 // appelee par la balise #PLUGIN
2333 // http://doc.spip.org/@filtre_info_plugin_dist
2334 function filtre_info_plugin_dist($plugin, $type_info) {
2335 include_spip('inc/plugin');
2336 $plugin = strtoupper($plugin);
2337 $plugins_actifs = liste_plugin_actifs();
2338
2339 if (!$plugin)
2340 return serialize(array_keys($plugins_actifs));
2341 elseif (empty($plugins_actifs[$plugin]))
2342 return '';
2343 elseif ($type_info == 'est_actif')
2344 return $plugins_actifs[$plugin] ? 1 : 0;
2345 elseif (isset($plugins_actifs[$plugin][$type_info]))
2346 return $plugins_actifs[$plugin][$type_info];
2347 else {
2348 $get_infos = charger_fonction('get_infos','plugins');
2349 // On prend en compte les extensions
2350 if (!is_dir($plugins_actifs[$plugin]['dir_type']))
2351 $dir_plugins = constant($plugins_actifs[$plugin]['dir_type']);
2352 else
2353 $dir_plugins = $plugins_actifs[$plugin]['dir_type'];
2354 if (!$infos = $get_infos($plugins_actifs[$plugin]['dir'], false, $dir_plugins))
2355 return '';
2356 if ($type_info == 'tout')
2357 return $infos;
2358 else
2359 return strval($infos[$type_info]);
2360 }
2361 }
2362
2363
2364 // http://doc.spip.org/@puce_changement_statut
2365 function puce_changement_statut($id_objet, $statut, $id_rubrique, $type, $ajax=false){
2366 $puce_statut = charger_fonction('puce_statut','inc');
2367 return $puce_statut($id_objet, $statut, $id_rubrique, $type, $ajax);
2368 }
2369
2370 /**
2371 * [(#STATUT|puce_statut{article})] affiche une puce passive
2372 * [(#STATUT|puce_statut{article,#ID_ARTICLE,#ID_RUBRIQUE})] affiche une puce avec changement rapide
2373 *
2374 * utilisable sur tout objet qui a declare
2375 * @param string $statut
2376 * @param string $objet
2377 * @param int $id_objet
2378 * @param int $id_parent
2379 * @return string
2380 */
2381 function filtre_puce_statut_dist($statut,$objet,$id_objet=0,$id_parent=0){
2382 static $puce_statut = null;
2383 if (!$puce_statut)
2384 $puce_statut = charger_fonction('puce_statut','inc');
2385 return $puce_statut($id_objet, $statut, $id_parent, $objet, false, objet_info($objet,'editable')?_ACTIVER_PUCE_RAPIDE:false);
2386 }
2387
2388
2389 /**
2390 * Encoder un contexte pour l'ajax, le signer avec une cle, le crypter
2391 * avec le secret du site, le gziper si possible...
2392 * l'entree peut etre serialisee (le #ENV** des fonds ajax et ajax_stat)
2393 *
2394 * http://doc.spip.org/@encoder_contexte_ajax
2395 *
2396 * @param string|array $c
2397 * contexte, peut etre un tableau serialize
2398 * @param string $form
2399 * nom du formulaire eventuel
2400 * @param string $emboite
2401 * contenu a emboiter dans le conteneur ajax
2402 * @param string $ajaxid
2403 * ajaxid pour cibler le bloc et forcer sa mise a jour
2404 * @return string
2405 */
2406 function encoder_contexte_ajax($c,$form='', $emboite=NULL, $ajaxid='') {
2407 if (is_string($c)
2408 AND !is_null(@unserialize($c))) {
2409 $c = unserialize($c);
2410 }
2411
2412 // supprimer les parametres debut_x
2413 // pour que la pagination ajax ne soit pas plantee
2414 // si on charge la page &debut_x=1 : car alors en cliquant sur l'item 0,
2415 // le debut_x=0 n'existe pas, et on resterait sur 1
2416 foreach ($c as $k => $v) {
2417 if (strpos($k,'debut_') === 0) {
2418 unset($c[$k]);
2419 }
2420 }
2421
2422 if (!function_exists('calculer_cle_action'))
2423 include_spip("inc/securiser_action");
2424 $cle = calculer_cle_action($form.(is_array($c)?serialize($c):$c));
2425 $c = serialize(array($c,$cle));
2426
2427 // on ne stocke pas les contextes dans des fichiers caches
2428 // par defaut, sauf si cette configuration a ete forcee
2429 // OU que la longueur de l''argument generee est plus long
2430 // que ce que telere Suhosin.
2431 $cache_contextes_ajax = (defined('_CACHE_CONTEXTES_AJAX') AND _CACHE_CONTEXTES_AJAX);
2432 if (!$cache_contextes_ajax) {
2433 $env = $c;
2434 if (function_exists('gzdeflate') && function_exists('gzinflate')) {
2435 $env = gzdeflate($env);
2436 // http://core.spip.org/issues/2667 | https://bugs.php.net/bug.php?id=61287
2437 if (substr(phpversion(),0,5) == '5.4.0' AND !@gzinflate($env)) {
2438 $cache_contextes_ajax = true;
2439 spip_log("Contextes AJAX forces en fichiers ! Erreur PHP 5.4.0", _LOG_AVERTISSEMENT);
2440 }
2441 }
2442 $env = _xor($env);
2443 $env = base64_encode($env);
2444 // tester Suhosin et la valeur maximale des variables en GET...
2445 if ($max_len = @ini_get('suhosin.get.max_value_length')
2446 and $max_len < ($len = strlen($env))) {
2447 $cache_contextes_ajax = true;
2448 spip_log("Contextes AJAX forces en fichiers !"
2449 . " Cela arrive lorsque la valeur du contexte"
2450 . " depasse la longueur maximale autorisee par Suhosin"
2451 . " ($max_len) dans 'suhosin.get.max_value_length'. Ici : $len."
2452 . " Vous devriez modifier les parametres de Suhosin"
2453 . " pour accepter au moins 1024 caracteres.", _LOG_AVERTISSEMENT);
2454 }
2455 }
2456
2457 if ($cache_contextes_ajax) {
2458 $dir = sous_repertoire(_DIR_CACHE, 'contextes');
2459 // stocker les contextes sur disque et ne passer qu'un hash dans l'url
2460 $md5 = md5($c);
2461 ecrire_fichier("$dir/c$md5",$c);
2462 $env = $md5;
2463 }
2464
2465 if ($emboite === NULL) return $env;
2466 if (!trim($emboite)) return "";
2467 // toujours encoder l'url source dans le bloc ajax
2468 $r = self();
2469 $r = ' data-origin="'.$r.'"';
2470 $class = 'ajaxbloc';
2471 if ($ajaxid AND is_string($ajaxid)){
2472 $class .= ' ajax-id-'.$ajaxid;
2473 }
2474 $compl = "aria-live='polite' aria-atomic='true' ";
2475 return "<div class='$class' ".$compl."data-ajax-env='$env'$r>\n$emboite</div><!--ajaxbloc-->\n";
2476 }
2477
2478 // la procedure inverse de encoder_contexte_ajax()
2479 // http://doc.spip.org/@decoder_contexte_ajax
2480 function decoder_contexte_ajax($c,$form='') {
2481 if (!function_exists('calculer_cle_action'))
2482 include_spip("inc/securiser_action");
2483 if (( (defined('_CACHE_CONTEXTES_AJAX') AND _CACHE_CONTEXTES_AJAX) OR strlen($c)==32)
2484 AND $dir = sous_repertoire(_DIR_CACHE, 'contextes')
2485 AND lire_fichier("$dir/c$c",$contexte)) {
2486 $c = $contexte;
2487 } else {
2488 $c = @base64_decode($c);
2489 $c = _xor($c);
2490 if (function_exists('gzdeflate') && function_exists('gzinflate'))
2491 $c = @gzinflate($c);
2492 }
2493 list($env, $cle) = @unserialize($c);
2494
2495 if ($cle == calculer_cle_action($form.(is_array($env)?serialize($env):$env)))
2496 return $env;
2497 return false;
2498 }
2499
2500 // encrypter/decrypter un message
2501 // http://www.php.net/manual/fr/language.operators.bitwise.php#81358
2502 // http://doc.spip.org/@_xor
2503 function _xor($message, $key=null){
2504 if (is_null($key)) {
2505 if (!function_exists('calculer_cle_action'))
2506 include_spip("inc/securiser_action");
2507 $key = pack("H*", calculer_cle_action('_xor'));
2508 }
2509
2510 $keylen = strlen($key);
2511 $messagelen = strlen($message);
2512 for($i=0; $i<$messagelen; $i++)
2513 $message[$i] = ~($message[$i]^$key[$i%$keylen]);
2514
2515 return $message;
2516 }
2517
2518 // Les vrai fonctions sont dans le plugin forum, mais on evite ici une erreur du compilateur
2519 // en absence du plugin
2520 function url_reponse_forum($texte){return $texte;}
2521 function url_rss_forum($texte){return $texte;}
2522
2523
2524 /**
2525 * une fonction pour generer des menus avec liens
2526 * ou un <strong class='on'> non clicable lorsque l'item est selectionne
2527 *
2528 * @param string $url
2529 * @param string $libelle
2530 * le texte du lien
2531 * @param bool $on
2532 * etat expose (genere un strong) ou non (genere un lien)
2533 * @param string $class
2534 * @param string $title
2535 * @param string $rel
2536 * @param string $evt
2537 * complement a la balise a pour gerer un evenement javascript, de la forme " onclick='...'"
2538 * @return string
2539 */
2540 function lien_ou_expose($url,$libelle=NULL,$on=false,$class="",$title="",$rel="", $evt=''){
2541 if ($on) {
2542 $bal = "strong";
2543 $att = "class='on'";
2544 } else {
2545 $bal = 'a';
2546 $att = "href='$url'"
2547 .($title?" title='".attribut_html($title)."'":'')
2548 .($class?" class='".attribut_html($class)."'":'')
2549 .($rel?" rel='".attribut_html($rel)."'":'')
2550 .$evt;
2551 }
2552 if ($libelle === NULL)
2553 $libelle = $url;
2554 return "<$bal $att>$libelle</$bal>";
2555 }
2556
2557
2558 /**
2559 * Afficher un message "un truc"/"N trucs"
2560 * Les items sont à indiquer comme pour la fonction _T() sous la forme :
2561 * "module:chaine"
2562 *
2563 * @param int $nb : le nombre
2564 * @param string $chaine_un : l'item de langue si $nb vaut un
2565 * @param string $chaine_plusieurs : l'item de lanque si $nb > 1
2566 * @param string $var : La variable à remplacer par $nb dans l'item de langue (facultatif, défaut "nb")
2567 * @param array $vars : Les autres variables nécessaires aux chaines de langues (facultatif)
2568 * @return string : la chaine de langue finale en utilisant la fonction _T()
2569 */
2570 function singulier_ou_pluriel($nb,$chaine_un,$chaine_plusieurs,$var='nb',$vars=array()){
2571 if (!$nb=intval($nb)) return "";
2572 if (!is_array($vars)) return "";
2573 $vars[$var] = $nb;
2574 if ($nb>1) return _T($chaine_plusieurs, $vars);
2575 else return _T($chaine_un,$vars);
2576 }
2577
2578
2579 /**
2580 * Fonction de base pour une icone dans un squelette
2581 * structure html : <span><a><img><b>texte</b></span>
2582 *
2583 * @param string $type
2584 * 'lien' ou 'bouton'
2585 * @param string $lien
2586 * url
2587 * @param string $texte
2588 * texte du lien / alt de l'image
2589 * @param string $fond
2590 * objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
2591 * @param string $fonction
2592 * new/del/edit
2593 * @param string $class
2594 * classe supplementaire (horizontale, verticale, ajax ...)
2595 * @param string $javascript
2596 * "onclick='...'" par exemple
2597 * @return string
2598 */
2599 function prepare_icone_base($type, $lien, $texte, $fond, $fonction="", $class="",$javascript=""){
2600 if (in_array($fonction,array("del","supprimer.gif")))
2601 $class .= ' danger';
2602 elseif ($fonction == "rien.gif")
2603 $fonction = "";
2604 elseif ($fonction == "delsafe")
2605 $fonction = "del";
2606
2607 // remappage des icone : article-24.png+new => article-new-24.png
2608 if ($icone_renommer = charger_fonction('icone_renommer','inc',true))
2609 list($fond,$fonction) = $icone_renommer($fond,$fonction);
2610
2611 // ajouter le type d'objet dans la class de l'icone
2612 $class .= " " . substr(basename($fond),0,-4);
2613
2614 $alt = attribut_html($texte);
2615 $title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?
2616
2617 $ajax = "";
2618 if (strpos($class,"ajax")!==false) {
2619 $ajax="ajax";
2620 if (strpos($class,"preload")!==false)
2621 $ajax.=" preload";
2622 if (strpos($class,"nocache")!==false)
2623 $ajax.=" nocache";
2624 $ajax=" class='$ajax'";
2625 }
2626
2627 $size = 24;
2628 if (preg_match("/-([0-9]{1,3})[.](gif|png)$/i",$fond,$match))
2629 $size = $match[1];
2630
2631 if ($fonction){
2632 // 2 images pour composer l'icone : le fond (article) en background,
2633 // la fonction (new) en image
2634 $icone = http_img_pack($fonction, $alt, "width='$size' height='$size'\n" .
2635 http_style_background($fond));
2636 }
2637 else {
2638 $icone = http_img_pack($fond, $alt, "width='$size' height='$size'");
2639 }
2640
2641 if ($type=='lien')
2642 return "<span class='icone s$size $class'>"
2643 . "<a href='$lien'$title$ajax$javascript>"
2644 . $icone
2645 . "<b>$texte</b>"
2646 . "</a></span>\n";
2647
2648 else
2649 return bouton_action("$icone<b>$texte</b>",$lien,"icone s$size $class",$javascript,$alt);
2650 }
2651
2652 function icone_base($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
2653 return prepare_icone_base('lien', $lien, $texte, $fond, $fonction, $class, $javascript);
2654 }
2655 function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
2656 return icone_base($lien,$texte,$fond,$fonction,"verticale $class",$javascript);
2657 }
2658 function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
2659 return icone_base($lien,$texte,$fond,$fonction,"horizontale $class",$javascript);
2660 }
2661
2662 function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction="", $class="",$confirm=""){
2663 return prepare_icone_base('bouton', $lien, $texte, $fond, $fonction, "horizontale $class", $confirm);
2664 }
2665 /*
2666 * Filtre icone pour compatibilite
2667 * mappe sur icone_base
2668 */
2669 function filtre_icone_dist($lien, $texte, $fond, $align="", $fonction="", $class="",$javascript=""){
2670 return icone_base($lien,$texte,$fond,$fonction,"verticale $align $class",$javascript);
2671 }
2672
2673
2674 /**
2675 * filtre explode pour les squelettes permettant d'ecrire
2676 * #GET{truc}|explode{-}
2677 *
2678 * @param strong $a
2679 * @param string $b
2680 * @return array
2681 */
2682 function filtre_explode_dist($a,$b){return explode($b,$a);}
2683
2684 /**
2685 * filtre implode pour les squelettes permettant d'ecrire
2686 * #GET{truc}|implode{-}
2687 *
2688 * @param array $a
2689 * @param string $b
2690 * @return string
2691 */
2692 function filtre_implode_dist($a,$b){return is_array($a)?implode($b,$a):$a;}
2693
2694 /**
2695 * Produire les styles prives qui associent item de menu avec icone en background
2696 * @return string
2697 */
2698 function bando_images_background(){
2699 include_spip('inc/bandeau');
2700 // recuperer tous les boutons et leurs images
2701 $boutons = definir_barre_boutons(definir_barre_contexte(),true,false);
2702
2703 $res = "";
2704 foreach($boutons as $page => $detail){
2705 if ($detail->icone AND strlen(trim($detail->icone)))
2706 $res .="\n.navigation_avec_icones #bando1_$page {background-image:url(".$detail->icone.");}";
2707 $selecteur = (in_array($page,array('outils_rapides','outils_collaboratifs'))?"":".navigation_avec_icones ");
2708 if (is_array($detail->sousmenu))
2709 foreach($detail->sousmenu as $souspage=>$sousdetail)
2710 if ($sousdetail->icone AND strlen(trim($sousdetail->icone)))
2711 $res .="\n$selecteur.bando2_$souspage {background-image:url(".$sousdetail->icone.");}";
2712 }
2713 return $res;
2714 }
2715
2716 /**
2717 * Generer un bouton_action
2718 * utilise par #BOUTON_ACTION
2719 *
2720 * @param string $libelle
2721 * @param string $url
2722 * @param string $class
2723 * @param string $confirm
2724 * message de confirmation oui/non avant l'action
2725 * @param string $title
2726 * @param string $callback
2727 * callback js a appeler lors de l'evenement action (apres confirmation eventuelle si $confirm est non vide)
2728 * et avant execution de l'action. Si la callback renvoie false, elle annule le declenchement de l'action
2729 * @return string
2730 */
2731 function bouton_action($libelle, $url, $class="", $confirm="", $title="", $callback=""){
2732 if ($confirm) {
2733 $confirm = "confirm(\"" . attribut_html($confirm) . "\")";
2734 if ($callback)
2735 $callback = "$confirm?($callback):false";
2736 else
2737 $callback = $confirm;
2738 }
2739 $onclick = $callback?" onclick='return ".addcslashes($callback,"'")."'":"";
2740 $title = $title ? " title='$title'" : "";
2741 return "<form class='bouton_action_post $class' method='post' action='$url'><div>".form_hidden($url)
2742 ."<button type='submit' class='submit'$title$onclick>$libelle</button></div></form>";
2743 }
2744
2745
2746 /**
2747 * Proteger les champs passes dans l'url et utiliser dans {tri ...}
2748 * preserver l'espace pour interpreter ensuite num xxx et multi xxx
2749 * @param string $t
2750 * @return string
2751 */
2752 function tri_protege_champ($t){
2753 return preg_replace(',[^\s\w.+],','',$t);
2754 }
2755
2756 /**
2757 * Interpreter les multi xxx et num xxx utilise comme tri
2758 * pour la clause order
2759 * 'multi xxx' devient simplement 'multi' qui est calcule dans le select
2760 * @param string $t
2761 * @param array $from
2762 * @return string
2763 */
2764 function tri_champ_order($t, $from=null){
2765 if(strncmp($t,'multi ',6)==0){
2766 return "multi";
2767 }
2768
2769 $champ = $t;
2770
2771 if (strncmp($t,'num ',4)==0)
2772 $champ = substr($t,4);
2773 // enlever les autres espaces non evacues par tri_protege_champ
2774 $champ = preg_replace(',\s,','',$champ);
2775
2776 if (is_array($from)){
2777 $trouver_table = charger_fonction('trouver_table','base');
2778 foreach($from as $idt=>$table_sql){
2779 if ($desc = $trouver_table($table_sql)
2780 AND isset($desc['field'][$champ])){
2781 $champ = "$idt.$champ";
2782 break;
2783 }
2784 }
2785 }
2786 if (strncmp($t,'num ',4)==0)
2787 return "0+$champ";
2788 else
2789 return $champ;
2790 }
2791
2792 /**
2793 * Interpreter les multi xxx et num xxx utilise comme tri
2794 * pour la clause select
2795 * 'multi xxx' devient select "...." as multi
2796 * les autres cas ne produisent qu'une chaine vide '' en select
2797 * 'hasard' devient 'rand() AS hasard' dans le select
2798 *
2799 * @param string $t
2800 * @return string
2801 */
2802 function tri_champ_select($t){
2803 if(strncmp($t,'multi ',6)==0){
2804 $t = substr($t,6);
2805 $t = preg_replace(',\s,','',$t);
2806 $t = sql_multi($t,$GLOBALS['spip_lang']);
2807 return $t;
2808 }
2809 if(trim($t)=='hasard'){
2810 return 'rand() AS hasard';
2811 }
2812 return "''";
2813 }
2814
2815
2816 /**
2817 * Donner n'importe quelle information sur un objet de maniere generique.
2818 *
2819 * La fonction va gerer en interne deux cas particuliers les plus utilises :
2820 * l'URL et le titre (qui n'est pas forcemment le champ SQL "titre").
2821 *
2822 * On peut ensuite personnaliser les autres infos en creant une fonction
2823 * generer_<nom_info>_entite($id_objet, $type_objet, $ligne).
2824 * $ligne correspond a la ligne SQL de tous les champs de l'objet, les fonctions
2825 * de personnalisation n'ont donc pas a refaire de requete.
2826 *
2827 * @param int $id_objet
2828 * @param string $type_objet
2829 * @param string $info
2830 * @param string $etoile
2831 * @return string
2832 */
2833 function generer_info_entite($id_objet, $type_objet, $info, $etoile=""){
2834 global $table_des_traitements;
2835 static $trouver_table=null;
2836 static $objets;
2837
2838 // On verifie qu'on a tout ce qu'il faut
2839 $id_objet = intval($id_objet);
2840 if (!($id_objet and $type_objet and $info))
2841 return '';
2842
2843 // si on a deja note que l'objet n'existe pas, ne pas aller plus loin
2844 if (isset($objets[$type_objet]) AND $objets[$type_objet]===false)
2845 return '';
2846
2847 // Si on demande l'url, on retourne direct la fonction
2848 if ($info == 'url')
2849 return generer_url_entite($id_objet, $type_objet);
2850
2851 // Sinon on va tout chercher dans la table et on garde en memoire
2852 $demande_titre = ($info == 'titre');
2853
2854 // On ne fait la requete que si on a pas deja l'objet ou si on demande le titre mais qu'on ne l'a pas encore
2855 if (!isset($objets[$type_objet][$id_objet])
2856 OR
2857 ($demande_titre AND !isset($objets[$type_objet][$id_objet]['titre']))
2858 ){
2859 if (!$trouver_table)
2860 $trouver_table = charger_fonction('trouver_table','base');
2861 $desc = $trouver_table(table_objet_sql($type_objet));
2862 if (!$desc)
2863 return $objets[$type_objet] = false;
2864
2865 // Si on demande le titre, on le gere en interne
2866 $champ_titre = "";
2867 if ($demande_titre){
2868 // si pas de titre declare mais champ titre, il sera peuple par le select *
2869 $champ_titre = (!empty($desc['titre'])) ? ', ' . $desc['titre']:'';
2870 }
2871 include_spip('base/abstract_sql');
2872 include_spip('base/connect_sql');
2873 $objets[$type_objet][$id_objet] = sql_fetsel(
2874 '*'.$champ_titre,
2875 $desc['table_sql'],
2876 id_table_objet($type_objet).' = '.intval($id_objet)
2877 );
2878 }
2879
2880 // Si la fonction generer_TRUC_TYPE existe, on l'utilise pour formater $info_generee
2881 if ($generer = charger_fonction("generer_${info}_${type_objet}", '', true))
2882 $info_generee = $generer($id_objet, $objets[$type_objet][$id_objet]);
2883 // Si la fonction generer_TRUC_entite existe, on l'utilise pour formater $info_generee
2884 else if ($generer = charger_fonction("generer_${info}_entite", '', true))
2885 $info_generee = $generer($id_objet, $type_objet, $objets[$type_objet][$id_objet]);
2886 // Sinon on prend directement le champ SQL tel quel
2887 else
2888 $info_generee = (isset($objets[$type_objet][$id_objet][$info])?$objets[$type_objet][$id_objet][$info]:'');
2889
2890 // On va ensuite chercher les traitements automatiques a faire
2891 $champ = strtoupper($info);
2892 $traitement = isset($table_des_traitements[$champ]) ? $table_des_traitements[$champ] : false;
2893 $table_sql = table_objet_sql($type_objet);
2894
2895 if (!$etoile
2896 AND is_array($traitement)
2897 AND (isset($traitement[$table_sql]) OR isset($traitement[0]))){
2898 $traitement = $traitement[isset($traitement[$table_sql]) ? $table_sql : 0];
2899 $traitement = str_replace('%s', "'".texte_script($info_generee)."'", $traitement);
2900 // FIXME: $connect et $Pile[0] font souvent partie des traitements.
2901 // on les definit pour eviter des notices, mais ce fonctionnement est a ameliorer !
2902 $connect = ""; $Pile = array(0 => array('id_objet'=>$id_objet,'objet'=>$type_objet));
2903 eval("\$info_generee = $traitement;");
2904 }
2905
2906 return $info_generee;
2907 }
2908
2909 /**
2910 * Wrap un texte avec des balises
2911 * wrap('mot','<b>') => '<b>mot</b>'
2912 * @param string $texte
2913 * @param string $wrap
2914 * @return string
2915 */
2916 function wrap($texte,$wrap) {
2917 $balises = extraire_balises($wrap);
2918 if (preg_match_all(",<([a-z]\w*)\b[^>]*>,UimsS",$wrap, $regs, PREG_PATTERN_ORDER)) {
2919 $texte = $wrap . $texte;
2920 $regs = array_reverse($regs[1]);
2921 $wrap = "</".implode("></",$regs).">";
2922 $texte = $texte . $wrap;
2923 }
2924 return $texte;
2925 }
2926
2927
2928 /**
2929 * afficher proprement n'importe quoi
2930 * On affiche in fine un pseudo-yaml qui premet de lire humainement les tableaux et de s'y reperer
2931 *
2932 * Les textes sont retournes avec simplement mise en forme typo
2933 *
2934 * le $join sert a separer les items d'un tableau, c'est en general un \n ou <br /> selon si on fait du html ou du texte
2935 * les tableaux-listes (qui n'ont que des cles numeriques), sont affiches sous forme de liste separee par des virgules :
2936 * c'est VOULU !
2937 *
2938 * @param $u
2939 * @param string $join
2940 * @param int $indent
2941 * @return array|mixed|string
2942 */
2943 function filtre_print_dist($u, $join="<br />", $indent=0) {
2944 if (is_string($u)){
2945 $u = typo($u);
2946 return $u;
2947 }
2948
2949 // caster $u en array si besoin
2950 if (is_object($u))
2951 $u = (array) $u;
2952
2953 if (is_array($u)){
2954 $out = "";
2955 // toutes les cles sont numeriques ?
2956 // et aucun enfant n'est un tableau
2957 // liste simple separee par des virgules
2958 $numeric_keys = array_map('is_numeric',array_keys($u));
2959 $array_values = array_map('is_array',$u);
2960 $object_values = array_map('is_object',$u);
2961 if (array_sum($numeric_keys)==count($numeric_keys)
2962 AND !array_sum($array_values)
2963 AND !array_sum($object_values)){
2964 return join(", ", array_map('filtre_print_dist', $u));
2965 }
2966
2967 // sinon on passe a la ligne et on indente
2968 $i_str = str_pad("",$indent," ");
2969 foreach($u as $k => $v){
2970 $out .= $join . $i_str . "$k: " . filtre_print_dist($v,$join,$indent+2);
2971 }
2972 return $out;
2973 }
2974
2975 // on sait pas quoi faire...
2976 return $u;
2977 }
2978
2979
2980 /**
2981 * Renvoyer l'info d'un objet
2982 * telles que definies dans declarer_tables_objets_sql
2983 *
2984 * @param string $objet
2985 * @param string $info
2986 * @return string
2987 */
2988 function objet_info($objet,$info){
2989 $table = table_objet_sql($objet);
2990 $infos = lister_tables_objets_sql($table);
2991 return (isset($infos[$info])?$infos[$info]:'');
2992 }
2993
2994 /**
2995 * Filtre pour afficher 'Aucun truc' ou '1 truc' ou 'N trucs'
2996 * avec la bonne chaine de langue en fonction de l'objet utilise
2997 * @param $nb
2998 * @param $objet
2999 * @return mixed|string
3000 */
3001 function objet_afficher_nb($nb, $objet){
3002 if (!$nb)
3003 return _T(objet_info($objet,'info_aucun_objet'));
3004 else
3005 return _T(objet_info($objet,$nb==1?'info_1_objet':'info_nb_objets'),array('nb'=>$nb));
3006 }
3007
3008 /**
3009 * Filtre pour afficher l'img icone d'un objet
3010 *
3011 * @param string $objet
3012 * @param int $taille
3013 * @return string
3014 */
3015 function objet_icone($objet,$taille=24){
3016 $icone = objet_info($objet,'icone_objet')."-".$taille.".png";
3017 $icone = chemin_image($icone);
3018 $balise_img = charger_filtre('balise_img');
3019 return $icone?$balise_img($icone,_T(objet_info($objet,'texte_objet'))):'';
3020 }
3021
3022 /**
3023 * Fonction de secours pour inserer le head_css de facon conditionnelle
3024 *
3025 * Appelée en filtre sur le squelette qui contient #INSERT_HEAD,
3026 * elle vérifie l'absence éventuelle de #INSERT_HEAD_CSS et y suplée si besoin
3027 * pour assurer la compat avec les squelettes qui n'utilisent pas.
3028 *
3029 * @param string $flux Code HTML
3030 * @return string Code HTML
3031 */
3032 function insert_head_css_conditionnel($flux){
3033 if (strpos($flux,'<!-- insert_head_css -->')===false
3034 AND $p=strpos($flux,'<!-- insert_head -->')){
3035 // plutot avant le premier js externe (jquery) pour etre non bloquant
3036 if ($p1 = stripos($flux,'<script src=') AND $p1<$p)
3037 $p = $p1;
3038 $flux = substr_replace($flux,pipeline('insert_head_css','<!-- insert_head_css -->'),$p,0);
3039 }
3040 return $flux;
3041 }
3042
3043 /**
3044 * Produire un fichier statique a partir d'un squelette dynamique
3045 * Permet ensuite a apache de le servir en statique sans repasser
3046 * par spip.php a chaque hit sur le fichier
3047 * si le format (css ou js) est passe dans contexte['format'], on l'utilise
3048 * sinon on regarde si le fond finit par .css ou .js
3049 * sinon on utilie "html"
3050 *
3051 * @param string $fond
3052 * @param array $contexte
3053 * @param array $options
3054 * @param string $connect
3055 * @return string
3056 */
3057 function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
3058 if (isset($contexte['format'])){
3059 $extension = $contexte['format'];
3060 unset($contexte['format']);
3061 }
3062 else {
3063 $extension = "html";
3064 if (preg_match(',[.](css|js|json)$,',$fond,$m))
3065 $extension = $m[1];
3066 }
3067 // recuperer le contenu produit par le squelette
3068 $options['raw'] = true;
3069 $cache = recuperer_fond($fond,$contexte,$options,$connect);
3070
3071 // calculer le nom de la css
3072 $dir_var = sous_repertoire (_DIR_VAR, 'cache-'.$extension);
3073 $nom_safe = preg_replace(",\W,",'_',str_replace('.','_',$fond));
3074 $filename = $dir_var . $extension."dyn-$nom_safe-".substr(md5($fond.serialize($contexte).$connect),0,8) .".$extension";
3075
3076 // mettre a jour le fichier si il n'existe pas
3077 // ou trop ancien
3078 // le dernier fichier produit est toujours suffixe par .last
3079 // et recopie sur le fichier cible uniquement si il change
3080 if (!file_exists($filename)
3081 OR !file_exists($filename.".last")
3082 OR (isset($cache['lastmodified']) AND $cache['lastmodified'] AND filemtime($filename.".last")<$cache['lastmodified'])
3083 OR (defined('_VAR_MODE') AND _VAR_MODE=='recalcul')) {
3084 $contenu = $cache['texte'];
3085 // passer les urls en absolu si c'est une css
3086 if ($extension=="css")
3087 $contenu = urls_absolues_css($contenu, test_espace_prive()?generer_url_ecrire('accueil'):generer_url_public($fond));
3088
3089 // ne pas insérer de commentaire si c'est du json
3090 if ($extension!="json") {
3091 $comment = "/* #PRODUIRE{fond=$fond";
3092 foreach($contexte as $k=>$v)
3093 $comment .= ",$k=$v";
3094 // pas de date dans le commentaire car sinon ca invalide le md5 et force la maj
3095 // mais on peut mettre un md5 du contenu, ce qui donne un aperu rapide si la feuille a change ou non
3096 $comment .="}\n md5:".md5($contenu)." */\n";
3097 }
3098 // et ecrire le fichier
3099 ecrire_fichier($filename.".last",$comment.$contenu);
3100 // regarder si on recopie
3101 if (!file_exists($filename)
3102 OR md5_file($filename)!==md5_file($filename.".last")){
3103 @copy($filename.".last",$filename);
3104 spip_clearstatcache(true,$filename); // eviter que PHP ne reserve le vieux timestamp
3105 }
3106 }
3107
3108 return $filename;
3109 }
3110
3111 /**
3112 * Ajouter un timestamp a une url de fichier
3113 * [(#CHEMIN{monfichier}|timestamp)]
3114 *
3115 * @param string $fichier
3116 * @return string
3117 */
3118 function timestamp($fichier){
3119 if (!$fichier OR !file_exists($fichier)) return $fichier;
3120 $m = filemtime($fichier);
3121 return "$fichier?$m";
3122 }
3123
3124 /**
3125 * Nettoyer le titre d'un email
3126 * eviter une erreur lorsqu'on utilise |nettoyer_titre_email dans un squelette de mail
3127 * @param $titre
3128 * @return mixed
3129 */
3130 function filtre_nettoyer_titre_email_dist($titre){
3131 include_spip('inc/envoyer_mail');
3132 return nettoyer_titre_email($titre);
3133 }
3134
3135 /**
3136 * Afficher le sélecteur de rubrique
3137 *
3138 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
3139 *
3140 * @param string $titre
3141 * @param int $id_objet
3142 * @param int $id_parent
3143 * @param string $objet
3144 * @param int $id_secteur
3145 * @param bool $restreint
3146 * @param bool $actionable
3147 * true : fournit le selecteur dans un form directement postable
3148 * @param bool $retour_sans_cadre
3149 * @return string
3150 */
3151 function filtre_chercher_rubrique_dist($titre,$id_objet, $id_parent, $objet, $id_secteur, $restreint,$actionable = false, $retour_sans_cadre=false){
3152 include_spip('inc/filtres_ecrire');
3153 return chercher_rubrique($titre,$id_objet, $id_parent, $objet, $id_secteur, $restreint,$actionable, $retour_sans_cadre);
3154 }
3155
3156 /**
3157 * Rediriger une page suivant une autorisation,
3158 * et ce, n'importe où dans un squelette, même dans les inclusions.
3159 * Exemple :
3160 * [(#AUTORISER{non}|sinon_interdire_acces)]
3161 * [(#AUTORISER{non}|sinon_interdire_acces{#URL_PAGE{login}, 401})]
3162 *
3163 * @param bool $ok Indique si l'on doit rediriger ou pas
3164 * @param string $url Adresse vers laquelle rediriger
3165 * @param int $statut Statut HTML avec lequel on redirigera
3166 * @return string
3167 */
3168 function sinon_interdire_acces($ok=false, $url='', $statut=0){
3169 if ($ok) return '';
3170
3171 // Vider tous les tampons
3172 $level = @ob_get_level();
3173 while ($level--)
3174 @ob_end_clean();
3175
3176 include_spip('inc/headers');
3177 $statut = intval($statut);
3178
3179 // Si aucun argument on essaye de deviner quoi faire par défaut
3180 if (!$url and !$statut){
3181 // Si on est dans l'espace privé, on génère du 403 Forbidden
3182 if (test_espace_prive()){
3183 http_status(403);
3184 $echec = charger_fonction('403','exec');
3185 $echec();
3186 }
3187 // Sinon dans l'espace public on redirige vers une 404 par défaut, car elle toujours présente normalement
3188 else{
3189 $statut = 404;
3190 }
3191 }
3192
3193 // On suit les directives indiquées dans les deux arguments
3194
3195 // S'il y a un statut
3196 if ($statut){
3197 // Dans tous les cas on modifie l'entité avec ce qui est demandé
3198 http_status($statut);
3199 // Si le statut est une erreur et qu'il n'y a pas de redirection on va chercher le squelette du même nom
3200 if ($statut >= 400 and !$url)
3201 echo recuperer_fond("$statut");
3202 }
3203
3204 // S'il y a une URL, on redirige (si pas de statut, la fonction mettra 302 par défaut)
3205 if ($url) redirige_par_entete($url, '', $statut);
3206
3207 exit;
3208 }
3209
3210 /**
3211 * Assurer le fonctionnement de |compacte meme sans l'extension compresseur
3212 * @param string $source
3213 * @param null|string $format
3214 * @return string
3215 */
3216 function filtre_compacte_dist($source, $format = null){
3217 if (function_exists('compacte'))
3218 return compacte($source, $format);
3219 return $source;
3220 }
3221
3222 ?>