[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / textwheel / inc / lien.php
index 91da19b..b54edff 100644 (file)
  *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
-if (!defined('_ECRIRE_INC_VERSION')) return;
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
 
 include_spip('base/abstract_sql');
 
-//
-// Production de la balise A+href a partir des raccourcis [xxx->url] etc.
-// Note : complique car c'est ici qu'on applique typo(),
-// et en plus on veut pouvoir les passer en pipeline
-//
-
-function inc_lien_dist($lien, $texte='', $class='', $title='', $hlang='', $rel='', $connect='', $env=array()) {
-       static $u=null;
-       if (!$u) $u=url_de_base();
+/**
+ * Production de la balise a+href à partir des raccourcis `[xxx->url]` etc.
+ *
+ * @note
+ *     Compliqué car c'est ici qu'on applique typo(),
+ *     et en plus, on veut pouvoir les passer en pipeline
+ *
+ * @see typo()
+ * @param string $lien
+ * @param string $texte
+ * @param string $class
+ * @param string $title
+ * @param string $hlang
+ * @param string $rel
+ * @param string $connect
+ * @param array $env
+ * @return string
+ */
+function inc_lien_dist(
+       $lien,
+       $texte = '',
+       $class = '',
+       $title = '',
+       $hlang = '',
+       $rel = '',
+       $connect = '',
+       $env = array()
+) {
+       static $u = null;
+       if (!$u) {
+               $u = url_de_base();
+       }
        $typo = false;
 
        // Si une langue est demandee sur un raccourci d'article, chercher
@@ -30,81 +55,195 @@ function inc_lien_dist($lien, $texte='', $class='', $title='', $hlang='', $rel='
        // - [{en}->art2] => traduction anglaise de l'article 2, sinon art 2
        // - [{}->art2] => traduction en langue courante de l'art 2, sinon art 2
        // s'applique a tout objet traduit
-       if ($hlang
-       AND $match = typer_raccourci($lien)) { 
-               @list($type,,$id,,$args,,$ancre) = $match;
+       if (
+               $hlang
+               and $match = typer_raccourci($lien)
+       ) {
+               @list($type, , $id, , $args, , $ancre) = $match;
                $trouver_table = charger_fonction('trouver_table', 'base');
-               $desc = $trouver_table(table_objet($type, $connect),$connect);
-               if ($desc
-                       AND $id_table_objet = $desc['key']['PRIMARY KEY']) {
+               $desc = $trouver_table(table_objet($type, $connect), $connect);
+               if (
+                       $desc
+                       and $id_table_objet = $desc['key']['PRIMARY KEY']
+               ) {
                        $table_objet_sql = $desc['table'];
-                       if ($row=sql_fetsel('*', $table_objet_sql, "$id_table_objet=".intval($id))
-                               AND isset($row['id_trad'])
-                               AND isset($row['lang'])
-                               AND $id_dest = sql_getfetsel($id_table_objet, $table_objet_sql,"id_trad=".intval($row['id_trad'])." AND lang=" . sql_quote($hlang))
-                               AND objet_test_si_publie($type,$id_dest)
-                       )
+                       if (
+                               $row = sql_fetsel('*', $table_objet_sql, "$id_table_objet=" . intval($id))
+                               and isset($row['id_trad'])
+                               and isset($row['lang'])
+                               and $id_dest = sql_getfetsel($id_table_objet, $table_objet_sql,
+                                       "id_trad=" . intval($row['id_trad']) . " AND lang=" . sql_quote($hlang))
+                               and objet_test_si_publie($type, $id_dest)
+                       ) {
                                $lien = "$type$id_dest";
-                       else
+                       } else {
                                $hlang = '';
-               }
-               else
+                       }
+               } else {
                        $hlang = '';
+               }
        }
 
-       $mode = ($texte AND $class) ? 'url' : 'tout';
+       $mode = ($texte and $class) ? 'url' : 'tout';
+       $lang = '';
        $lien = calculer_url($lien, $texte, $mode, $connect);
        if ($mode === 'tout') {
                $texte = $lien['titre'];
-               if (!$class AND isset($lien['class'])) $class = $lien['class'];
-               $lang = isset($lien['lang']) ?$lien['lang'] : '';
-               $mime = isset($lien['mime']) ? " type='".$lien['mime']."'" : "";
+               if (!$class and isset($lien['class'])) {
+                       $class = $lien['class'];
+               }
+               $lang = isset($lien['lang']) ? $lien['lang'] : '';
+               $mime = isset($lien['mime']) ? " type='" . $lien['mime'] . "'" : "";
                $lien = $lien['url'];
        }
 
        $lien = trim($lien);
-       if (strncmp($lien,"#",1) == 0)  # ancres pures (internes a la page)
+       if (strncmp($lien, "#", 1) == 0) {  # ancres pures (internes a la page)
                $class = 'spip_ancre';
-       elseif (strncasecmp($lien,'mailto:',7)==0) # pseudo URL de mail
+       } elseif (strncasecmp($lien, 'mailto:', 7) == 0) { # pseudo URL de mail
                $class = "spip_mail";
-       elseif (strncmp($texte,'<html>',6)==0) # cf traiter_lien_explicite
-               $class = "spip_url spip_out";
-       elseif (!$class) $class = "spip_out"; # si pas spip_in|spip_glossaire
+       } elseif (strncmp($texte, '<html>', 6) == 0) { # cf traiter_lien_explicite
+               $class = "spip_url";
+               # spip_out sur les URLs externes
+               if (
+                       preg_match(',^\w+://,iS', $lien)
+                       and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
+               ) {
+                       $class .= " spip_out";
+               }
+       } elseif (!$class) {
+               # spip_out sur les URLs externes
+               if (
+                       preg_match(',^\w+://,iS', $lien)
+                       and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
+               ) {
+                       $class = "spip_out"; # si pas spip_in|spip_glossaire
+               }
+       }
+       if ($class) {
+               $class = " class='$class'";
+       }
 
        // Si l'objet n'est pas de la langue courante, on ajoute hreflang
-       if (!$hlang AND $lang!==$GLOBALS['spip_lang'])
+       if (!$hlang and isset($lang) and $lang !== $GLOBALS['spip_lang']) {
                $hlang = $lang;
+       }
 
        $lang = ($hlang ? " hreflang='$hlang'" : '');
 
-       if ($title)
-               $title = ' title="'.attribut_html($title).'"';
-       else
+       if ($title) {
+               $title = ' title="' . attribut_html($title) . '"';
+       } else {
                $title = ''; // $title peut etre 'false'
+       }
+
        // rel=external pour les liens externes
-       if ((strncmp($lien,'http://',7)==0 OR strncmp($lien,'https://',8)==0)
-         AND strncmp("$lien/", $u ,strlen($u))!=0)
+       if (
+               (strncmp($lien, 'http://', 7) == 0 or strncmp($lien, 'https://', 8) == 0)
+               and strncmp("$lien/", $u, strlen($u)) != 0
+       ) {
                $rel = trim("$rel external");
-       if ($rel) $rel = " rel='$rel'";
+       }
+       if ($rel) {
+               $rel = " rel='$rel'";
+       }
+
+       $lang_objet_prev = '';
+       if ($hlang and $hlang !== $GLOBALS['spip_lang']) {
+               $lang_objet_prev = isset($GLOBALS['lang_objet']) ? $GLOBALS['lang_objet'] : null;
+               $GLOBALS['lang_objet'] = $hlang;
+       }
 
        // si pas de modele dans le texte du lien, on peut juste passer typo sur le texte, c'est plus rapide
        // les rares cas de lien qui encapsule un modele passe en dessous, c'est plus lent
-       if (traiter_modeles($texte, false, '', $connect, null, $env)==$texte){
+       if (traiter_modeles($texte, false, '', $connect, null, $env) == $texte) {
                $texte = typo($texte, true, $connect, $env);
-               $lien = "<a href=\"".str_replace('"', '&quot;', $lien)."\" class='$class'$lang$title$rel".(isset($mime)?$mime:'').">$texte</a>";
+               $lien = "<a href=\"" . str_replace('"', '&quot;',
+                               $lien) . "\"$class$lang$title$rel" . (isset($mime) ? $mime : '') . ">$texte</a>";
+               if ($lang_objet_prev !== '') {
+                       if ($lang_objet_prev) {
+                               $GLOBALS['lang_objet'] = $lang_objet_prev;
+                       } else {
+                               unset($GLOBALS['lang_objet']);
+                       }
+               }
+
                return $lien;
        }
+
        # ceci s'execute heureusement avant les tableaux et leur "|".
        # Attention, le texte initial est deja echappe mais pas forcement
        # celui retourne par calculer_url.
        # Penser au cas [<imgXX|right>->URL], qui exige typo('<a>...</a>')
-       $lien = "<a href=\"".str_replace('"', '&quot;', $lien)."\" class='$class'$lang$title$rel$mime>$texte</a>";
-       $res = typo($lien, true, $connect, $env);
+       $lien = "<a href=\"" . str_replace('"', '&quot;', $lien) . "\"$class$lang$title$rel$mime>$texte</a>";
+       #$res = typo($lien, true, $connect, $env);
+       $p = $GLOBALS['toujours_paragrapher'];
+       $GLOBALS['toujours_paragrapher'] = false;
+       $res = propre($lien, $connect, $env);
+       $GLOBALS['toujours_paragrapher'] = $p;
+
        // dans ce cas, echapons le resultat du modele pour que propre etc ne viennent pas pouicher le html
        $res = echappe_html("<html>$res</html>");
+       if ($lang_objet_prev !== '') {
+               if ($lang_objet_prev) {
+                       $GLOBALS['lang_objet'] = $lang_objet_prev;
+               } else {
+                       unset($GLOBALS['lang_objet']);
+               }
+       }
+
        return $res;
 }
 
+/**
+ * Générer le HTML d'un lien quelconque
+ *
+ * Cette fonction génère une balise `<a>` suivant de multiples arguments.
+ *
+ * @param array $args
+ *   Tableau des arguments disponibles pour générer le lien :
+ *   - texte : texte du lien, seul argument qui n'est pas un attribut
+ *   - href
+ *   - name
+ *   - etc, tout autre attribut supplémentaire…
+ * @return string
+ *   Retourne une balise HTML de lien ou une chaîne vide.
+ */
+function balise_a($args = array()) {
+       $balise_a = '';
+
+       // Il faut soit au minimum un href OU un name pour réussir à générer quelque chose
+       if (is_array($args) and (isset($args['href']) or isset($args['name']))) {
+               include_spip('inc/filtres');
+               $texte = '';
+
+               // S'il y a un texte, on le récupère et on l'enlève des attributs
+               if (isset($args['texte']) and is_scalar($args['texte'])) {
+                       $texte = $args['texte'];
+                       unset($args['texte']);
+               } // Si on a un href sans texte, on en construit un avec l'URL
+               elseif (isset($args['href']) and is_scalar($args['href'])) {
+                       static $lien_court;
+                       if (!$lien_court) {
+                               $lien_court = charger_fonction('lien_court', 'inc');
+                       }
+                       $texte = quote_amp($lien_court($args['href']));
+               }
+
+               // Il ne reste normalement plus que des attributs, on les ajoute à la balise
+               $balise_a = '<a';
+               foreach ($args as $attribut => $valeur) {
+                       if (is_scalar($valeur) and !empty($valeur)) {
+                               $balise_a .= ' ' . $attribut . '="' . attribut_html($valeur) . '"';
+                       }
+               }
+               // Puis on ajoute le texte
+               $balise_a .= '>' . $texte . '</a>';
+       }
+
+       return $balise_a;
+}
+
 // Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip
 // Laisser passer des paires de crochets pour la balise multi
 // mais refuser plus d'imbrications ou de mauvaises imbrications
@@ -112,47 +251,58 @@ function inc_lien_dist($lien, $texte='', $class='', $title='', $hlang='', $rel='
 define('_RACCOURCI_LIEN', "/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS");
 
 // https://code.spip.net/@expanser_liens
-function expanser_liens($t, $connect='', $env=array())
-{
-
+function expanser_liens($t, $connect = '', $env = array()) {
        $t = pipeline('pre_liens', $t);
 
-       expanser_un_lien($connect,'init', $env);
+       if (strpos($t, '\[') !== false or strpos($t, '\]') !== false) {
+               $t = str_replace(array('\[', '\]'), array("\x1\x5", "\x1\x6"), $t);
+       }
+
+       expanser_un_lien($connect, 'init', $env);
 
-       if (strpos($t, '->') !== false)
-               $t = preg_replace_callback (_RACCOURCI_LIEN, 'expanser_un_lien',$t);
+       if (strpos($t, '->') !== false) {
+               $t = preg_replace_callback(_RACCOURCI_LIEN, 'expanser_un_lien', $t);
+       }
 
        // on passe a traiter_modeles la liste des liens reperes pour lui permettre
        // de remettre le texte d'origine dans les parametres du modele
-       $t = traiter_modeles($t, false, false, $connect, expanser_un_lien('','sources'), $env);
+       $t = traiter_modeles($t, false, false, $connect, expanser_un_lien('', 'sources'), $env);
+
+       if (strpos($t, "\x1") !== false) {
+               $t = str_replace(array("\x1\x5", "\x1\x6"), array('[', ']'), $t);
+       }
 
-       $t = corriger_typo($t);
+       $t = corriger_typo($t);
 
-       $t = expanser_un_lien($t,'reinsert');
+       $t = expanser_un_lien($t, 'reinsert');
 
        return $t;
 }
 
 
-function expanser_un_lien($reg, $quoi='echappe', $env=null){
+function expanser_un_lien($reg, $quoi = 'echappe', $env = null) {
        static $pile = array();
        static $inserts;
        static $sources;
        static $regs;
        static $k = 0;
        static $lien;
-       static $connect='';
+       static $connect = '';
        static $contexte = array();
 
-       switch ($quoi){
+       switch ($quoi) {
                case 'init':
-                       if (!$lien) $lien = charger_fonction('lien', 'inc');
-                       if (!is_null($env))
+                       if (!$lien) {
+                               $lien = charger_fonction('lien', 'inc');
+                       }
+                       if (!is_null($env)) {
                                $contexte = $env;
-                       array_push($pile,array($inserts,$sources,$regs,$connect,$k));
+                       }
+                       array_push($pile, array($inserts, $sources, $regs, $connect, $k));
                        $inserts = $sources = $regs = array();
                        $connect = $reg; // stocker le $connect pour les appels a inc_lien_dist
-                       $k=0;
+                       $k = 0;
+
                        return;
                        break;
                case 'echappe':
@@ -164,21 +314,24 @@ function expanser_un_lien($reg, $quoi='echappe', $env=null){
                        $r = end($reg);
                        // la mise en lien automatique est passee par la a tort !
                        // corrigeons pour eviter d'avoir un <a...> dans un href...
-                       if (strncmp($r,'<a',2)==0){
+                       if (strncmp($r, '<a', 2) == 0) {
                                $href = extraire_attribut($r, 'href');
                                // remplacons dans la source qui peut etre reinjectee dans les arguments
                                // d'un modele
-                               $sources[$k] = str_replace($r,$href,$sources[$k]);
+                               $sources[$k] = str_replace($r, $href, $sources[$k]);
                                // et prenons le href comme la vraie url a linker
                                $r = $href;
                        }
                        $regs[$k] = $lien($r, $titre, '', $bulle, $hlang, '', $connect, $contexte);
+
                        return $inserts[$k++];
                        break;
                case 'reinsert':
-                       if (count($inserts))
+                       if (count($inserts)) {
                                $reg = str_replace($inserts, $regs, $reg);
-                       list($inserts,$sources,$regs,$connect,$k) = array_pop($pile);
+                       }
+                       list($inserts, $sources, $regs, $connect, $k) = array_pop($pile);
+
                        return $reg;
                        break;
                case 'sources':
@@ -187,23 +340,47 @@ function expanser_un_lien($reg, $quoi='echappe', $env=null){
        }
 }
 
-// Meme analyse mais pour eliminer les liens
-// et ne laisser que leur titre, a expliciter si ce n'est fait
-// https://code.spip.net/@nettoyer_raccourcis_typo
-function nettoyer_raccourcis_typo($texte, $connect='')
-{
-       $texte = pipeline('nettoyer_raccourcis_typo',$texte);
+/**
+ * Nettoie un texte en enlevant les raccourcis typo, sans les traiter
+ *
+ * On ne laisse que les titres des liens, en les explicitant si ce n’est pas fait.
+ *
+ * @param string $texte
+ * @param string $connect
+ * @return string
+ */
+function nettoyer_raccourcis_typo($texte, $connect = '') {
+       $texte = pipeline('nettoyer_raccourcis_typo', $texte);
+
+       // on utilise les \r pour passer entre les gouttes
+       $texte = str_replace("\r\n", "\n", $texte);
+       $texte = str_replace("\r", "\n", $texte);
+
+       // sauts de ligne et paragraphes
+       $texte = preg_replace("/\n\n+/", "\r", $texte);
+
+       // supprimer les traits, lignes etc
+       $texte = preg_replace("/(^|\r|\n)(-[-#\*]*\s?|_ )/", "\n", $texte);
 
-       if (preg_match_all(_RACCOURCI_LIEN, $texte, $regs, PREG_SET_ORDER))
+       // travailler en accents charset
+       $texte = unicode2charset(html2unicode($texte, true /* secure */ ));
+
+       if (preg_match_all(_RACCOURCI_LIEN, $texte, $regs, PREG_SET_ORDER)) {
                include_spip('inc/texte');
                foreach ($regs as $reg) {
-                       list ($titre,,)= traiter_raccourci_lien_atts($reg[1]);
+                       list($titre, , ) = traiter_raccourci_lien_atts($reg[1]);
                        if (!$titre) {
-                               $match = typer_raccourci($reg[count($reg)-1]);
-                               @list($type,,$id,,,,) = $match;
+                               $match = typer_raccourci($reg[count($reg) - 1]);
+                               if (!isset($match[0])) {
+                                       $match[0] = '';
+                               }
+                               @list($type, , $id, , , , ) = $match;
+
                                if ($type) {
-                                       $url = generer_url_entite($id,$type,'','',true);
-                                       if (is_array($url)) list($type, $id) = $url;
+                                       $url = generer_url_entite($id, $type, '', '', true);
+                                       if (is_array($url)) {
+                                               list($type, $id) = $url;
+                                       }
                                        $titre = traiter_raccourci_titre($id, $type, $connect);
                                }
                                $titre = $titre ? $titre['titre'] : $match[0];
@@ -211,24 +388,30 @@ function nettoyer_raccourcis_typo($texte, $connect='')
                        $titre = corriger_typo(supprimer_tags($titre));
                        $texte = str_replace($reg[0], $titre, $texte);
                }
+       }
 
        // supprimer les ancres
-       $texte = preg_replace(_RACCOURCI_ANCRE,"",$texte);
+       $texte = preg_replace(_RACCOURCI_ANCRE, "", $texte);
 
        // supprimer les notes
-       $texte = preg_replace(",[[][[]([^]]|[]][^]])*[]][]],UimsS","",$texte);
+       $texte = preg_replace(",\[\[.*\]\],UimsS", "", $texte);
 
        // supprimer les codes typos
-       $texte = str_replace(array('}','{'), '', $texte);
+       $texte = str_replace(array('}', '{'), '', $texte);
 
        // supprimer les tableaux
-       $texte = preg_replace(",(^|\r)\|.*\|\r,s", "\r", $texte);
+       $texte = preg_replace(",(?:^|\r|\n)\|.*\|(?:\r|\n|$),s", "\r", $texte);
+
+       // indiquer les sauts de paragraphes
+       $texte = str_replace("\r", "\n\n", $texte);
+       $texte = str_replace("\n\n+", "\n\n", $texte);
+
+       $texte = trim($texte);
 
        return $texte;
 }
 
 
-
 // Repere dans la partie texte d'un raccourci [texte->...]
 // la langue et la bulle eventuelles : [texte|title{lang}->...]
 // accepte un niveau de paire de crochets dans le texte :
@@ -240,75 +423,99 @@ define('_RACCOURCI_ATTRIBUTS', '/^((?:[^[]*?(?:\[[^]]*\])?)*?)([|]([^<>]*?))?([{
 
 // https://code.spip.net/@traiter_raccourci_lien_atts
 function traiter_raccourci_lien_atts($texte) {
-
        $bulle = $hlang = false;
+
        // title et hreflang donnes par le raccourci ?
-       if (strpbrk($texte, "|{") !== false AND
-         preg_match(_RACCOURCI_ATTRIBUTS, $texte, $m)) {
+       if (
+               strpbrk($texte, "|{") !== false
+               and preg_match(_RACCOURCI_ATTRIBUTS, $texte, $m)
+       ) {
+               $n = count($m);
 
-               $n =count($m);
                // |infobulle ?
                if ($n > 2) {
                        $bulle = $m[3];
+
                        // {hreflang} ?
                        if ($n > 4) {
-                       // si c'est un code de langue connu, on met un hreflang
+                               // si c'est un code de langue connu, on met un hreflang
                                if (traduire_nom_langue($m[5]) <> $m[5]) {
                                        $hlang = $m[5];
                                } elseif (!$m[5]) {
                                        $hlang = test_espace_prive() ?
-                                         $GLOBALS['lang_objet'] : $GLOBALS['spip_lang'];
-                               // sinon c'est un italique
+                                               $GLOBALS['lang_objet'] : $GLOBALS['spip_lang'];
+                                       // sinon c'est un italique ou un gras dans le title ou dans le texte du lien
                                } else {
-                                       $m[1] .= $m[4];
+                                       if ($bulle) {
+                                               $bulle .= $m[4];
+                                       } else {
+                                               $m[1] .= $m[2] . $m[4];
+                                       }
                                }
-
+                       }
                        // S'il n'y a pas de hreflang sous la forme {}, ce qui suit le |
                        // est peut-etre une langue
-                       } else if (preg_match('/^[a-z_]+$/', $m[3])) {
-                       // si c'est un code de langue connu, on met un hreflang
-                       // mais on laisse le title (c'est arbitraire tout ca...)
-                               if (traduire_nom_langue($m[3]) <> $m[3]) {
-                                 $hlang = $m[3];
+                       else {
+                               if (preg_match('/^[a-z_]+$/', $m[3])) {
+                                       // si c'est un code de langue connu, on met un hreflang
+                                       // mais on laisse le title (c'est arbitraire tout ca...)
+                                       if (traduire_nom_langue($m[3]) <> $m[3]) {
+                                               $hlang = $m[3];
+                                       }
                                }
                        }
                }
                $texte = $m[1];
        }
 
+       if ($bulle) {
+               $bulle = nettoyer_raccourcis_typo($bulle);
+               $bulle = corriger_typo($bulle);
+       }
+
        return array(trim($texte), $bulle, $hlang);
 }
 
 define('_EXTRAIRE_DOMAINE', '/^(?:(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z0-9]{2,6}|localhost)\b/Si');
-
 define('_RACCOURCI_CHAPO', '/^(\W*)(\W*)(\w*\d+([?#].*)?)$/');
+
 /**
- * Fonction pour les champs virtuels de redirection qui peut etre:
- * 1. un raccourci Spip habituel (premier If) [texte->TYPEnnn]
- * 2. un ultra raccourci TYPEnnn voire nnn (article) (deuxieme If)
- * 3. une URL std
- *
- * renvoie l'url reelle de redirection si le $url=true,
- * l'url brute contenue dans le chapo sinon
- *
- * https://code.spip.net/@chapo_redirige
+ * Retourne la valeur d'un champ de redirection (articles virtuels)
+ * 
+ * L'entrée accepte plusiers types d'écritures : 
+ * - une URL compète, 
+ * - un lien SPIP tel que `[Lien->article23]`, 
+ * - ou un raccourcis SPIP comme `rub2` ou `rubrique2`
  *
  * @param string $virtuel
+ *     Texte qui définit la redirection, à analyser. 
+ *     Plusieurs types peuvent être acceptés :
+ *     - un raccourci Spip habituel, tel que `[texte->TYPEnnn]`
+ *     - un ultra raccourci Spip, tel que `TYPEnnn`
+ *     - une URL standard
  * @param bool $url
+ *     false : retourne uniquement le nom du lien (TYPEnnn)
+ *     true : retourne l'URL calculée pour le lien
  * @return string
+ *     Nom du lien ou URL
  */
-function virtuel_redirige($virtuel, $url=false){
-       if (!strlen($virtuel)) return '';
-       if (!preg_match(_RACCOURCI_LIEN, $virtuel, $m))
-               if (!preg_match(_RACCOURCI_CHAPO, $virtuel, $m))
-                       return $virtuel;
+function virtuel_redirige($virtuel, $url = false) {
+       if (!strlen($virtuel)) {
+               return '';
+       }
+       if (
+               !preg_match(_RACCOURCI_LIEN, $virtuel, $m)
+               and !preg_match(_RACCOURCI_CHAPO, $virtuel, $m)
+       ) {
+               return $virtuel;
+       }
 
        return !$url ? $m[3] : traiter_lien_implicite($m[3]);
 }
 
 
 // Cherche un lien du type [->raccourci 123]
-// associe a une fonction generer_url_raccourci() definie explicitement 
+// associe a une fonction generer_url_raccourci() definie explicitement
 // ou implicitement par le jeu de type_urls courant.
 //
 // Valeur retournee selon le parametre $pour:
@@ -317,19 +524,20 @@ function virtuel_redirige($virtuel, $url=false){
 // 'url':   seulement U  (i.e. generer_url_RACCOURCI)
 
 // https://code.spip.net/@calculer_url
-function calculer_url ($ref, $texte='', $pour='url', $connect='', $echappe_typo = true) {
+function calculer_url($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
        $r = traiter_lien_implicite($ref, $texte, $pour, $connect, $echappe_typo);
        $r = ($r ? $r : traiter_lien_explicite($ref, $texte, $pour, $connect, $echappe_typo));
+
        return $r;
 }
 
 define('_EXTRAIRE_LIEN', ",^\s*(http:?/?/?|mailto:?)\s*$,iS");
 
 // https://code.spip.net/@traiter_lien_explicite
-function traiter_lien_explicite ($ref, $texte='', $pour='url', $connect='', $echappe_typo = true)
-{
-       if (preg_match(_EXTRAIRE_LIEN, $ref))
-               return ($pour != 'tout') ? '' : array('','','','');
+function traiter_lien_explicite($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
+       if (preg_match(_EXTRAIRE_LIEN, $ref)) {
+               return ($pour != 'tout') ? '' : array('', '', '', '');
+       }
 
        $lien = entites_html(trim($ref));
 
@@ -338,35 +546,45 @@ function traiter_lien_explicite ($ref, $texte='', $pour='url', $connect='', $ech
                $texte = str_replace('"', '', $lien);
                static $lien_court;
                // evite l'affichage de trop longues urls.
-               if (!$lien_court)
+               if (!$lien_court) {
                        $lien_court = charger_fonction('lien_court', 'inc');
+               }
                $texte = $lien_court($texte);
-               if ($echappe_typo){
-                       $texte = "<html>".quote_amp($texte)."</html>";
+               if ($echappe_typo) {
+                       $texte = "<html>" . quote_amp($texte) . "</html>";
                }
        }
 
        // petites corrections d'URL
-       if (preg_match('/^www\.[^@]+$/S',$lien))
-               $lien = "http://".$lien;
-       else if (strpos($lien, "@") && email_valide($lien)) {
-               if (!$texte) $texte = $lien;
-               $lien = "mailto:".$lien;
+       if (preg_match('/^www\.[^@]+$/S', $lien)) {
+               $lien = "http://" . $lien;
+       } else {
+               if (strpos($lien, "@") && email_valide($lien)) {
+                       if (!$texte) {
+                               $texte = $lien;
+                       }
+                       $lien = "mailto:" . $lien;
+               }
+       }
+
+       if ($pour == 'url') {
+               return $lien;
        }
-       
-       if ($pour == 'url') return $lien;
 
-       if ($pour == 'titre') return $texte;
+       if ($pour == 'titre') {
+               return $texte;
+       }
 
        return array('url' => $lien, 'titre' => $texte);
 }
 
-function liens_implicite_glose_dist($texte,$id,$type,$args,$ancre,$connect=''){
-       if ( ($ancre AND function_exists($f = 'glossaire_' . $ancre))
-         OR function_exists($f = 'glossaire_'))
-               $url = $f($texte, $id, $ancre);
-       else
+function liens_implicite_glose_dist($texte, $id, $type, $args, $ancre, $connect = '') {
+       if (function_exists($f = 'glossaire_' . $ancre)) {
+               $url = $f($texte, $id);
+       } else {
                $url = glossaire_std($texte);
+       }
+
        return $url;
 }
 
@@ -390,38 +608,66 @@ function liens_implicite_glose_dist($texte,$id,$type,$args,$ancre,$connect=''){
  * @param string $connect
  * @return array|bool|string
  */
-function traiter_lien_implicite ($ref, $texte='', $pour='url', $connect='')
-{
-       $cible = ($connect ? $connect : (isset($GLOBALS['lien_implicite_cible_public'])?$GLOBALS['lien_implicite_cible_public']:null));
-       if (!($match = typer_raccourci($ref))) return false;
-       @list($type,,$id,,$args,,$ancre) = $match;
-# attention dans le cas des sites le lien doit pointer non pas sur
-# la page locale du site, mais directement sur le site lui-meme
+function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '') {
+       $cible = ($connect ? $connect : (isset($GLOBALS['lien_implicite_cible_public']) ? $GLOBALS['lien_implicite_cible_public'] : null));
+       if (!($match = typer_raccourci($ref))) {
+               return false;
+       }
+
+       @list($type, , $id, , $args, , $ancre) = $match;
+
+       # attention dans le cas des sites le lien doit pointer non pas sur
+       # la page locale du site, mais directement sur le site lui-meme
        $url = '';
-       if ($f = charger_fonction("implicite_$type","liens",true))
-               $url = $f($texte,$id,$type,$args,$ancre,$connect);
-       if (!$url)
-               $url = generer_url_entite($id,$type,$args,$ancre,$cible);
-       if (!$url) return false;
+       if ($f = charger_fonction("implicite_$type", "liens", true)) {
+               $url = $f($texte, $id, $type, $args, $ancre, $connect);
+       }
+
+       if (!$url) {
+               $url = generer_url_entite($id, $type, $args, $ancre, $cible);
+       }
+
+       if (!$url) {
+               return false;
+       }
+
        if (is_array($url)) {
-               @list($type,$id) = $url;
-               $url = generer_url_entite($id,$type,$args,$ancre,$cible);
+               @list($type, $id) = $url;
+               $url = generer_url_entite($id, $type, $args, $ancre, $cible);
+       }
+
+       if ($pour === 'url') {
+               return $url;
        }
-       if ($pour === 'url') return $url;
+
        $r = traiter_raccourci_titre($id, $type, $connect);
-       if ($r) $r['class'] =  ($type == 'site')?'spip_out':'spip_in';
-       if ($texte = trim($texte)) $r['titre'] = $texte;
-       if (!@$r['titre']) $r['titre'] =  _T($type) . " $id";
-       if ($pour=='titre') return $r['titre'];
+       if ($r) {
+               $r['class'] = ($type == 'site') ? 'spip_out' : 'spip_in';
+       }
+
+       if ($texte = trim($texte)) {
+               $r['titre'] = $texte;
+       }
+
+       if (!@$r['titre']) {
+               $r['titre'] = _T($type) . " $id";
+       }
+
+       if ($pour == 'titre') {
+               return $r['titre'];
+       }
+
        $r['url'] = $url;
 
        // dans le cas d'un lien vers un doc, ajouter le type='mime/type'
-       if ($type == 'document'
-       AND $mime = sql_getfetsel('mime_type', 'spip_types_documents',
-                       "extension IN (".sql_get_select("extension","spip_documents","id_document=".sql_quote($id)).")",
-                       '','','','',$connect)
-       )
+       if (
+               $type == 'document'
+               and $mime = sql_getfetsel('mime_type', 'spip_types_documents',
+                       "extension IN (" . sql_get_select("extension", "spip_documents", "id_document=" . sql_quote($id)) . ")",
+                       '', '', '', '', $connect)
+       ) {
                $r['mime'] = $mime;
+       }
 
        return $r;
 }
@@ -431,34 +677,82 @@ function traiter_lien_implicite ($ref, $texte='', $pour='url', $connect='')
 define('_RACCOURCI_URL', '/^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$/S');
 
 // https://code.spip.net/@typer_raccourci
-function typer_raccourci ($lien) {
-       if (!preg_match(_RACCOURCI_URL, $lien, $match)) return array();
+function typer_raccourci($lien) {
+       if (!preg_match(_RACCOURCI_URL, $lien, $match)) {
+               return array();
+       }
+
        $f = $match[1];
        // valeur par defaut et alias historiques
-       if (!$f) $f = 'article';
-       else if ($f == 'art') $f = 'article';
-       else if ($f == 'br') $f = 'breve';
-       else if ($f == 'rub') $f = 'rubrique';
-       else if ($f == 'aut') $f = 'auteur';
-       else if ($f == 'doc' OR $f == 'im' OR $f == 'img' OR $f == 'image' OR $f == 'emb')
-               $f = 'document';
-       else if (preg_match('/^br..?ve$/S', $f)) $f = 'breve'; # accents :(
+       if (!$f) {
+               $f = 'article';
+       } else {
+               if ($f == 'art') {
+                       $f = 'article';
+               } else {
+                       if ($f == 'br') {
+                               $f = 'breve';
+                       } else {
+                               if ($f == 'rub') {
+                                       $f = 'rubrique';
+                               } else {
+                                       if ($f == 'aut') {
+                                               $f = 'auteur';
+                                       } else {
+                                               if ($f == 'doc' or $f == 'im' or $f == 'img' or $f == 'image' or $f == 'emb') {
+                                                       $f = 'document';
+                                               } else {
+                                                       if (preg_match('/^br..?ve$/S', $f)) {
+                                                               $f = 'breve'; # accents :(
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
        $match[0] = $f;
+
        return $match;
 }
 
-// Retourne le champ textuel associe a une cle primaire, et sa langue
-function traiter_raccourci_titre($id, $type, $connect=NULL)
-{
+/**
+ * Retourne le titre et la langue d'un objet éditorial
+ *
+ * @param int $id Identifiant de l'objet
+ * @param string $type Type d'objet
+ * @param string|null $connect Connecteur SQL utilisé
+ * @return array {
+ * @var string $titre Titre si présent, sinon ''
+ * @var string $lang Langue si présente, sinon ''
+ * }
+ **/
+function traiter_raccourci_titre($id, $type, $connect = null) {
        $trouver_table = charger_fonction('trouver_table', 'base');
        $desc = $trouver_table(table_objet($type));
-       if (!($desc AND $s = $desc['titre'])) return array();
+
+       if (!($desc and $s = $desc['titre'])) {
+               return array();
+       }
+
        $_id = $desc['key']['PRIMARY KEY'];
-       $r = sql_fetsel($s, $desc['table'], "$_id=$id", '','','','',$connect);
-       if (!$r) return array();
+       $r = sql_fetsel($s, $desc['table'], "$_id=$id", '', '', '', '', $connect);
+
+       if (!$r) {
+               return array();
+       }
+
        $r['titre'] = supprimer_numero($r['titre']);
-       if (!$r['titre']) $r['titre'] = $r['surnom'];
-       if (!isset($r['lang'])) $r['lang'] = '';
+
+       if (!$r['titre'] and !empty($r['surnom'])) {
+               $r['titre'] = $r['surnom'];
+       }
+
+       if (!isset($r['lang'])) {
+               $r['lang'] = '';
+       }
+
        return $r;
 }
 
@@ -471,45 +765,55 @@ function traiter_raccourci_titre($id, $type, $connect=NULL)
 // https://code.spip.net/@traiter_modeles
 define('_PREG_MODELE',
        '(<([a-z_-]{3,})' # <modele
.'\s*([0-9]*)\s*' # id
.'([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
.'\s*/?'.'>)' # fin du modele >
      . '\s*([0-9]*)\s*' # id
      . '([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
      . '\s*/?' . '>)' # fin du modele >
 );
 
 define('_RACCOURCI_MODELE',
        _PREG_MODELE
-       .'\s*(<\/a>)?' # eventuel </a>
-       );
+       . '\s*(<\/a>)?' # eventuel </a>
+);
 
-define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE .'@isS');
+define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE . '@isS');
 
 // https://code.spip.net/@traiter_modeles
-function traiter_modeles($texte, $doublons=false, $echap='', $connect='', $liens = null, $env = array()) {
+function traiter_modeles($texte, $doublons = false, $echap = '', $connect = '', $liens = null, $env = array()) {
        // preserver la compatibilite : true = recherche des documents
-       if ($doublons===true)
-               $doublons = array('documents'=>array('doc','emb','img'));
+       if ($doublons === true) {
+               $doublons = array('documents' => array('doc', 'emb', 'img'));
+       }
+
        // detecter les modeles (rapide)
-       if (strpos($texte,"<")!==false AND
-         preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS', $texte, $matches, PREG_SET_ORDER)) {
+       if (
+               strpos($texte, "<") !== false
+               and preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS', $texte, $matches, PREG_SET_ORDER)
+       ) {
                include_spip('public/assembler');
-               $wrap_embed_html = charger_fonction("wrap_embed_html","inc",true);
+               $wrap_embed_html = charger_fonction("wrap_embed_html", "inc", true);
+
+               // Recuperer l'appel complet (y compris un eventuel lien)
                foreach ($matches as $match) {
-                       // Recuperer l'appel complet (y compris un eventuel lien)
-
-                       $a = strpos($texte,$match[0]);
-                       preg_match(_RACCOURCI_MODELE_DEBUT,
-                       substr($texte, $a), $regs);
-                       while(count($regs) < 6) $regs[] = ""; // s'assurer qu'il y a toujours un 5e arg, eventuellement vide
-                       list(,$mod, $type, $id, $params, $fin) = $regs;
-                       if ($fin AND
-                       preg_match('/<a\s[^<>]*>\s*$/i',
-                                       substr($texte, 0, $a), $r)) {
+                       $a = strpos($texte, $match[0]);
+                       preg_match(_RACCOURCI_MODELE_DEBUT, substr($texte, $a), $regs);
+
+                       // s'assurer qu'il y a toujours un 5e arg, eventuellement vide
+                       while (count($regs) < 6) {
+                               $regs[] = "";
+                       }
+
+                       list(, $mod, $type, $id, $params, $fin) = $regs;
+
+                       if (
+                               $fin
+                               and preg_match('/<a\s[^<>]*>\s*$/i', substr($texte, 0, $a), $r)
+                       ) {
                                $lien = array(
-                                       'href' => extraire_attribut($r[0],'href'),
-                                       'class' => extraire_attribut($r[0],'class'),
-                                       'mime' => extraire_attribut($r[0],'type'),
-                                       'title' => extraire_attribut($r[0],'title'),
-                                       'hreflang' => extraire_attribut($r[0],'hreflang')
+                                       'href' => extraire_attribut($r[0], 'href'),
+                                       'class' => extraire_attribut($r[0], 'class'),
+                                       'mime' => extraire_attribut($r[0], 'type'),
+                                       'title' => extraire_attribut($r[0], 'title'),
+                                       'hreflang' => extraire_attribut($r[0], 'hreflang')
                                );
                                $n = strlen($r[0]);
                                $a -= $n;
@@ -521,51 +825,61 @@ function traiter_modeles($texte, $doublons=false, $echap='', $connect='', $liens
 
                        // calculer le modele
                        # hack indexation
-                       if ($doublons)
-                               $texte .= preg_replace(',[|][^|=]*,s',' ',$params);
-                       # version normale
+                       if ($doublons) {
+                               $texte .= preg_replace(',[|][^|=]*,s', ' ', $params);
+                       # version normale
                        else {
                                // si un tableau de liens a ete passe, reinjecter le contenu d'origine
                                // dans les parametres, plutot que les liens echappes
-                               if (!is_null($liens))
+                               if (!is_null($liens)) {
                                        $params = str_replace($liens[0], $liens[1], $params);
-                         $modele = inclure_modele($type, $id, $params, $lien, $connect, $env);
-                               // en cas d'echec, 
-                               // si l'objet demande a une url, 
+                               }
+
+                               $modele = inclure_modele($type, $id, $params, $lien, $connect, $env);
+
+                               // en cas d'echec,
+                               // si l'objet demande a une url,
                                // creer un petit encadre vers elle
                                if ($modele === false) {
-                                       $modele = substr($texte,$a,$cherche);
-                                       if (!is_null($liens))
+                                       $modele = substr($texte, $a, $cherche);
+
+                                       if (!is_null($liens)) {
                                                $modele = str_replace($liens[0], $liens[1], $modele);
-                                       $contexte = array_merge($env,array('id'=>$id,'type'=>$type,'modele'=>$modele));
+                                       }
+
+                                       $contexte = array_merge($env, array('id' => $id, 'type' => $type, 'modele' => $modele));
+
                                        if ($lien) {
                                                # un eventuel guillemet (") sera reechappe par #ENV
-                                               $contexte['lien'] = str_replace("&quot;",'"', $lien['href']);
+                                               $contexte['lien'] = str_replace("&quot;", '"', $lien['href']);
                                                $contexte['lien_class'] = $lien['class'];
                                                $contexte['lien_mime'] = $lien['mime'];
                                                $contexte['lien_title'] = $lien['title'];
                                                $contexte['lien_hreflang'] = $lien['hreflang'];
                                        }
+
                                        $modele = recuperer_fond("modeles/dist", $contexte, array(), $connect);
                                }
                                // le remplacer dans le texte
                                if ($modele !== false) {
                                        $modele = protege_js_modeles($modele);
-                                       if ($wrap_embed_html){
-                                               $modele = $wrap_embed_html($mod,$modele);
+                                       if ($wrap_embed_html) {
+                                               $modele = $wrap_embed_html($mod, $modele);
                                        }
                                        $rempl = code_echappement($modele, $echap);
                                        $texte = substr($texte, 0, $a)
                                                . $rempl
-                                               . substr($texte, $a+$cherche);
+                                               . substr($texte, $a + $cherche);
                                }
                        }
 
                        // hack pour tout l'espace prive
-                       if (((!_DIR_RESTREINT) OR ($doublons)) AND ($id)){
-                               foreach($doublons?$doublons:array('documents'=>array('doc','emb','img')) as $quoi=>$modeles)
-                                       if (in_array($type,$modeles))
+                       if (((!_DIR_RESTREINT) or ($doublons)) and ($id)) {
+                               foreach ($doublons ? $doublons : array('documents' => array('doc', 'emb', 'img')) as $quoi => $modeles) {
+                                       if (in_array(strtolower($type), $modeles)) {
                                                $GLOBALS["doublons_{$quoi}_inclus"][] = $id;
+                                       }
+                               }
                        }
                }
        }
@@ -580,12 +894,17 @@ function traiter_modeles($texte, $doublons=false, $echap='', $connect='', $liens
 define('_RACCOURCI_ANCRE', "/\[#?([^][]*)<-\]/S");
 
 // https://code.spip.net/@traiter_raccourci_ancre
-function traiter_raccourci_ancre($letexte)
-{
-       if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER))
-       foreach ($m as $regs)
-               $letexte = str_replace($regs[0],
-               '<a name="'.entites_html($regs[1]).'"></a>', $letexte);
+function traiter_raccourci_ancre($letexte) {
+       if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER)) {
+               foreach ($m as $regs) {
+                       $letexte = str_replace(
+                               $regs[0],
+                               '<a ' . (html5_permis() ? 'id' : 'name') . '="' . entites_html($regs[1]) . '"></a>',
+                               $letexte
+                       );
+               }
+       }
+
        return $letexte;
 }
 
@@ -598,56 +917,59 @@ define('_RACCOURCI_GLOSSAIRE', "/\[\?+\s*([^][<>]+)\]/S");
 define('_RACCOURCI_GLOSES', '/^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$/S');
 
 // https://code.spip.net/@traiter_raccourci_glossaire
-function traiter_raccourci_glossaire($texte)
-{
-       if (!preg_match_all(_RACCOURCI_GLOSSAIRE,
-       $texte, $matches, PREG_SET_ORDER))
+function traiter_raccourci_glossaire($texte) {
+       if (!preg_match_all(_RACCOURCI_GLOSSAIRE, $texte, $matches, PREG_SET_ORDER)) {
                return $texte;
+       }
 
        include_spip('inc/charsets');
        $lien = charger_fonction('lien', 'inc');
 
-       foreach ($matches as $regs) {
        // Eviter les cas particulier genre "[?!?]"
        // et isoler le lexeme a gloser de ses accessoires
        // (#:url du glossaire, | bulle d'aide, {} hreflang)
        // Transformation en pseudo-raccourci pour passer dans inc_lien
+       foreach ($matches as $regs) {
                if (preg_match(_RACCOURCI_GLOSES, $regs[1], $r)) {
                        preg_match('/^(.*?)(\d*)$/', $r[4], $m);
                        $_n = intval($m[2]);
                        $gloss = $m[1] ? ('#' . $m[1]) : '';
                        $t = $r[1] . $r[2] . $r[5];
                        list($t, $bulle, $hlang) = traiter_raccourci_lien_atts($t);
-                       if ($bulle===false) $bulle = $m[1];
+
+                       if ($bulle === false) {
+                               $bulle = $m[1];
+                       }
+
                        $t = unicode2charset(charset2unicode($t), 'utf-8');
                        $ref = $lien("glose$_n$gloss", $t, 'spip_glossaire', $bulle, $hlang);
                        $texte = str_replace($regs[0], $ref, $texte);
                }
        }
+
        return $texte;
 }
 
 // https://code.spip.net/@glossaire_std
-function glossaire_std($terme)
-{
+function glossaire_std($terme) {
        global $url_glossaire_externe;
-       static $pcre = NULL;
+       static $pcre = null;
+
+       if ($pcre === null) {
+               $pcre = isset($GLOBALS['meta']['pcre_u']) ? $GLOBALS['meta']['pcre_u'] : '';
 
-       if ($pcre === NULL) {
-               $pcre = isset($GLOBALS['meta']['pcre_u']) 
-               ? $GLOBALS['meta']['pcre_u']
-                 : '';
-               if (strpos($url_glossaire_externe, "%s") === false)
+               if (strpos($url_glossaire_externe, "%s") === false) {
                        $url_glossaire_externe .= '%s';
+               }
        }
 
-       $glosateur = str_replace("@lang@",
-                               $GLOBALS['spip_lang'],
-                               $GLOBALS['url_glossaire_externe']);
+       $glosateur = str_replace(
+               "@lang@",
+               $GLOBALS['spip_lang'],
+               $GLOBALS['url_glossaire_externe']
+       );
 
-       $terme = rawurlencode(preg_replace(',\s+,'.$pcre, '_', $terme));
-       
-       return  str_replace("%s", $terme, $glosateur);
-}
+       $terme = rawurlencode(preg_replace(',\s+,' . $pcre, '_', $terme));
 
-?>
+       return str_replace("%s", $terme, $glosateur);
+}