b54edffbc29a23826542ee1144c37b9b7960b325
[lhc/web/www.git] / www / plugins-dist / textwheel / inc / lien.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
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')) {
14 return;
15 }
16
17 include_spip('base/abstract_sql');
18
19 /**
20 * Production de la balise a+href à partir des raccourcis `[xxx->url]` etc.
21 *
22 * @note
23 * Compliqué car c'est ici qu'on applique typo(),
24 * et en plus, on veut pouvoir les passer en pipeline
25 *
26 * @see typo()
27 * @param string $lien
28 * @param string $texte
29 * @param string $class
30 * @param string $title
31 * @param string $hlang
32 * @param string $rel
33 * @param string $connect
34 * @param array $env
35 * @return string
36 */
37 function inc_lien_dist(
38 $lien,
39 $texte = '',
40 $class = '',
41 $title = '',
42 $hlang = '',
43 $rel = '',
44 $connect = '',
45 $env = array()
46 ) {
47 static $u = null;
48 if (!$u) {
49 $u = url_de_base();
50 }
51 $typo = false;
52
53 // Si une langue est demandee sur un raccourci d'article, chercher
54 // la traduction ;
55 // - [{en}->art2] => traduction anglaise de l'article 2, sinon art 2
56 // - [{}->art2] => traduction en langue courante de l'art 2, sinon art 2
57 // s'applique a tout objet traduit
58 if (
59 $hlang
60 and $match = typer_raccourci($lien)
61 ) {
62 @list($type, , $id, , $args, , $ancre) = $match;
63 $trouver_table = charger_fonction('trouver_table', 'base');
64 $desc = $trouver_table(table_objet($type, $connect), $connect);
65 if (
66 $desc
67 and $id_table_objet = $desc['key']['PRIMARY KEY']
68 ) {
69 $table_objet_sql = $desc['table'];
70 if (
71 $row = sql_fetsel('*', $table_objet_sql, "$id_table_objet=" . intval($id))
72 and isset($row['id_trad'])
73 and isset($row['lang'])
74 and $id_dest = sql_getfetsel($id_table_objet, $table_objet_sql,
75 "id_trad=" . intval($row['id_trad']) . " AND lang=" . sql_quote($hlang))
76 and objet_test_si_publie($type, $id_dest)
77 ) {
78 $lien = "$type$id_dest";
79 } else {
80 $hlang = '';
81 }
82 } else {
83 $hlang = '';
84 }
85 }
86
87 $mode = ($texte and $class) ? 'url' : 'tout';
88 $lang = '';
89 $lien = calculer_url($lien, $texte, $mode, $connect);
90 if ($mode === 'tout') {
91 $texte = $lien['titre'];
92 if (!$class and isset($lien['class'])) {
93 $class = $lien['class'];
94 }
95 $lang = isset($lien['lang']) ? $lien['lang'] : '';
96 $mime = isset($lien['mime']) ? " type='" . $lien['mime'] . "'" : "";
97 $lien = $lien['url'];
98 }
99
100 $lien = trim($lien);
101 if (strncmp($lien, "#", 1) == 0) { # ancres pures (internes a la page)
102 $class = 'spip_ancre';
103 } elseif (strncasecmp($lien, 'mailto:', 7) == 0) { # pseudo URL de mail
104 $class = "spip_mail";
105 } elseif (strncmp($texte, '<html>', 6) == 0) { # cf traiter_lien_explicite
106 $class = "spip_url";
107 # spip_out sur les URLs externes
108 if (
109 preg_match(',^\w+://,iS', $lien)
110 and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
111 ) {
112 $class .= " spip_out";
113 }
114 } elseif (!$class) {
115 # spip_out sur les URLs externes
116 if (
117 preg_match(',^\w+://,iS', $lien)
118 and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
119 ) {
120 $class = "spip_out"; # si pas spip_in|spip_glossaire
121 }
122 }
123 if ($class) {
124 $class = " class='$class'";
125 }
126
127 // Si l'objet n'est pas de la langue courante, on ajoute hreflang
128 if (!$hlang and isset($lang) and $lang !== $GLOBALS['spip_lang']) {
129 $hlang = $lang;
130 }
131
132 $lang = ($hlang ? " hreflang='$hlang'" : '');
133
134 if ($title) {
135 $title = ' title="' . attribut_html($title) . '"';
136 } else {
137 $title = ''; // $title peut etre 'false'
138 }
139
140 // rel=external pour les liens externes
141 if (
142 (strncmp($lien, 'http://', 7) == 0 or strncmp($lien, 'https://', 8) == 0)
143 and strncmp("$lien/", $u, strlen($u)) != 0
144 ) {
145 $rel = trim("$rel external");
146 }
147 if ($rel) {
148 $rel = " rel='$rel'";
149 }
150
151 $lang_objet_prev = '';
152 if ($hlang and $hlang !== $GLOBALS['spip_lang']) {
153 $lang_objet_prev = isset($GLOBALS['lang_objet']) ? $GLOBALS['lang_objet'] : null;
154 $GLOBALS['lang_objet'] = $hlang;
155 }
156
157 // si pas de modele dans le texte du lien, on peut juste passer typo sur le texte, c'est plus rapide
158 // les rares cas de lien qui encapsule un modele passe en dessous, c'est plus lent
159 if (traiter_modeles($texte, false, '', $connect, null, $env) == $texte) {
160 $texte = typo($texte, true, $connect, $env);
161 $lien = "<a href=\"" . str_replace('"', '&quot;',
162 $lien) . "\"$class$lang$title$rel" . (isset($mime) ? $mime : '') . ">$texte</a>";
163 if ($lang_objet_prev !== '') {
164 if ($lang_objet_prev) {
165 $GLOBALS['lang_objet'] = $lang_objet_prev;
166 } else {
167 unset($GLOBALS['lang_objet']);
168 }
169 }
170
171 return $lien;
172 }
173
174 # ceci s'execute heureusement avant les tableaux et leur "|".
175 # Attention, le texte initial est deja echappe mais pas forcement
176 # celui retourne par calculer_url.
177 # Penser au cas [<imgXX|right>->URL], qui exige typo('<a>...</a>')
178 $lien = "<a href=\"" . str_replace('"', '&quot;', $lien) . "\"$class$lang$title$rel$mime>$texte</a>";
179 #$res = typo($lien, true, $connect, $env);
180 $p = $GLOBALS['toujours_paragrapher'];
181 $GLOBALS['toujours_paragrapher'] = false;
182 $res = propre($lien, $connect, $env);
183 $GLOBALS['toujours_paragrapher'] = $p;
184
185 // dans ce cas, echapons le resultat du modele pour que propre etc ne viennent pas pouicher le html
186 $res = echappe_html("<html>$res</html>");
187 if ($lang_objet_prev !== '') {
188 if ($lang_objet_prev) {
189 $GLOBALS['lang_objet'] = $lang_objet_prev;
190 } else {
191 unset($GLOBALS['lang_objet']);
192 }
193 }
194
195 return $res;
196 }
197
198 /**
199 * Générer le HTML d'un lien quelconque
200 *
201 * Cette fonction génère une balise `<a>` suivant de multiples arguments.
202 *
203 * @param array $args
204 * Tableau des arguments disponibles pour générer le lien :
205 * - texte : texte du lien, seul argument qui n'est pas un attribut
206 * - href
207 * - name
208 * - etc, tout autre attribut supplémentaire…
209 * @return string
210 * Retourne une balise HTML de lien ou une chaîne vide.
211 */
212 function balise_a($args = array()) {
213 $balise_a = '';
214
215 // Il faut soit au minimum un href OU un name pour réussir à générer quelque chose
216 if (is_array($args) and (isset($args['href']) or isset($args['name']))) {
217 include_spip('inc/filtres');
218 $texte = '';
219
220 // S'il y a un texte, on le récupère et on l'enlève des attributs
221 if (isset($args['texte']) and is_scalar($args['texte'])) {
222 $texte = $args['texte'];
223 unset($args['texte']);
224 } // Si on a un href sans texte, on en construit un avec l'URL
225 elseif (isset($args['href']) and is_scalar($args['href'])) {
226 static $lien_court;
227 if (!$lien_court) {
228 $lien_court = charger_fonction('lien_court', 'inc');
229 }
230 $texte = quote_amp($lien_court($args['href']));
231 }
232
233 // Il ne reste normalement plus que des attributs, on les ajoute à la balise
234 $balise_a = '<a';
235 foreach ($args as $attribut => $valeur) {
236 if (is_scalar($valeur) and !empty($valeur)) {
237 $balise_a .= ' ' . $attribut . '="' . attribut_html($valeur) . '"';
238 }
239 }
240 // Puis on ajoute le texte
241 $balise_a .= '>' . $texte . '</a>';
242 }
243
244 return $balise_a;
245 }
246
247 // Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip
248 // Laisser passer des paires de crochets pour la balise multi
249 // mais refuser plus d'imbrications ou de mauvaises imbrications
250 // sinon les crochets ne peuvent plus servir qu'a ce type de raccourci
251 define('_RACCOURCI_LIEN', "/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS");
252
253 // https://code.spip.net/@expanser_liens
254 function expanser_liens($t, $connect = '', $env = array()) {
255 $t = pipeline('pre_liens', $t);
256
257 if (strpos($t, '\[') !== false or strpos($t, '\]') !== false) {
258 $t = str_replace(array('\[', '\]'), array("\x1\x5", "\x1\x6"), $t);
259 }
260
261 expanser_un_lien($connect, 'init', $env);
262
263 if (strpos($t, '->') !== false) {
264 $t = preg_replace_callback(_RACCOURCI_LIEN, 'expanser_un_lien', $t);
265 }
266
267 // on passe a traiter_modeles la liste des liens reperes pour lui permettre
268 // de remettre le texte d'origine dans les parametres du modele
269 $t = traiter_modeles($t, false, false, $connect, expanser_un_lien('', 'sources'), $env);
270
271 if (strpos($t, "\x1") !== false) {
272 $t = str_replace(array("\x1\x5", "\x1\x6"), array('[', ']'), $t);
273 }
274
275 $t = corriger_typo($t);
276
277 $t = expanser_un_lien($t, 'reinsert');
278
279 return $t;
280 }
281
282
283 function expanser_un_lien($reg, $quoi = 'echappe', $env = null) {
284 static $pile = array();
285 static $inserts;
286 static $sources;
287 static $regs;
288 static $k = 0;
289 static $lien;
290 static $connect = '';
291 static $contexte = array();
292
293 switch ($quoi) {
294 case 'init':
295 if (!$lien) {
296 $lien = charger_fonction('lien', 'inc');
297 }
298 if (!is_null($env)) {
299 $contexte = $env;
300 }
301 array_push($pile, array($inserts, $sources, $regs, $connect, $k));
302 $inserts = $sources = $regs = array();
303 $connect = $reg; // stocker le $connect pour les appels a inc_lien_dist
304 $k = 0;
305
306 return;
307 break;
308 case 'echappe':
309 $inserts[$k] = '@@SPIP_ECHAPPE_LIEN_' . $k . '@@';
310 $sources[$k] = $reg[0];
311
312 #$titre=$reg[1];
313 list($titre, $bulle, $hlang) = traiter_raccourci_lien_atts($reg[1]);
314 $r = end($reg);
315 // la mise en lien automatique est passee par la a tort !
316 // corrigeons pour eviter d'avoir un <a...> dans un href...
317 if (strncmp($r, '<a', 2) == 0) {
318 $href = extraire_attribut($r, 'href');
319 // remplacons dans la source qui peut etre reinjectee dans les arguments
320 // d'un modele
321 $sources[$k] = str_replace($r, $href, $sources[$k]);
322 // et prenons le href comme la vraie url a linker
323 $r = $href;
324 }
325 $regs[$k] = $lien($r, $titre, '', $bulle, $hlang, '', $connect, $contexte);
326
327 return $inserts[$k++];
328 break;
329 case 'reinsert':
330 if (count($inserts)) {
331 $reg = str_replace($inserts, $regs, $reg);
332 }
333 list($inserts, $sources, $regs, $connect, $k) = array_pop($pile);
334
335 return $reg;
336 break;
337 case 'sources':
338 return array($inserts, $sources);
339 break;
340 }
341 }
342
343 /**
344 * Nettoie un texte en enlevant les raccourcis typo, sans les traiter
345 *
346 * On ne laisse que les titres des liens, en les explicitant si ce n’est pas fait.
347 *
348 * @param string $texte
349 * @param string $connect
350 * @return string
351 */
352 function nettoyer_raccourcis_typo($texte, $connect = '') {
353 $texte = pipeline('nettoyer_raccourcis_typo', $texte);
354
355 // on utilise les \r pour passer entre les gouttes
356 $texte = str_replace("\r\n", "\n", $texte);
357 $texte = str_replace("\r", "\n", $texte);
358
359 // sauts de ligne et paragraphes
360 $texte = preg_replace("/\n\n+/", "\r", $texte);
361
362 // supprimer les traits, lignes etc
363 $texte = preg_replace("/(^|\r|\n)(-[-#\*]*\s?|_ )/", "\n", $texte);
364
365 // travailler en accents charset
366 $texte = unicode2charset(html2unicode($texte, true /* secure */ ));
367
368 if (preg_match_all(_RACCOURCI_LIEN, $texte, $regs, PREG_SET_ORDER)) {
369 include_spip('inc/texte');
370 foreach ($regs as $reg) {
371 list($titre, , ) = traiter_raccourci_lien_atts($reg[1]);
372 if (!$titre) {
373 $match = typer_raccourci($reg[count($reg) - 1]);
374 if (!isset($match[0])) {
375 $match[0] = '';
376 }
377 @list($type, , $id, , , , ) = $match;
378
379 if ($type) {
380 $url = generer_url_entite($id, $type, '', '', true);
381 if (is_array($url)) {
382 list($type, $id) = $url;
383 }
384 $titre = traiter_raccourci_titre($id, $type, $connect);
385 }
386 $titre = $titre ? $titre['titre'] : $match[0];
387 }
388 $titre = corriger_typo(supprimer_tags($titre));
389 $texte = str_replace($reg[0], $titre, $texte);
390 }
391 }
392
393 // supprimer les ancres
394 $texte = preg_replace(_RACCOURCI_ANCRE, "", $texte);
395
396 // supprimer les notes
397 $texte = preg_replace(",\[\[.*\]\],UimsS", "", $texte);
398
399 // supprimer les codes typos
400 $texte = str_replace(array('}', '{'), '', $texte);
401
402 // supprimer les tableaux
403 $texte = preg_replace(",(?:^|\r|\n)\|.*\|(?:\r|\n|$),s", "\r", $texte);
404
405 // indiquer les sauts de paragraphes
406 $texte = str_replace("\r", "\n\n", $texte);
407 $texte = str_replace("\n\n+", "\n\n", $texte);
408
409 $texte = trim($texte);
410
411 return $texte;
412 }
413
414
415 // Repere dans la partie texte d'un raccourci [texte->...]
416 // la langue et la bulle eventuelles : [texte|title{lang}->...]
417 // accepte un niveau de paire de crochets dans le texte :
418 // [texte[]|title{lang}->...]
419 // mais refuse
420 // [texte[|title{lang}->...]
421 // pour ne pas confondre avec un autre raccourci
422 define('_RACCOURCI_ATTRIBUTS', '/^((?:[^[]*?(?:\[[^]]*\])?)*?)([|]([^<>]*?))?([{]([a-z_]*)[}])?$/');
423
424 // https://code.spip.net/@traiter_raccourci_lien_atts
425 function traiter_raccourci_lien_atts($texte) {
426 $bulle = $hlang = false;
427
428 // title et hreflang donnes par le raccourci ?
429 if (
430 strpbrk($texte, "|{") !== false
431 and preg_match(_RACCOURCI_ATTRIBUTS, $texte, $m)
432 ) {
433 $n = count($m);
434
435 // |infobulle ?
436 if ($n > 2) {
437 $bulle = $m[3];
438
439 // {hreflang} ?
440 if ($n > 4) {
441 // si c'est un code de langue connu, on met un hreflang
442 if (traduire_nom_langue($m[5]) <> $m[5]) {
443 $hlang = $m[5];
444 } elseif (!$m[5]) {
445 $hlang = test_espace_prive() ?
446 $GLOBALS['lang_objet'] : $GLOBALS['spip_lang'];
447 // sinon c'est un italique ou un gras dans le title ou dans le texte du lien
448 } else {
449 if ($bulle) {
450 $bulle .= $m[4];
451 } else {
452 $m[1] .= $m[2] . $m[4];
453 }
454 }
455 }
456 // S'il n'y a pas de hreflang sous la forme {}, ce qui suit le |
457 // est peut-etre une langue
458 else {
459 if (preg_match('/^[a-z_]+$/', $m[3])) {
460 // si c'est un code de langue connu, on met un hreflang
461 // mais on laisse le title (c'est arbitraire tout ca...)
462 if (traduire_nom_langue($m[3]) <> $m[3]) {
463 $hlang = $m[3];
464 }
465 }
466 }
467 }
468 $texte = $m[1];
469 }
470
471 if ($bulle) {
472 $bulle = nettoyer_raccourcis_typo($bulle);
473 $bulle = corriger_typo($bulle);
474 }
475
476 return array(trim($texte), $bulle, $hlang);
477 }
478
479 define('_EXTRAIRE_DOMAINE', '/^(?:(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z0-9]{2,6}|localhost)\b/Si');
480 define('_RACCOURCI_CHAPO', '/^(\W*)(\W*)(\w*\d+([?#].*)?)$/');
481
482 /**
483 * Retourne la valeur d'un champ de redirection (articles virtuels)
484 *
485 * L'entrée accepte plusiers types d'écritures :
486 * - une URL compète,
487 * - un lien SPIP tel que `[Lien->article23]`,
488 * - ou un raccourcis SPIP comme `rub2` ou `rubrique2`
489 *
490 * @param string $virtuel
491 * Texte qui définit la redirection, à analyser.
492 * Plusieurs types peuvent être acceptés :
493 * - un raccourci Spip habituel, tel que `[texte->TYPEnnn]`
494 * - un ultra raccourci Spip, tel que `TYPEnnn`
495 * - une URL standard
496 * @param bool $url
497 * false : retourne uniquement le nom du lien (TYPEnnn)
498 * true : retourne l'URL calculée pour le lien
499 * @return string
500 * Nom du lien ou URL
501 */
502 function virtuel_redirige($virtuel, $url = false) {
503 if (!strlen($virtuel)) {
504 return '';
505 }
506 if (
507 !preg_match(_RACCOURCI_LIEN, $virtuel, $m)
508 and !preg_match(_RACCOURCI_CHAPO, $virtuel, $m)
509 ) {
510 return $virtuel;
511 }
512
513 return !$url ? $m[3] : traiter_lien_implicite($m[3]);
514 }
515
516
517 // Cherche un lien du type [->raccourci 123]
518 // associe a une fonction generer_url_raccourci() definie explicitement
519 // ou implicitement par le jeu de type_urls courant.
520 //
521 // Valeur retournee selon le parametre $pour:
522 // 'tout' : tableau d'index url,class,titre,lang (vise <a href="U" class='C' hreflang='L'>T</a>)
523 // 'titre': seulement T ci-dessus (i.e. le TITRE ci-dessus ou dans table SQL)
524 // 'url': seulement U (i.e. generer_url_RACCOURCI)
525
526 // https://code.spip.net/@calculer_url
527 function calculer_url($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
528 $r = traiter_lien_implicite($ref, $texte, $pour, $connect, $echappe_typo);
529 $r = ($r ? $r : traiter_lien_explicite($ref, $texte, $pour, $connect, $echappe_typo));
530
531 return $r;
532 }
533
534 define('_EXTRAIRE_LIEN', ",^\s*(http:?/?/?|mailto:?)\s*$,iS");
535
536 // https://code.spip.net/@traiter_lien_explicite
537 function traiter_lien_explicite($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
538 if (preg_match(_EXTRAIRE_LIEN, $ref)) {
539 return ($pour != 'tout') ? '' : array('', '', '', '');
540 }
541
542 $lien = entites_html(trim($ref));
543
544 // Liens explicites
545 if (!$texte) {
546 $texte = str_replace('"', '', $lien);
547 static $lien_court;
548 // evite l'affichage de trop longues urls.
549 if (!$lien_court) {
550 $lien_court = charger_fonction('lien_court', 'inc');
551 }
552 $texte = $lien_court($texte);
553 if ($echappe_typo) {
554 $texte = "<html>" . quote_amp($texte) . "</html>";
555 }
556 }
557
558 // petites corrections d'URL
559 if (preg_match('/^www\.[^@]+$/S', $lien)) {
560 $lien = "http://" . $lien;
561 } else {
562 if (strpos($lien, "@") && email_valide($lien)) {
563 if (!$texte) {
564 $texte = $lien;
565 }
566 $lien = "mailto:" . $lien;
567 }
568 }
569
570 if ($pour == 'url') {
571 return $lien;
572 }
573
574 if ($pour == 'titre') {
575 return $texte;
576 }
577
578 return array('url' => $lien, 'titre' => $texte);
579 }
580
581 function liens_implicite_glose_dist($texte, $id, $type, $args, $ancre, $connect = '') {
582 if (function_exists($f = 'glossaire_' . $ancre)) {
583 $url = $f($texte, $id);
584 } else {
585 $url = glossaire_std($texte);
586 }
587
588 return $url;
589 }
590
591 /**
592 * Transformer un lien raccourci art23 en son URL
593 * Par defaut la fonction produit une url prive si on est dans le prive
594 * ou publique si on est dans le public.
595 * La globale lien_implicite_cible_public permet de forcer un cas ou l'autre :
596 * $GLOBALS['lien_implicite_cible_public'] = true;
597 * => tous les liens raccourcis pointent vers le public
598 * $GLOBALS['lien_implicite_cible_public'] = false;
599 * => tous les liens raccourcis pointent vers le prive
600 * unset($GLOBALS['lien_implicite_cible_public']);
601 * => retablit le comportement automatique
602 *
603 * https://code.spip.net/@traiter_lien_implicite
604 *
605 * @param string $ref
606 * @param string $texte
607 * @param string $pour
608 * @param string $connect
609 * @return array|bool|string
610 */
611 function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '') {
612 $cible = ($connect ? $connect : (isset($GLOBALS['lien_implicite_cible_public']) ? $GLOBALS['lien_implicite_cible_public'] : null));
613 if (!($match = typer_raccourci($ref))) {
614 return false;
615 }
616
617 @list($type, , $id, , $args, , $ancre) = $match;
618
619 # attention dans le cas des sites le lien doit pointer non pas sur
620 # la page locale du site, mais directement sur le site lui-meme
621 $url = '';
622 if ($f = charger_fonction("implicite_$type", "liens", true)) {
623 $url = $f($texte, $id, $type, $args, $ancre, $connect);
624 }
625
626 if (!$url) {
627 $url = generer_url_entite($id, $type, $args, $ancre, $cible);
628 }
629
630 if (!$url) {
631 return false;
632 }
633
634 if (is_array($url)) {
635 @list($type, $id) = $url;
636 $url = generer_url_entite($id, $type, $args, $ancre, $cible);
637 }
638
639 if ($pour === 'url') {
640 return $url;
641 }
642
643 $r = traiter_raccourci_titre($id, $type, $connect);
644 if ($r) {
645 $r['class'] = ($type == 'site') ? 'spip_out' : 'spip_in';
646 }
647
648 if ($texte = trim($texte)) {
649 $r['titre'] = $texte;
650 }
651
652 if (!@$r['titre']) {
653 $r['titre'] = _T($type) . " $id";
654 }
655
656 if ($pour == 'titre') {
657 return $r['titre'];
658 }
659
660 $r['url'] = $url;
661
662 // dans le cas d'un lien vers un doc, ajouter le type='mime/type'
663 if (
664 $type == 'document'
665 and $mime = sql_getfetsel('mime_type', 'spip_types_documents',
666 "extension IN (" . sql_get_select("extension", "spip_documents", "id_document=" . sql_quote($id)) . ")",
667 '', '', '', '', $connect)
668 ) {
669 $r['mime'] = $mime;
670 }
671
672 return $r;
673 }
674
675 // analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
676
677 define('_RACCOURCI_URL', '/^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$/S');
678
679 // https://code.spip.net/@typer_raccourci
680 function typer_raccourci($lien) {
681 if (!preg_match(_RACCOURCI_URL, $lien, $match)) {
682 return array();
683 }
684
685 $f = $match[1];
686 // valeur par defaut et alias historiques
687 if (!$f) {
688 $f = 'article';
689 } else {
690 if ($f == 'art') {
691 $f = 'article';
692 } else {
693 if ($f == 'br') {
694 $f = 'breve';
695 } else {
696 if ($f == 'rub') {
697 $f = 'rubrique';
698 } else {
699 if ($f == 'aut') {
700 $f = 'auteur';
701 } else {
702 if ($f == 'doc' or $f == 'im' or $f == 'img' or $f == 'image' or $f == 'emb') {
703 $f = 'document';
704 } else {
705 if (preg_match('/^br..?ve$/S', $f)) {
706 $f = 'breve'; # accents :(
707 }
708 }
709 }
710 }
711 }
712 }
713 }
714
715 $match[0] = $f;
716
717 return $match;
718 }
719
720 /**
721 * Retourne le titre et la langue d'un objet éditorial
722 *
723 * @param int $id Identifiant de l'objet
724 * @param string $type Type d'objet
725 * @param string|null $connect Connecteur SQL utilisé
726 * @return array {
727 * @var string $titre Titre si présent, sinon ''
728 * @var string $lang Langue si présente, sinon ''
729 * }
730 **/
731 function traiter_raccourci_titre($id, $type, $connect = null) {
732 $trouver_table = charger_fonction('trouver_table', 'base');
733 $desc = $trouver_table(table_objet($type));
734
735 if (!($desc and $s = $desc['titre'])) {
736 return array();
737 }
738
739 $_id = $desc['key']['PRIMARY KEY'];
740 $r = sql_fetsel($s, $desc['table'], "$_id=$id", '', '', '', '', $connect);
741
742 if (!$r) {
743 return array();
744 }
745
746 $r['titre'] = supprimer_numero($r['titre']);
747
748 if (!$r['titre'] and !empty($r['surnom'])) {
749 $r['titre'] = $r['surnom'];
750 }
751
752 if (!isset($r['lang'])) {
753 $r['lang'] = '';
754 }
755
756 return $r;
757 }
758
759 // traite les modeles (dans la fonction typo), en remplacant
760 // le raccourci <modeleN|parametres> par la page calculee a
761 // partir du squelette modeles/modele.html
762 // Le nom du modele doit faire au moins trois caracteres (evite <h2>)
763 // Si $doublons==true, on repere les documents sans calculer les modeles
764 // mais on renvoie les params (pour l'indexation par le moteur de recherche)
765 // https://code.spip.net/@traiter_modeles
766 define('_PREG_MODELE',
767 '(<([a-z_-]{3,})' # <modele
768 . '\s*([0-9]*)\s*' # id
769 . '([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
770 . '\s*/?' . '>)' # fin du modele >
771 );
772
773 define('_RACCOURCI_MODELE',
774 _PREG_MODELE
775 . '\s*(<\/a>)?' # eventuel </a>
776 );
777
778 define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE . '@isS');
779
780 // https://code.spip.net/@traiter_modeles
781 function traiter_modeles($texte, $doublons = false, $echap = '', $connect = '', $liens = null, $env = array()) {
782 // preserver la compatibilite : true = recherche des documents
783 if ($doublons === true) {
784 $doublons = array('documents' => array('doc', 'emb', 'img'));
785 }
786
787 // detecter les modeles (rapide)
788 if (
789 strpos($texte, "<") !== false
790 and preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS', $texte, $matches, PREG_SET_ORDER)
791 ) {
792 include_spip('public/assembler');
793 $wrap_embed_html = charger_fonction("wrap_embed_html", "inc", true);
794
795 // Recuperer l'appel complet (y compris un eventuel lien)
796 foreach ($matches as $match) {
797 $a = strpos($texte, $match[0]);
798 preg_match(_RACCOURCI_MODELE_DEBUT, substr($texte, $a), $regs);
799
800 // s'assurer qu'il y a toujours un 5e arg, eventuellement vide
801 while (count($regs) < 6) {
802 $regs[] = "";
803 }
804
805 list(, $mod, $type, $id, $params, $fin) = $regs;
806
807 if (
808 $fin
809 and preg_match('/<a\s[^<>]*>\s*$/i', substr($texte, 0, $a), $r)
810 ) {
811 $lien = array(
812 'href' => extraire_attribut($r[0], 'href'),
813 'class' => extraire_attribut($r[0], 'class'),
814 'mime' => extraire_attribut($r[0], 'type'),
815 'title' => extraire_attribut($r[0], 'title'),
816 'hreflang' => extraire_attribut($r[0], 'hreflang')
817 );
818 $n = strlen($r[0]);
819 $a -= $n;
820 $cherche = $n + strlen($regs[0]);
821 } else {
822 $lien = false;
823 $cherche = strlen($mod);
824 }
825
826 // calculer le modele
827 # hack indexation
828 if ($doublons) {
829 $texte .= preg_replace(',[|][^|=]*,s', ' ', $params);
830 } # version normale
831 else {
832 // si un tableau de liens a ete passe, reinjecter le contenu d'origine
833 // dans les parametres, plutot que les liens echappes
834 if (!is_null($liens)) {
835 $params = str_replace($liens[0], $liens[1], $params);
836 }
837
838 $modele = inclure_modele($type, $id, $params, $lien, $connect, $env);
839
840 // en cas d'echec,
841 // si l'objet demande a une url,
842 // creer un petit encadre vers elle
843 if ($modele === false) {
844 $modele = substr($texte, $a, $cherche);
845
846 if (!is_null($liens)) {
847 $modele = str_replace($liens[0], $liens[1], $modele);
848 }
849
850 $contexte = array_merge($env, array('id' => $id, 'type' => $type, 'modele' => $modele));
851
852 if ($lien) {
853 # un eventuel guillemet (") sera reechappe par #ENV
854 $contexte['lien'] = str_replace("&quot;", '"', $lien['href']);
855 $contexte['lien_class'] = $lien['class'];
856 $contexte['lien_mime'] = $lien['mime'];
857 $contexte['lien_title'] = $lien['title'];
858 $contexte['lien_hreflang'] = $lien['hreflang'];
859 }
860
861 $modele = recuperer_fond("modeles/dist", $contexte, array(), $connect);
862 }
863 // le remplacer dans le texte
864 if ($modele !== false) {
865 $modele = protege_js_modeles($modele);
866 if ($wrap_embed_html) {
867 $modele = $wrap_embed_html($mod, $modele);
868 }
869 $rempl = code_echappement($modele, $echap);
870 $texte = substr($texte, 0, $a)
871 . $rempl
872 . substr($texte, $a + $cherche);
873 }
874 }
875
876 // hack pour tout l'espace prive
877 if (((!_DIR_RESTREINT) or ($doublons)) and ($id)) {
878 foreach ($doublons ? $doublons : array('documents' => array('doc', 'emb', 'img')) as $quoi => $modeles) {
879 if (in_array(strtolower($type), $modeles)) {
880 $GLOBALS["doublons_{$quoi}_inclus"][] = $id;
881 }
882 }
883 }
884 }
885 }
886
887 return $texte;
888 }
889
890 //
891 // Raccourcis ancre [#ancre<-]
892 //
893
894 define('_RACCOURCI_ANCRE', "/\[#?([^][]*)<-\]/S");
895
896 // https://code.spip.net/@traiter_raccourci_ancre
897 function traiter_raccourci_ancre($letexte) {
898 if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER)) {
899 foreach ($m as $regs) {
900 $letexte = str_replace(
901 $regs[0],
902 '<a ' . (html5_permis() ? 'id' : 'name') . '="' . entites_html($regs[1]) . '"></a>',
903 $letexte
904 );
905 }
906 }
907
908 return $letexte;
909 }
910
911 //
912 // Raccourcis automatiques [?SPIP] vers un glossaire
913 // Wikipedia par defaut, avec ses contraintes techniques
914 // cf. http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Conventions_sur_les_titres
915
916 define('_RACCOURCI_GLOSSAIRE', "/\[\?+\s*([^][<>]+)\]/S");
917 define('_RACCOURCI_GLOSES', '/^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$/S');
918
919 // https://code.spip.net/@traiter_raccourci_glossaire
920 function traiter_raccourci_glossaire($texte) {
921 if (!preg_match_all(_RACCOURCI_GLOSSAIRE, $texte, $matches, PREG_SET_ORDER)) {
922 return $texte;
923 }
924
925 include_spip('inc/charsets');
926 $lien = charger_fonction('lien', 'inc');
927
928 // Eviter les cas particulier genre "[?!?]"
929 // et isoler le lexeme a gloser de ses accessoires
930 // (#:url du glossaire, | bulle d'aide, {} hreflang)
931 // Transformation en pseudo-raccourci pour passer dans inc_lien
932 foreach ($matches as $regs) {
933 if (preg_match(_RACCOURCI_GLOSES, $regs[1], $r)) {
934 preg_match('/^(.*?)(\d*)$/', $r[4], $m);
935 $_n = intval($m[2]);
936 $gloss = $m[1] ? ('#' . $m[1]) : '';
937 $t = $r[1] . $r[2] . $r[5];
938 list($t, $bulle, $hlang) = traiter_raccourci_lien_atts($t);
939
940 if ($bulle === false) {
941 $bulle = $m[1];
942 }
943
944 $t = unicode2charset(charset2unicode($t), 'utf-8');
945 $ref = $lien("glose$_n$gloss", $t, 'spip_glossaire', $bulle, $hlang);
946 $texte = str_replace($regs[0], $ref, $texte);
947 }
948 }
949
950 return $texte;
951 }
952
953 // https://code.spip.net/@glossaire_std
954 function glossaire_std($terme) {
955 global $url_glossaire_externe;
956 static $pcre = null;
957
958 if ($pcre === null) {
959 $pcre = isset($GLOBALS['meta']['pcre_u']) ? $GLOBALS['meta']['pcre_u'] : '';
960
961 if (strpos($url_glossaire_externe, "%s") === false) {
962 $url_glossaire_externe .= '%s';
963 }
964 }
965
966 $glosateur = str_replace(
967 "@lang@",
968 $GLOBALS['spip_lang'],
969 $GLOBALS['url_glossaire_externe']
970 );
971
972 $terme = rawurlencode(preg_replace(',\s+,' . $pcre, '_', $terme));
973
974 return str_replace("%s", $terme, $glosateur);
975 }