source: spip-zone/_core_/plugins/textwheel/inc/lien.php

Last change on this file was 113294, checked in by spip.franck@…, 2 months ago

Il parait que le futur c'est maintenant :-D

File size: 27.4 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2019                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13if (!defined('_ECRIRE_INC_VERSION')) {
14        return;
15}
16
17include_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 */
37function 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 */
212function 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
251define('_RACCOURCI_LIEN', "/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS");
252
253// https://code.spip.net/@expanser_liens
254function 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
283function 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 */
352function 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
422define('_RACCOURCI_ATTRIBUTS', '/^((?:[^[]*?(?:\[[^]]*\])?)*?)([|]([^<>]*?))?([{]([a-z_]*)[}])?$/');
423
424// https://code.spip.net/@traiter_raccourci_lien_atts
425function 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
479define('_EXTRAIRE_DOMAINE', '/^(?:(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z0-9]{2,6}|localhost)\b/Si');
480define('_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 */
502function 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
527function 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
534define('_EXTRAIRE_LIEN', ",^\s*(http:?/?/?|mailto:?)\s*$,iS");
535
536// https://code.spip.net/@traiter_lien_explicite
537function 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
581function 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 */
611function 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
677define('_RACCOURCI_URL', '/^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$/S');
678
679// https://code.spip.net/@typer_raccourci
680function 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 **/
731function 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
766define('_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
773define('_RACCOURCI_MODELE',
774        _PREG_MODELE
775        . '\s*(<\/a>)?' # eventuel </a>
776);
777
778define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE . '@isS');
779
780// https://code.spip.net/@traiter_modeles
781function 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
894define('_RACCOURCI_ANCRE', "/\[#?([^][]*)<-\]/S");
895
896// https://code.spip.net/@traiter_raccourci_ancre
897function 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
916define('_RACCOURCI_GLOSSAIRE', "/\[\?+\s*([^][<>]+)\]/S");
917define('_RACCOURCI_GLOSES', '/^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$/S');
918
919// https://code.spip.net/@traiter_raccourci_glossaire
920function 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
954function 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}
Note: See TracBrowser for help on using the repository browser.