[ Index ]
 

Code source de SPIP 1.9.2c

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/ecrire/inc/ -> texte.php (source)

   1  <?php
   2  
   3  /***************************************************************************\
   4   *  SPIP, Systeme de publication pour l'internet                           *
   5   *                                                                         *
   6   *  Copyright (c) 2001-2007                                                *
   7   *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
   8   *                                                                         *
   9   *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
  10   *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
  11  \***************************************************************************/
  12  
  13  if (!defined("_ECRIRE_INC_VERSION")) return;
  14  
  15  include_spip('inc/filtres');
  16  include_spip('inc/charsets');
  17  include_spip('inc/lang');
  18  
  19  
  20  //
  21  // Gerer les variables de personnalisation, qui peuvent provenir
  22  // des fichiers d'appel, en verifiant qu'elles n'ont pas ete passees
  23  // par le visiteur (sinon, pas de cache)
  24  //
  25  // http://doc.spip.org/@tester_variable
  26  function tester_variable($var, $val){
  27      if (!isset($GLOBALS[$var]))
  28          $GLOBALS[$var] = $val;
  29  
  30      if (
  31          isset($_REQUEST[$var])
  32          AND $GLOBALS[$var] == $_REQUEST[$var]
  33      )
  34          die ("tester_variable: $var interdite");
  35  }
  36  
  37  // on initialise la puce ici car il serait couteux de faire le find_in_path()
  38  // a chaque hit, alors qu'on n'a besoin de cette valeur que lors du calcul
  39  // http://doc.spip.org/@definir_puce
  40  function definir_puce() {
  41      static $les_puces = array();
  42  
  43      // Attention au sens, qui n'est pas defini de la meme facon dans
  44      // l'espace prive (spip_lang est la langue de l'interface, lang_dir
  45      // celle du texte) et public (spip_lang est la langue du texte)
  46      #include_spip('inc/lang');
  47      $dir = _DIR_RESTREINT ?
  48          lang_dir($GLOBALS['spip_lang']) : $GLOBALS['lang_dir'];
  49      $p = ($dir == 'rtl') ? 'puce_rtl' : 'puce';
  50  
  51      if (!isset($les_puces[$p])) {
  52          tester_variable($p, 'AUTO');
  53          if ($GLOBALS[$p] == 'AUTO') {
  54              $img = find_in_path($p.'.gif');
  55              list(,,,$size) = @getimagesize($img);
  56              $img = '<img src="'.$img.'" '
  57                  .$size.' alt="-" />';
  58          } else
  59              $img = $GLOBALS[$p];
  60  
  61          $les_puces[$p] = $img;
  62      }
  63  
  64      return $les_puces[$p];
  65  }
  66  
  67  //
  68  // Diverses fonctions essentielles
  69  //
  70  
  71  
  72  // XHTML - Preserver les balises-bloc : on liste ici tous les elements
  73  // dont on souhaite qu'ils provoquent un saut de paragraphe
  74  define('_BALISES_BLOCS',
  75      'div|pre|ul|ol|li|blockquote|h[1-6r]|'
  76      .'t(able|[rdh]|body|foot|extarea)|'
  77      .'form|object|center|marquee|address|'
  78      .'d[ltd]|script|noscript|map|button|fieldset');
  79  
  80  
  81  // Ne pas afficher le chapo si article virtuel
  82  // http://doc.spip.org/@nettoyer_chapo
  83  function nettoyer_chapo($chapo){
  84      return (substr($chapo,0,1) == "=") ? '' : $chapo;
  85  }
  86  
  87  
  88  //
  89  // Echapper les les elements perilleux en les passant en base64
  90  //
  91  
  92  // Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
  93  // une $source differente ; le script detecte automagiquement si ce qu'on
  94  // echappe est un div ou un span
  95  // http://doc.spip.org/@code_echappement
  96  function code_echappement($rempl, $source='') {
  97      if (!strlen($rempl)) return '';
  98  
  99      // Tester si on echappe en span ou en div
 100      $mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $rempl) ?
 101          'div' : 'span';
 102      $nn = ($mode == 'div') ? "\n\n" : '';
 103      $return = '';
 104  
 105      // Decouper en morceaux, base64 a des probleme selon la taille de la pile
 106      $taille = 30000;
 107      for($i = 0; $i < strlen($rempl); $i += $taille) {
 108          // Convertir en base64
 109          $base64 = base64_encode(substr($rempl, $i, $taille));
 110          $return .=
 111              inserer_attribut("<$mode class=\"base64$source\">", 'title', $base64)
 112                                      ."</$mode>";
 113      }
 114  
 115      return $return . $nn;
 116  }
 117  
 118  // Echapper les <html>...</ html>
 119  // http://doc.spip.org/@traiter_echap_html_dist
 120  function traiter_echap_html_dist($regs) {
 121      return $regs[3];
 122  }
 123  
 124  // Echapper les <code>...</ code>
 125  // http://doc.spip.org/@traiter_echap_code_dist
 126  function traiter_echap_code_dist($regs) {
 127      $echap = entites_html($regs[3]);
 128      // supprimer les sauts de ligne debut/fin
 129      // (mais pas les espaces => ascii art).
 130      $echap = ereg_replace("^\n+|\n+$", "", $echap);
 131  
 132      // ne pas mettre le <div...> s'il n'y a qu'une ligne
 133      if (is_int(strpos($echap,"\n"))) {
 134          $echap = nl2br("<div style='text-align: left;' "
 135          . "class='spip_code' dir='ltr'><code>"
 136          .$echap."</code></div>");
 137      } else
 138          $echap = "<code class='spip_code' "
 139          ."dir='ltr'>".$echap."</code>";
 140  
 141      $echap = str_replace("\t",
 142          "&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
 143      $echap = str_replace("  ", " &nbsp;", $echap);
 144      return $echap;
 145  }
 146  
 147  // Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
 148  // http://doc.spip.org/@traiter_echap_cadre_dist
 149  function traiter_echap_cadre_dist($regs) {
 150      $echap = trim(entites_html($regs[3]));
 151      $total_lignes = substr_count($echap, "\n") + 1;
 152      $echap = "<form action=\"/\" method=\"get\"><div>"
 153      ."<textarea readonly='readonly' cols='40' rows='$total_lignes' "
 154      ."class='spip_cadre' dir='ltr'>"
 155      .$echap
 156      ."</textarea></div></form>";
 157      return $echap;
 158  }
 159  // http://doc.spip.org/@traiter_echap_frame_dist
 160  function traiter_echap_frame_dist($regs) {
 161      return traiter_echap_cadre_dist($regs);
 162  }
 163  
 164  // http://doc.spip.org/@traiter_echap_script_dist
 165  function traiter_echap_script_dist($regs) {
 166      // rendre joli (et inactif) si c'est un script language=php
 167      if (preg_match(',<script\b[^>]+php,ims',
 168      $regs[0]))
 169          return highlight_string($regs[0],true);
 170  
 171      // Cas normal : le script passe tel quel
 172      return $regs[0];
 173  }
 174  
 175  
 176  // - pour $source voir commentaire infra (echappe_retour)
 177  // - pour $no_transform voir le filtre post_autobr dans inc_filtres.php3
 178  // http://doc.spip.org/@echappe_html
 179  function echappe_html($letexte, $source='', $no_transform=false,
 180  $preg='') {
 181      if (!strlen($letexte)) return '';
 182  
 183      if (!$preg) $preg = ',<(html|code|cadre|frame|script)'
 184              .'(\s[^>]*)?'
 185              .'>(.*)</\1>,UimsS';
 186      if (preg_match_all(
 187      $preg,
 188      $letexte, $matches, PREG_SET_ORDER))
 189      foreach ($matches as $regs) {
 190          // echappements tels quels ?
 191          if ($no_transform) {
 192              $echap = $regs[0];
 193          }
 194  
 195          // sinon les traiter selon le cas
 196          else if (function_exists($f = 'traiter_echap_'.strtolower($regs[1])))
 197              $echap = $f($regs);
 198          else if (function_exists($f = $f.'_dist'))
 199              $echap = $f($regs);
 200  
 201          $letexte = str_replace($regs[0],
 202              code_echappement($echap, $source),
 203              $letexte);
 204      }
 205  
 206      // Gestion du TeX
 207      if (strpos($letexte, "<math>") !== false) {
 208          include_spip('inc/math');
 209          $letexte = traiter_math($letexte, $source);
 210      }
 211  
 212      // Echapper le php pour faire joli (ici, c'est pas pour la securite)
 213      if (preg_match_all(
 214      ',<[?].*($|[?]>),UisS',
 215      $letexte, $matches, PREG_SET_ORDER))
 216      foreach ($matches as $regs) {
 217          $letexte = str_replace($regs[0],
 218              code_echappement(highlight_string($regs[0],true), $source),
 219              $letexte);
 220      }
 221  
 222      return $letexte;
 223  }
 224  
 225  //
 226  // Traitement final des echappements
 227  // Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
 228  // par propre() : exemple dans ecrire/inc_articles_ortho.php, $source='ORTHO'
 229  // ou encore dans typo()
 230  // http://doc.spip.org/@echappe_retour
 231  function echappe_retour($letexte, $source='') {
 232      if (strpos($letexte,"base64$source")) {
 233          # spip_log(htmlspecialchars($letexte));  ## pour les curieux
 234          if (preg_match_all(
 235          ',<(span|div) class=[\'"]base64'.$source.'[\'"]\s.*></\1>,UmsS',
 236          $letexte, $regs, PREG_SET_ORDER)) {
 237              foreach ($regs as $reg) {
 238                  $rempl = base64_decode(extraire_attribut($reg[0], 'title'));
 239                  $letexte = str_replace($reg[0], $rempl, $letexte);
 240              }
 241          }
 242      }
 243      return $letexte;
 244  }
 245  
 246  // http://doc.spip.org/@nettoyer_raccourcis_typo
 247  function nettoyer_raccourcis_typo($texte){
 248      $texte = pipeline('nettoyer_raccourcis_typo',$texte);
 249      // remplacer les liens
 250      if (preg_match_all(',[[]([^][]*)->(>?)([^][]*)[]],S', $texte, $regs, PREG_SET_ORDER))
 251          foreach ($regs as $reg) {
 252              $titre = supprimer_tags(traiter_raccourci_lien($reg));
 253              $texte = str_replace($reg[0], $titre, $texte);
 254          }
 255  
 256      // supprimer les notes
 257      $texte = preg_replace(",[[][[]([^]]|[]][^]])*[]][]],UimsS", "", $texte);
 258  
 259      // supprimer les codes typos
 260      $texte = str_replace(array('}','{'), '', $texte);
 261  
 262      // supprimer les tableaux
 263      $texte = preg_replace(",(^|\r)\|.*\|\r,s", "\r", $texte);
 264      return $texte;
 265  }
 266  
 267  // http://doc.spip.org/@couper
 268  function couper($texte, $taille=50) {
 269      $offset = 400 + 2*$taille;
 270      if (    $offset<strlen($texte)
 271              && ($p_tag_ouvrant = strpos($texte,'<',$offset))!==NULL){
 272          $p_tag_fermant = strpos($texte,'>',$offset);
 273          if ($p_tag_fermant<$p_tag_ouvrant)
 274              $offset += $p_tag_fermant; // prolonger la coupe jusqu'au tag fermant suivant eventuel
 275      }
 276      $texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
 277  
 278      // on utilise les \r pour passer entre les gouttes
 279      $texte = str_replace("\r\n", "\n", $texte);
 280      $texte = str_replace("\r", "\n", $texte);
 281  
 282      // sauts de ligne et paragraphes
 283      $texte = ereg_replace("\n\n+", "\r", $texte);
 284      $texte = ereg_replace("<(p|br)( [^>]*)?".">", "\r", $texte);
 285  
 286      // supprimer les traits, lignes etc
 287      $texte = ereg_replace("(^|\r|\n)(-[-#\*]*|_ )", "\r", $texte);
 288  
 289      // supprimer les tags
 290      $texte = supprimer_tags($texte);
 291      $texte = trim(str_replace("\n"," ", $texte));
 292      $texte .= "\n";    // marquer la fin
 293  
 294      // travailler en accents charset
 295      $texte = unicode2charset(html2unicode($texte, /* secure */ true));
 296  
 297      $texte = nettoyer_raccourcis_typo($texte);
 298  
 299      // corriger la longueur de coupe 
 300      // en fonction de la presence de caracteres utf
 301      if ($GLOBALS['meta']['charset']=='utf-8'){
 302          $long = charset2unicode($texte);
 303          $long = spip_substr($long, 0, max($taille,1));
 304          $nbcharutf = preg_match_all("/(&#[0-9]{3,5};)/S",$long,$matches);
 305          $taille += $nbcharutf;
 306      }
 307  
 308  
 309      // couper au mot precedent
 310      $long = spip_substr($texte, 0, max($taille-4,1));
 311      $court = ereg_replace("([^[:space:]][[:space:]]+)[^[:space:]]*\n?$", "\\1", $long);
 312      $points = '&nbsp;(...)';
 313  
 314      // trop court ? ne pas faire de (...)
 315      if (spip_strlen($court) < max(0.75 * $taille,2)) {
 316          $points = '';
 317          $long = spip_substr($texte, 0, $taille);
 318          $texte = ereg_replace("([^[:space:]][[:space:]]+)[^[:space:]]*$", "\\1", $long);
 319          // encore trop court ? couper au caractere
 320          if (spip_strlen($texte) < 0.75 * $taille)
 321              $texte = $long;
 322      } else
 323          $texte = $court;
 324  
 325      if (strpos($texte, "\n"))    // la fin est encore la : c'est qu'on n'a pas de texte de suite
 326          $points = '';
 327  
 328      // remettre les paragraphes
 329      $texte = ereg_replace("\r+", "\n\n", $texte);
 330  
 331      // supprimer l'eventuelle entite finale mal coupee
 332      $texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
 333  
 334      return quote_amp(trim($texte)).$points;
 335  }
 336  
 337  // prendre <intro>...</intro> sinon couper a la longueur demandee
 338  // http://doc.spip.org/@couper_intro
 339  function couper_intro($texte, $long) {
 340      $texte = extraire_multi(eregi_replace("(</?)intro>", "\\1intro>", $texte)); // minuscules
 341      $intro = '';
 342      while ($fin = strpos($texte, "</intro>")) {
 343          $zone = substr($texte, 0, $fin);
 344          $texte = substr($texte, $fin + strlen("</intro>"));
 345          if ($deb = strpos($zone, "<intro>") OR substr($zone, 0, 7) == "<intro>")
 346              $zone = substr($zone, $deb + 7);
 347          $intro .= $zone;
 348      }
 349  
 350      if ($intro)
 351          $intro = $intro.'&nbsp;(...)';
 352      else {
 353          $intro = preg_replace(',([|]\s*)+,S', '; ', couper($texte, $long));
 354      }
 355  
 356      // supprimer un eventuel chapo redirecteur =http:/.....
 357      return $intro;
 358  }
 359  
 360  
 361  //
 362  // Les elements de propre()
 363  //
 364  
 365  /*
 366  // Securite : empecher l'execution de code PHP ou javascript ou autre malice
 367  // http://doc.spip.org/@interdire_scripts
 368  function interdire_scripts($source) {
 369      $source = preg_replace(",<(\%|\?|/?[[:space:]]*(script|base)),imsS", "&lt;\\1", $source);
 370      return $source;
 371  }
 372  */
 373  
 374  // afficher joliment les <script>
 375  // http://doc.spip.org/@echappe_js
 376  function echappe_js($t,$class='') {
 377      if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER))
 378      foreach ($r as $regs)
 379          $t = str_replace($regs[0],
 380              "<code$class>".nl2br(htmlspecialchars($regs[0])).'</code>',
 381              $t);
 382      return $t;
 383  }
 384  // http://doc.spip.org/@protege_js_modeles
 385  function protege_js_modeles($t) {
 386      if (isset($GLOBALS['auteur_session'])){
 387          if (!defined('_PROTEGE_JS_MODELES')){
 388              include_spip('inc/acces');
 389              define('_PROTEGE_JS_MODELES',creer_uniqid());
 390          }
 391          if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER))
 392              foreach ($r as $regs)
 393                  $t = str_replace($regs[0],code_echappement($regs[0],'javascript'._PROTEGE_JS_MODELES),$t);
 394      }
 395      return $t;
 396  }
 397  
 398  // Securite : empecher l'execution de code PHP, en le transformant en joli code
 399  // http://doc.spip.org/@interdire_scripts
 400  function interdire_scripts($t) {
 401  
 402      // rien ?
 403      if (!$t OR !strstr($t, '<')) return $t;
 404  
 405      // echapper les tags asp/php
 406      $t = str_replace('<'.'%', '&lt;%', $t);
 407  
 408      // echapper le php
 409      $t = str_replace('<'.'?', '&lt;?', $t);
 410  
 411      // echapper le < script language=php >
 412      $t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '&lt;\1', $t);
 413  
 414      // Pour le js, trois modes : parano (-1), prive (0), ok (1)
 415      switch($GLOBALS['filtrer_javascript']) {
 416          case 0:
 417              if (!_DIR_RESTREINT)
 418                  $t = echappe_js($t,' style="color:red"');
 419              break;
 420          case -1:
 421              $t = echappe_js($t);
 422              break;
 423      }
 424  
 425      // pas de <base href /> svp !
 426      $t = preg_replace(',<(base\s),iS', '&lt;\1', $t);
 427  
 428      return $t;
 429  }
 430  
 431  // Securite : utiliser SafeHTML s'il est present dans ecrire/safehtml/
 432  // http://doc.spip.org/@safehtml
 433  function safehtml($t) {
 434      static $safehtml;
 435  
 436      # attention safehtml nettoie deux ou trois caracteres de plus. A voir
 437      if (strpos($t,'<')===false)
 438          return str_replace("\x00", '', $t);
 439  
 440      $t = interdire_scripts($t); // jolifier le php
 441      $t = echappe_js($t);
 442  
 443      if (!isset($safehtml))
 444          $safehtml = charger_fonction('safehtml', 'inc');
 445      if ($safehtml)
 446          $t = $safehtml($t);
 447  
 448      return interdire_scripts($t); // interdire le php (2 precautions)
 449  }
 450  
 451  // Correction typographique francaise
 452  // http://doc.spip.org/@typo_fr
 453  function typo_fr($letexte) {
 454      static $trans;
 455  
 456      // Nettoyer 160 = nbsp ; 187 = raquo ; 171 = laquo ; 176 = deg ; 147 = ldquo; 148 = rdquo
 457      if (!$trans) {
 458          $trans = array(
 459              "&nbsp;" => "~",
 460              "&raquo;" => "&#187;",
 461              "&laquo;" => "&#171;",
 462              "&rdquo;" => "&#8221;",
 463              "&ldquo;" => "&#8220;",
 464              "&deg;" => "&#176;"
 465          );
 466          $chars = array(160 => '~', 187 => '&#187;', 171 => '&#171;', 148 => '&#8221;', 147 => '&#8220;', 176 => '&#176;');
 467          $chars_trans = array_keys($chars);
 468          $chars = array_values($chars);
 469          $chars_trans = implode(' ',array_map('chr',$chars_trans));
 470          $chars_trans = unicode2charset(charset2unicode($chars_trans, 'iso-8859-1', 'forcer'));
 471          $chars_trans = explode(" ",$chars_trans);
 472          foreach($chars as $k=>$r)
 473              $trans[$chars_trans[$k]] = $r;
 474      }
 475  
 476      $letexte = strtr($letexte, $trans);
 477  
 478      $cherche1 = array(
 479          /* 1 */     '/((?:^|[^\#0-9a-zA-Z\&])[\#0-9a-zA-Z]*)\;/S',
 480          /* 2 */        '/&#187;| --?,|(?::| %)(?:\W|$)/S',
 481          /* 3 */        '/([^[<!?])([!?])/S',
 482          /* 4 */        '/&#171;|(?:M(?:M?\.|mes?|r\.?)|[MnN]&#176;) /S'
 483      );
 484      $remplace1 = array(
 485          /* 1 */        '\1~;',
 486          /* 2 */        '~\0',
 487          /* 3 */        '\1~\2',
 488          /* 4 */        '\0~'
 489      );
 490      $letexte = preg_replace($cherche1, $remplace1, $letexte);
 491      $letexte = preg_replace("/ *~+ */S", "~", $letexte);
 492  
 493      $cherche2 = array(
 494          '/([^-\n]|^)--([^-]|$)/S',
 495          '/(http|https|ftp|mailto)~:/S',
 496          '/~/'
 497      );
 498      $remplace2 = array(
 499          '\1&mdash;\2',
 500          '\1:',
 501          '&nbsp;'
 502      );
 503      $letexte = preg_replace($cherche2, $remplace2, $letexte);
 504  
 505      return $letexte;
 506  }
 507  
 508  // rien sauf les "~" et "-,"
 509  // http://doc.spip.org/@typo_en
 510  function typo_en($letexte) {
 511  
 512      $cherche1 = array(
 513          '/ --?,/S'
 514      );
 515      $remplace1 = array(
 516          '~\0'
 517      );
 518      $letexte = preg_replace($cherche1, $remplace1, $letexte);
 519  
 520      $letexte = str_replace("&nbsp;", "~", $letexte);
 521      $letexte = ereg_replace(" *~+ *", "~", $letexte);
 522  
 523      $cherche2 = array(
 524          '/([^-\n]|^)--([^-]|$)/',
 525          '/~/'
 526      );
 527      $remplace2 = array(
 528          '\1&mdash;\2',
 529          '&nbsp;'
 530      );
 531  
 532      $letexte = preg_replace($cherche2, $remplace2, $letexte);
 533  
 534      return $letexte;
 535  }
 536  
 537  //
 538  // Typographie generale
 539  // note: $echapper = false lorsqu'on appelle depuis propre() [pour accelerer]
 540  //
 541  // http://doc.spip.org/@typo
 542  function typo($letexte, $echapper=true) {
 543  
 544      // Plus vite !
 545      if (!$letexte) return $letexte;
 546  
 547      // Echapper les codes <html> etc
 548      if ($echapper)
 549          $letexte = echappe_html($letexte, 'TYPO');
 550  
 551      // Appeler les fonctions de pre-traitement
 552      $letexte = pipeline('pre_typo', $letexte);
 553      // old style
 554      if (function_exists('avant_typo'))
 555          $letexte = avant_typo($letexte);
 556  
 557      // Caracteres de controle "illegaux"
 558      $letexte = corriger_caracteres($letexte);
 559  
 560      // Proteger les caracteres typographiques a l'interieur des tags html
 561      $protege = "!':;?~%";
 562      $illegal = "\x1\x2\x3\x4\x5\x6\x7";
 563      if (preg_match_all(",</?[a-z!][^<>]*[!':;\?~%][^<>]*>,imsS",
 564      $letexte, $regs, PREG_SET_ORDER)) {
 565          foreach ($regs as $reg) {
 566              $insert = $reg[0];
 567              // hack: on transforme les caracteres a proteger en les remplacant
 568              // par des caracteres "illegaux". (cf corriger_caracteres())
 569              $insert = strtr($insert, $protege, $illegal);
 570              $letexte = str_replace($reg[0], $insert, $letexte);
 571          }
 572      }
 573  
 574      // zouli apostrophe
 575      $letexte = str_replace("'", "&#8217;", $letexte);
 576  
 577      // typo francaise ou anglaise ?
 578      // $lang_objet est fixee dans l'interface privee pour editer
 579      // un texte anglais en interface francaise (ou l'inverse) ;
 580      // sinon determiner la typo en fonction de la langue
 581      if (!$lang = $GLOBALS['lang_objet'])
 582          $lang = $GLOBALS['spip_lang'];
 583      lang_select($lang);
 584      switch (lang_typo($lang)) {
 585          case 'fr':
 586              $letexte = typo_fr($letexte);
 587              break;
 588          default:
 589              $letexte = typo_en($letexte);
 590              break;
 591      }
 592  
 593      // Retablir les caracteres proteges
 594      $letexte = strtr($letexte, $illegal, $protege);
 595  
 596      //
 597      // Installer les modeles, notamment images et documents ;
 598      //
 599      // NOTE : dans propre() ceci s'execute avant les tableaux a cause du "|",
 600      // et apres les liens a cause du traitement de [<imgXX|right>->URL]
 601      $letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '');
 602      if ($letexte != $mem) $echapper = true;
 603      unset($mem);
 604  
 605      // Appeler les fonctions de post-traitement
 606      $letexte = pipeline('post_typo', $letexte);
 607      // old style
 608      if (function_exists('apres_typo'))
 609          $letexte = apres_typo($letexte);
 610  
 611      // remettre la langue precedente
 612      lang_dselect();
 613  
 614      // reintegrer les echappements
 615      if ($echapper)
 616          $letexte = echappe_retour($letexte, 'TYPO');
 617  
 618      # un message pour abs_url - on est passe en mode texte
 619      $GLOBALS['mode_abs_url'] = 'texte';
 620  
 621      // Dans l'espace prive, securiser ici
 622      if (!_DIR_RESTREINT)
 623          $letexte = interdire_scripts($letexte);
 624  
 625      return $letexte;
 626  }
 627  
 628  // traitement des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
 629  
 630  define('_RACCOURCI_URL', ',^(\S*?)\s*(\d+)(\?.*?)?(#[^\s]*)?$,S');
 631  
 632  // http://doc.spip.org/@typer_raccourci
 633  function typer_raccourci ($lien) {
 634  
 635      if (!preg_match(_RACCOURCI_URL, trim($lien), $match)) return false;
 636      $f = $match[1];
 637      // valeur par defaut et alias historiques
 638      if (!$f) $f = 'article';
 639      else if ($f == 'art') $f = 'article';
 640      else if ($f == 'br') $f = 'breve';
 641      else if ($f == 'rub') $f = 'rubrique';
 642      else if ($f == 'aut') $f = 'auteur';
 643      else if ($f == 'doc' OR $f == 'im' OR $f == 'img' OR $f == 'image' OR $f == 'emb')
 644          $f = 'document';
 645      else if (preg_match(',^br..?ve$,S', $f)) $f = 'breve'; # accents :(
 646      $match[0] = $f;
 647      return $match;
 648  }
 649  
 650  // Cherche un lien du type [->raccourci 123]
 651  // associe a une fonction generer_url_raccourci()
 652  //
 653  // Valeur retournee selon le parametre $pour:
 654  // 'tout' : <a href="L">T</a>
 655  // 'titre': seulement T ci-dessus (i.e. le TITRE ci-dessus ou dans table SQL)
 656  // 'url':   seulement L (i.e. generer_url_RACCOURCI)
 657  
 658  // http://doc.spip.org/@calculer_url
 659  function calculer_url ($lien, $texte='', $pour='url') {
 660      $lien = vider_url($lien); # supprimer 'http://' ou 'mailto:'
 661  
 662      if ($match = typer_raccourci ($lien)) {
 663          list($f,$objet,$id,$params,$ancre) = $match;
 664          // chercher la fonction nommee generer_url_$raccourci
 665          // ou calculer_url_raccourci si on n'a besoin que du lien
 666          $f=(($pour == 'url') ? 'generer' : 'calculer') . '_url_' . $f;
 667          charger_generer_url();
 668          if (function_exists($f)) {
 669              if ($pour == 'url') {
 670                  $url = $f($id);
 671                  if ($params)
 672                      $url .= (strstr($url, '?') ? '&amp;' : '?')
 673                          . substr($params,1);
 674                  return $url . $ancre;
 675              }
 676  
 677              $res = $f($id, $texte, $ancre);
 678              $res[2] = $res[2];
 679              if ($pour == 'titre')
 680                  return $res[2];
 681              if ($params)
 682                  $res[0] .= (strstr($res[0], '?') ? '&amp;' : '?')
 683                      . substr($params,1);
 684              $res[0] .= $ancre;
 685              return $res;
 686          }
 687      }
 688    
 689      $lien = ltrim($lien);
 690      if ($lien[0] == '?') {
 691          if ($pour == 'titre') return $texte;
 692          $lien = entites_html(substr($lien, 1));
 693          return ($pour == 'url') ? $lien :
 694              array($lien, 'spip_glossaire', $texte);
 695      }
 696      // Liens explicites
 697      if (!$texte) {
 698          $texte = str_replace('"', '', $lien);
 699          if (strlen($texte)>40)
 700                  $texte = substr($texte,0,35).'...';
 701          $texte = "<html>$texte</html>";
 702          $class = "spip_url spip_out";
 703      } else     $class = "spip_out";
 704  
 705      if ($pour == 'titre') return $texte;
 706  
 707      $lien = vider_url($lien);
 708  
 709      // petites corrections d'URL
 710      if (preg_match(",^www\.[^@]+$,S",$lien))
 711          $lien = "http://".$lien;
 712      else if (strpos($lien, "@") && email_valide($lien))
 713          $lien = "mailto:".$lien;
 714  
 715      // class spip_ancre sur les ancres pures (internes a la page)
 716      if (substr($lien,0,1) == '#')
 717          $class = 'spip_ancre';
 718  
 719      return ($pour == 'url') ? $lien : array($lien, $class, $texte, '');
 720  }
 721  
 722  // http://doc.spip.org/@calculer_url_article
 723  function calculer_url_article($id, $texte='') {
 724      $lien = generer_url_article($id);
 725      $s = spip_query("SELECT titre,lang FROM spip_articles WHERE id_article=$id");
 726      $row = spip_fetch_array($s);
 727      if ($texte=='')
 728          $texte = supprimer_numero($row['titre']);
 729      return array($lien, 'spip_in', $texte, $row['lang']);
 730  }
 731  
 732  // http://doc.spip.org/@calculer_url_rubrique
 733  function calculer_url_rubrique($id, $texte='')
 734  {
 735      $lien = generer_url_rubrique($id);
 736      $s = spip_query("SELECT titre,lang FROM spip_rubriques WHERE id_rubrique=$id");
 737      $row = spip_fetch_array($s);
 738      if ($texte=='')
 739          $texte = supprimer_numero($row['titre']);
 740      return array($lien, 'spip_in', $texte, $row['lang']);
 741  }
 742  
 743  // http://doc.spip.org/@calculer_url_mot
 744  function calculer_url_mot($id, $texte='')
 745  {
 746      $lien = generer_url_mot($id);
 747      $s = spip_query("SELECT titre FROM spip_mots WHERE id_mot=$id");
 748      $row = spip_fetch_array($s);
 749      if ($texte=='')
 750          $texte = supprimer_numero($row['titre']);
 751      return array($lien, 'spip_in', $texte);
 752  }
 753  
 754  // http://doc.spip.org/@calculer_url_breve
 755  function calculer_url_breve($id, $texte='')
 756  {
 757      $lien = generer_url_breve($id);
 758      $s = spip_query("SELECT titre,lang FROM spip_breves WHERE id_breve=$id");
 759      $row = spip_fetch_array($s);
 760      if ($texte=='')
 761          $texte = supprimer_numero($row['titre']);
 762      return array($lien, 'spip_in', $texte, $row['lang']);
 763  }
 764  
 765  // http://doc.spip.org/@calculer_url_auteur
 766  function calculer_url_auteur($id, $texte='')
 767  {
 768      $lien = generer_url_auteur($id);
 769      if ($texte=='') {
 770          $s = spip_query("SELECT nom FROM spip_auteurs WHERE id_auteur=$id");
 771      $row = spip_fetch_array($s);
 772          $texte = $row['nom'];
 773      }
 774      return array($lien, 'spip_in', $texte); # pas de hreflang
 775  }
 776  
 777  // http://doc.spip.org/@calculer_url_document
 778  function calculer_url_document($id, $texte='')
 779  {
 780      $lien = generer_url_document($id);
 781      if ($texte=='') {
 782          $s = spip_query("SELECT titre,fichier FROM spip_documents WHERE id_document=$id");
 783          $row = spip_fetch_array($s);
 784          $texte = $row['titre'];
 785          if ($texte=='')
 786              $texte = preg_replace(",^.*/,","",$row['fichier']);
 787      }
 788      return array($lien, 'spip_in', $texte); # pas de hreflang
 789  }
 790  
 791  // http://doc.spip.org/@calculer_url_site
 792  function calculer_url_site($id, $texte='')
 793  {
 794      # attention dans le cas des sites le lien pointe non pas sur
 795      # la page locale du site, mais directement sur le site lui-meme
 796      $s = spip_query("SELECT nom_site,url_site FROM spip_syndic WHERE id_syndic=$id");
 797      $row = spip_fetch_array($s);
 798      if ($row) {
 799          $lien = $row['url_site'];
 800          if ($texte=='')
 801              $texte = supprimer_numero($row['nom_site']);
 802      }
 803      return array($lien, 'spip_out', $texte, $row['lang']);
 804  }
 805  
 806  // http://doc.spip.org/@calculer_url_forum
 807  function calculer_url_forum($id, $texte='')
 808  {
 809      $lien = generer_url_forum($id);
 810      if ($texte=='') {
 811          $s = spip_query("SELECT titre FROM spip_forum WHERE id_forum=$id AND statut='publie'");
 812          $row = spip_fetch_array($s);
 813          if ($texte=='')
 814              $texte = $row['titre'];
 815      }
 816      return array($lien, 'spip_in', $texte); # pas de hreflang
 817  }
 818  
 819  
 820  //
 821  // Tableaux
 822  //
 823  // http://doc.spip.org/@traiter_tableau
 824  function traiter_tableau($bloc) {
 825  
 826      // Decouper le tableau en lignes
 827      preg_match_all(',([|].*)[|]\n,UmsS', $bloc, $regs, PREG_PATTERN_ORDER);
 828      $lignes = array();
 829  
 830      // Traiter chaque ligne
 831      foreach ($regs[1] as $ligne) {
 832          $l ++;
 833  
 834          // Gestion de la premiere ligne :
 835          if ($l == 1) {
 836          // - <caption> et summary dans la premiere ligne :
 837          //   || caption | summary || (|summary est optionnel)
 838              if (preg_match(',^\|\|([^|]*)(\|(.*))?\|$,sS', $ligne, $cap)) {
 839                  $l = 0;
 840                  if ($caption = trim($cap[1]))
 841                      $debut_table .= "<caption>".$caption."</caption>\n";
 842                  $summary = ' summary="'.entites_html(trim($cap[3])).'"';
 843              }
 844          // - <thead> sous la forme |{{titre}}|{{titre}}|
 845          //   Attention thead oblige a avoir tbody
 846              else if (preg_match(',^(\|([[:space:]]*{{[^}]+}}[[:space:]]*|<))+$,sS',
 847                  $ligne, $thead)) {
 848                    preg_match_all("/\|([^|]*)/S", $ligne, $cols);
 849                  $ligne='';$cols= $cols[1];
 850                  $colspan=1;
 851                  for($c=count($cols)-1; $c>=0; $c--) {
 852                      $attr='';
 853                      if($cols[$c]=='<') {
 854                        $colspan++;
 855                      } else {
 856                        if($colspan>1) {
 857                          $attr= " colspan='$colspan'";
 858                          $colspan=1;
 859                        }
 860                        $ligne= "<th scope='col'$attr>$cols[$c]</th>$ligne";
 861                      }
 862                  }
 863  
 864                  $debut_table .= "<thead><tr class='row_first'>".
 865                      $ligne."</tr></thead>\n";
 866                  $l = 0;
 867              }
 868          }
 869  
 870          // Sinon ligne normale
 871          if ($l) {
 872              // Gerer les listes a puce dans les cellules
 873              if (ereg("\n-[*#]", $ligne))
 874                  $ligne = traiter_listes($ligne);
 875  
 876              // Pas de paragraphes dans les cellules
 877              $ligne = preg_replace("/\n{2,}/", "<br />\n", $ligne);
 878  
 879              // tout mettre dans un tableau 2d
 880              preg_match_all("/\|([^|]*)/S", $ligne, $cols);
 881              $lignes[]= $cols[1];
 882          }
 883      }
 884  
 885      // maintenant qu'on a toutes les cellules
 886      // on prepare une liste de rowspan par defaut, a partir
 887      // du nombre de colonnes dans la premiere ligne
 888      $rowspans = array();
 889      for ($i=0; $i<count($lignes[0]); $i++)
 890          $rowspans[] = 1;
 891  
 892      // et on parcourt le tableau a l'envers pour ramasser les
 893      // colspan et rowspan en passant
 894      for($l=count($lignes)-1; $l>=0; $l--) {
 895          $cols= $lignes[$l];
 896          $colspan=1;
 897          $ligne='';
 898  
 899          for($c=count($cols)-1; $c>=0; $c--) {
 900              $attr='';
 901              if($cols[$c]=='<') {
 902                $colspan++;
 903  
 904              } elseif($cols[$c]=='^') {
 905                $rowspans[$c]++;
 906  
 907              } else {
 908                if($colspan>1) {
 909                  $attr.= " colspan='$colspan'";
 910                  $colspan=1;
 911                }
 912                if($rowspans[$c]>1) {
 913                  $attr.= " rowspan='$rowspans[$c]'";
 914                  $rowspans[$c]=1;
 915                }
 916                $ligne= '<td'.$attr.'>'.$cols[$c].'</td>'.$ligne;
 917              }
 918          }
 919  
 920          // ligne complete
 921          $class = 'row_'.alterner($l+1, 'even', 'odd');
 922          $html = "<tr class=\"$class\">" . $ligne . "</tr>\n".$html;
 923      }
 924  
 925      return "\n\n<table class=\"spip\"$summary>\n"
 926          . $debut_table
 927          . "<tbody>\n"
 928          . $html
 929          . "</tbody>\n"
 930          . "</table>\n\n";
 931  }
 932  
 933  
 934  //
 935  // Traitement des listes (merci a Michael Parienti)
 936  //
 937  // http://doc.spip.org/@traiter_listes
 938  function traiter_listes ($texte) {
 939      $parags = preg_split(",\n[[:space:]]*\n,S", $texte);
 940      $texte ='';
 941  
 942      // chaque paragraphe est traite a part
 943      while (list(,$para) = each($parags)) {
 944          $niveau = 0;
 945          $lignes = explode("\n-", "\n" . $para);
 946  
 947          // ne pas toucher a la premiere ligne
 948          list(,$debut) = each($lignes);
 949          $texte .= $debut;
 950  
 951          // chaque item a sa profondeur = nb d'etoiles
 952          $type ='';
 953          while (list(,$item) = each($lignes)) {
 954              preg_match(",^([*]*|[#]*)([^*#].*)$,sS", $item, $regs);
 955              $profond = strlen($regs[1]);
 956  
 957              if ($profond > 0) {
 958                  $ajout='';
 959  
 960                  // changement de type de liste au meme niveau : il faut
 961                  // descendre un niveau plus bas, fermer ce niveau, et
 962                  // remonter
 963                  $nouv_type = (substr($item,0,1) == '*') ? 'ul' : 'ol';
 964                  $change_type = ($type AND ($type <> $nouv_type) AND ($profond == $niveau)) ? 1 : 0;
 965                  $type = $nouv_type;
 966  
 967                  // d'abord traiter les descentes
 968                  while ($niveau > $profond - $change_type) {
 969                      $ajout .= $pile_li[$niveau];
 970                      $ajout .= $pile_type[$niveau];
 971                      if (!$change_type)
 972                          unset ($pile_li[$niveau]);
 973                      $niveau --;
 974                  }
 975  
 976                  // puis les identites (y compris en fin de descente)
 977                  if ($niveau == $profond && !$change_type) {
 978                      $ajout .= $pile_li[$niveau];
 979                  }
 980  
 981                  // puis les montees (y compris apres une descente un cran trop bas)
 982                  while ($niveau < $profond) {
 983                      if ($niveau == 0) $ajout .= "\n\n";
 984                      $niveau ++;
 985                      $ajout .= "<$type class=\"spip\">";
 986                      $pile_type[$niveau] = "</$type>";
 987                  }
 988  
 989                  $ajout .= "<li class=\"spip\">";
 990                  $pile_li[$profond] = "</li>";
 991              }
 992              else {
 993                  $ajout = "\n-";    // puce normale ou <hr>
 994              }
 995  
 996              $texte .= $ajout . $regs[2];
 997          }
 998  
 999          // retour sur terre
1000          $ajout = '';
1001          while ($niveau > 0) {
1002              $ajout .= $pile_li[$niveau];
1003              $ajout .= $pile_type[$niveau];
1004              $niveau --;
1005          }
1006          $texte .= $ajout;
1007  
1008          // paragraphe
1009          $texte .= "\n\n";
1010      }
1011  
1012      // sucrer les deux derniers \n
1013      return substr($texte, 0, -2);
1014  }
1015  
1016  
1017  // fonction en cas de texte extrait d'un serveur distant:
1018  // on ne sait pas (encore) rapatrier les documents joints
1019  // TODO: gerer les modeles ?
1020  // http://doc.spip.org/@supprime_img
1021  function supprime_img($letexte) {
1022      $message = _T('img_indisponible');
1023      return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'>,i',
1024          "($message)", $letexte);
1025  }
1026  
1027  // traite les modeles (dans la fonction typo), en remplacant
1028  // le raccourci <modeleN|parametres> par la page calculee a
1029  // partir du squelette modeles/modele.html
1030  // Le nom du modele doit faire au moins trois caracteres (evite <h2>)
1031  // Si $doublons==true, on repere les documents sans calculer les modeles
1032  // mais on renvoie les params (pour l'indexation par le moteur de recherche)
1033  // http://doc.spip.org/@traiter_modeles
1034  
1035  define('_RACCOURCI_MODELE', 
1036       '(<([a-z_-]{3,})' # <modele
1037      .'\s*([0-9]*)\s*' # id
1038      .'([|](?:<[^<>]*>|[^>])*)?' # |arguments (y compris des tags <...>)
1039      .'>)' # fin du modele >
1040      .'\s*(<\/a>)?' # eventuel </a>
1041         );
1042  
1043  define('_RACCOURCI_MODELE_DEBUT', '/^' . _RACCOURCI_MODELE .'/is');
1044  
1045  // http://doc.spip.org/@traiter_modeles
1046  function traiter_modeles($texte, $doublons=false, $echap='') {
1047      // detecter les modeles (rapide)
1048      if (preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS',
1049      $texte, $matches, PREG_SET_ORDER)) {
1050          include_spip('public/assembler');
1051          foreach ($matches as $match) {
1052              // Recuperer l'appel complet (y compris un eventuel lien)
1053              // $regs : 1 => modele, 2 => type, 3 => id, 4 => params, 5 => a
1054              $a = strpos($texte,$match[0]);
1055              preg_match(_RACCOURCI_MODELE_DEBUT, substr($texte, $a), $regs);
1056  
1057              if ($regs[5] AND preg_match(
1058              ',<a\s[^<>]*>\s*$,i', substr($texte, 0, $a), $r)) {
1059                  $lien = array(
1060                      extraire_attribut($r[0],'href'),
1061                      extraire_attribut($r[0],'class')
1062                  );
1063                  $a -= strlen($r[0]);
1064                  $cherche = $r[0].$regs[0];
1065              } else {
1066                  $lien = false;
1067                  $cherche = $regs[1];
1068              }
1069  
1070              // calculer le modele
1071              # hack articles_edit, breves_edit, indexation
1072              if ($doublons)
1073                  $texte .= preg_replace(',[|][^|=]*,s',' ',$regs[4]);
1074              # version normale
1075              else {
1076                  $modele = inclure_modele($regs[2], $regs[3], $regs[4], $lien);
1077  
1078                  // le remplacer dans le texte
1079                  if ($modele !== false) {
1080                      $modele = protege_js_modeles($modele);
1081                      $rempl = code_echappement($modele, $echap);
1082                      $texte = substr($texte, 0, $a)
1083                          . $rempl
1084                          . substr($texte, $a+strlen($cherche));
1085                  }
1086              }
1087  
1088              // hack pour tout l'espace prive
1089              if (!_DIR_RESTREINT)
1090              $GLOBALS['doublons_documents_inclus'][] = $regs[3];
1091          }
1092      }
1093  
1094      return $texte;
1095  }
1096  
1097  
1098  //
1099  // Une fonction pour fermer les paragraphes ; on essaie de preserver
1100  // des paragraphes indiques a la main dans le texte
1101  // (par ex: on ne modifie pas un <p align='center'>)
1102  //
1103  // deuxieme argument : forcer les <p> meme pour un seul paragraphe
1104  //
1105  // http://doc.spip.org/@paragrapher
1106  function paragrapher($letexte, $forcer=true) {
1107      $letexte = trim($letexte);
1108      if (!strlen($letexte))
1109          return '';
1110  
1111      if ($forcer OR (
1112      strstr($letexte,'<') AND preg_match(',<p\b,iS',$letexte)
1113      )) {
1114  
1115          // Ajouter un espace aux <p> et un "STOP P"
1116          // transformer aussi les </p> existants en <p>, nettoyes ensuite
1117          $letexte = preg_replace(',</?p(\s([^>]*))?'.'>,iS', '<STOP P><p \2>',
1118              '<p>'.$letexte.'<STOP P>');
1119  
1120          // Fermer les paragraphes (y compris sur "STOP P")
1121          $letexte = preg_replace(
1122              ',(<p\s.*)(</?(STOP P|'._BALISES_BLOCS.')[>[:space:]]),UimsS',
1123              "\n\\1</p>\n\\2", $letexte);
1124  
1125          // Supprimer les marqueurs "STOP P"
1126          $letexte = str_replace('<STOP P>', '', $letexte);
1127  
1128          // Reduire les blancs dans les <p>
1129          // Do not delete multibyte utf character just before </p> having last byte equal to whitespace  
1130          $u = ($GLOBALS['meta']['charset']=='utf-8' && test_pcre_unicode()) ? 'u':'S';
1131          $letexte = preg_replace(
1132          ',(<p(>|\s[^>]*)>)\s*|\s*(</p[>[:space:]]),'.$u.'i', '\1\3',
1133              $letexte);
1134  
1135          // Supprimer les <p xx></p> vides
1136          $letexte = preg_replace(',<p\s[^>]*></p>\s*,iS', '',
1137              $letexte);
1138  
1139          // Renommer les paragraphes normaux avec class="spip"
1140          $letexte = str_replace('<p >', '<p class="spip">',
1141              $letexte);
1142  
1143      }
1144  
1145      return $letexte;
1146  }
1147  
1148  //
1149  // Inserer un lien a partir du preg_match du raccourci [xx->url]
1150  // $regs:
1151  // 0=>tout le raccourci
1152  // 1=>texte (ou texte|hreflang ou texte|bulle ou texte|bulle{hreflang})
1153  // 2=>double fleche (historiquement, liens ouvrants)
1154  // 3=>url
1155  //
1156  // http://doc.spip.org/@traiter_raccourci_lien
1157  function traiter_raccourci_lien($regs) {
1158  
1159      $bulle = $hlang = '';
1160      // title et hreflang donnes par le raccourci ?
1161      if (preg_match(',^(.*?)([|]([^<>]*?))?([{]([a-z_]+)[}])?$,', $regs[1], $m)) {
1162          // |infobulle ?
1163          if ($m[2])
1164              $bulle = ' title="'.texte_backend($m[3]).'"';
1165          // {hreflang} ?
1166          if ($m[4]) {
1167              // si c'est un code de langue connu, on met un hreflang
1168              include_spip('inc/lang');
1169              if (traduire_nom_langue($m[5]) <> $m[5]) {
1170                  $hlang = $m[5];
1171              }
1172              // sinon c'est un italique
1173              else {
1174                  $m[1] .= $m[4];
1175              }
1176          }
1177          // S'il n'y a pas de hreflang sous la forme {}, ce qui suit le |
1178          // est peut-etre une langue
1179          else if (preg_match(',^[a-z_]+$,', $m[3])) {
1180              // si c'est un code de langue connu, on met un hreflang
1181              // mais on laisse le title (c'est arbitraire tout ca...)
1182              include_spip('inc/lang');
1183              if (traduire_nom_langue($m[3]) <> $m[3]) {
1184                  $hlang = $m[3];
1185              }
1186          }
1187          $regs[1] = $m[1];
1188      }
1189  
1190      list ($lien, $class, $texte, $lang) = calculer_url($regs[3], $regs[1], 'tout');
1191  
1192      // Si l'objet n'est pas de la langue courante, on ajoute hreflang
1193      if (!$hlang AND $lang AND ($lang!=$GLOBALS['spip_lang'])) $hlang=$lang;
1194      $hreflang = $hlang ? ' hreflang="'.$hlang.'"' : '';
1195  
1196      # ici bien passer le lien pour traiter [<doc3>->url]
1197      return typo("<a href=\"$lien\" class=\"$class\"$hreflang$bulle>"
1198          . $texte
1199          . "</a>");
1200  }
1201  
1202  // Fonction pour les champs chapo commencant par =,  redirection qui peut etre:
1203  // 1. un raccourci Spip habituel (premier If) [texte->TYPEnnn]
1204  // 2. un ultra raccourci TYPEnnn voire nnn (article) (deuxieme If)
1205  // 3. une URL std
1206  // renvoie une tableau structure comme ci-dessus mais sans calcul d'URL
1207  // (cf fusion de sauvegardes)
1208  
1209  define('_RACCOURCI_CHAPO', ',^(\W*)(\W*)(\w*\d+([?#].*)?)$,');
1210  
1211  // http://doc.spip.org/@chapo_redirige
1212  function chapo_redirige($chapo)
1213  {
1214      if (!preg_match(_RACCOURCI_LIEN, $chapo, $m))
1215          if (!preg_match(_RACCOURCI_CHAPO, $chapo, $m))
1216              $m = array('','','',$chapo);
1217      return $m;
1218  }
1219  
1220  // Regexp des raccouris, aussi utilisee pour la fusion de sauvegarde Spip
1221  define('_RACCOURCI_LIEN', ",\[([^][]*)->(>?)([^]]*)\],msS");
1222  
1223  // Nettoie un texte, traite les raccourcis spip, la typo, etc.
1224  // http://doc.spip.org/@traiter_raccourcis
1225  function traiter_raccourcis($letexte) {
1226      global $debut_intertitre, $fin_intertitre, $ligne_horizontale, $url_glossaire_externe;
1227      global $compt_note;
1228      global $marqueur_notes;
1229      global $ouvre_ref;
1230      global $ferme_ref;
1231      global $ouvre_note;
1232      global $ferme_note;
1233      global $lang_dir;
1234      static $notes_vues;
1235      static $tester_variables=0;
1236  
1237      // Appeler les fonctions de pre_traitement
1238      $letexte = pipeline('pre_propre', $letexte);
1239      // old style
1240      if (function_exists('avant_propre'))
1241          $letexte = avant_propre($letexte);
1242  
1243      // Verifier les variables de personnalisation
1244      if (!$tester_variables++) {
1245          tester_variable('debut_intertitre', "\n<h3 class=\"spip\">");
1246          tester_variable('fin_intertitre', "</h3>\n");
1247          tester_variable('ligne_horizontale', "\n<hr class=\"spip\" />\n");
1248          tester_variable('ouvre_ref', '&nbsp;[');
1249          tester_variable('ferme_ref', ']');
1250          tester_variable('ouvre_note', '[');
1251          tester_variable('ferme_note', '] ');
1252          tester_variable('url_glossaire_externe',
1253              "http://@lang@.wikipedia.org/wiki/");
1254          tester_variable('les_notes', '');
1255          tester_variable('compt_note', 0);
1256          tester_variable('toujours_paragrapher', false);
1257      }
1258  
1259      // Gestion de la <poesie>
1260      if (preg_match_all(",<(poesie|poetry)>(.*)<\/(poesie|poetry)>,UimsS",
1261      $letexte, $regs, PREG_SET_ORDER)) {
1262          foreach ($regs as $reg) {
1263              $lecode = preg_replace(",\r\n?,S", "\n", $reg[2]);
1264              $lecode = ereg_replace("\n[[:space:]]*\n", "\n&nbsp;\n",$lecode);
1265              $lecode = "<div class=\"spip_poesie\">\n<div>".ereg_replace("\n+", "</div>\n<div>", trim($lecode))."</div>\n</div>\n\n";
1266              $letexte = str_replace($reg[0], $lecode, $letexte);
1267          }
1268      }
1269  
1270      // Harmoniser les retours chariot
1271      $letexte = preg_replace(",\r\n?,S", "\n", $letexte);
1272  
1273      // Recuperer les para HTML
1274      $letexte = preg_replace(",<p[>[:space:]],iS", "\n\n\\0", $letexte);
1275      $letexte = preg_replace(",</p[>[:space:]],iS", "\\0\n\n", $letexte);
1276  
1277      //
1278      // Notes de bas de page
1279      //
1280      $mes_notes = '';
1281      $regexp = ', *\[\[(.*?)\]\],msS';
1282      if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER))
1283      foreach ($matches as $regs) {
1284          $note_source = $regs[0];
1285          $note_texte = $regs[1];
1286          $num_note = false;
1287  
1288          // note auto ou pas ?
1289          if (preg_match(",^ *<([^>]*)>,", $note_texte, $regs)){
1290              $num_note = $regs[1];
1291              $note_texte = str_replace($regs[0], "", $note_texte);
1292          } else {
1293              $compt_note++;
1294              $num_note = $compt_note;
1295          }
1296  
1297          // preparer la note
1298          if ($num_note) {
1299              if ($marqueur_notes) // quand il y a plusieurs series
1300                                   // de notes sur une meme page
1301                  $mn = $marqueur_notes.'-';
1302              $ancre = $mn.rawurlencode($num_note);
1303  
1304              // ne mettre qu'une ancre par appel de note (XHTML)
1305              if (!$notes_vues[$ancre]++)
1306                  $name_id = " name=\"nh$ancre\" id=\"nh$ancre\"";
1307              else
1308                  $name_id = "";
1309  
1310              $lien = "<a href=\"#nb$ancre\"$name_id class=\"spip_note\">";
1311  
1312              // creer le popup 'title' sur l'appel de note
1313              if ($title = supprimer_tags(propre($note_texte))) {
1314                  $title = $ouvre_note.$num_note.$ferme_note.$title;
1315                  $title = couper($title,80);
1316                  $lien = inserer_attribut($lien, 'title', $title);
1317              }
1318  
1319              $insert = "$ouvre_ref$lien$num_note</a>$ferme_ref";
1320  
1321              // on l'echappe
1322              $insert = code_echappement($insert);
1323  
1324              $appel = "$ouvre_note<a href=\"#nh$ancre\" name=\"nb$ancre\" class=\"spip_note\" title=\"" . _T('info_notes') . " $ancre\">$num_note</a>$ferme_note";
1325          } else {
1326              $insert = '';
1327              $appel = '';
1328          }
1329  
1330          // l'ajouter "tel quel" (echappe) dans les notes
1331          if ($note_texte) {
1332              if ($mes_notes)
1333                  $mes_notes .= "\n\n";
1334              $mes_notes .= code_echappement($appel) . $note_texte;
1335          }
1336  
1337          // dans le texte, mettre l'appel de note a la place de la note
1338          $pos = strpos($letexte, $note_source);
1339          $letexte = substr($letexte, 0, $pos) . $insert
1340              . substr($letexte, $pos + strlen($note_source));
1341      }
1342  
1343      //
1344      // Raccourcis automatiques [?SPIP] vers un glossaire
1345      // (on traite ce raccourci en deux temps afin de ne pas appliquer
1346      //  la typo sur les URLs, voir raccourcis liens ci-dessous)
1347      //
1348      if ($url_glossaire_externe) {
1349          $regexp = "|\[\?+([^][<>]+)\]|S";
1350          if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER))
1351          foreach ($matches as $regs) {
1352              $terme = trim($regs[1]);
1353              $terme_underscore = preg_replace(',\s+,', '_', $terme);
1354              // faire sauter l'eventuelle partie "|bulle d'aide" du lien
1355              // cf. http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Conventions_sur_les_titres
1356              $terme_underscore = preg_replace(',[|].*,', '', $terme_underscore);
1357              if (strstr($url_glossaire_externe,"%s"))
1358                  $url = str_replace("%s", rawurlencode($terme_underscore),
1359                      $url_glossaire_externe);
1360              else
1361                  $url = $url_glossaire_externe.$terme_underscore;
1362              $url = str_replace("@lang@", $GLOBALS['spip_lang'], $url);
1363              $code = '['.$terme.'->?'.$url.']';
1364  
1365              // Eviter les cas particulier genre "[?!?]"
1366              if (preg_match(',[a-z],i', $terme))
1367                  $letexte = str_replace($regs[0], $code, $letexte);
1368          }
1369      }
1370  
1371  
1372      //
1373      // Raccourcis ancre [#ancre<-]
1374      //
1375      $regexp = "|\[#?([^][]*)<-\]|S";
1376      if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER))
1377      foreach ($matches as $regs)
1378          $letexte = str_replace($regs[0],
1379          '<a name="'.entites_html($regs[1]).'"></a>', $letexte);
1380  
1381      //
1382      // Raccourcis liens [xxx->url]
1383      // Note : complique car c'est ici qu'on applique typo(),
1384      // et en plus on veut pouvoir les passer en pipeline
1385      //
1386  
1387      $inserts = array();
1388      if (preg_match_all(_RACCOURCI_LIEN, $letexte, $matches, PREG_SET_ORDER)) {
1389          $i = 0;
1390          foreach ($matches as $regs) {
1391              $inserts[++$i] = traiter_raccourci_lien($regs);
1392              $letexte = str_replace($regs[0], "@@SPIP_ECHAPPE_LIEN_$i@@",
1393                  $letexte);
1394          }
1395      }
1396  
1397      $letexte = typo($letexte, /* echap deja fait, accelerer */ false);
1398  
1399      foreach ($inserts as $i => $insert) {
1400          $letexte = str_replace("@@SPIP_ECHAPPE_LIEN_$i@@", $insert, $letexte);
1401      }
1402  
1403  
1404      //
1405      // Tableaux
1406      //
1407  
1408      // ne pas oublier les tableaux au debut ou a la fin du texte
1409      $letexte = preg_replace(",^\n?[|],S", "\n\n|", $letexte);
1410      $letexte = preg_replace(",\n\n+[|],S", "\n\n\n\n|", $letexte);
1411      $letexte = preg_replace(",[|](\n\n+|\n?$),S", "|\n\n\n\n", $letexte);
1412  
1413      // traiter chaque tableau
1414      if (preg_match_all(',[^|](\n[|].*[|]\n)[^|],UmsS', $letexte,
1415      $regs, PREG_SET_ORDER))
1416      foreach ($regs as $tab) {
1417          $letexte = str_replace($tab[1], traiter_tableau($tab[1]), $letexte);
1418      }
1419  
1420      //
1421      // Ensemble de remplacements implementant le systeme de mise
1422      // en forme (paragraphes, raccourcis...)
1423      //
1424  
1425      $letexte = "\n".trim($letexte);
1426  
1427      // les listes
1428      if (ereg("\n-[*#]", $letexte))
1429          $letexte = traiter_listes($letexte);
1430  
1431      // Puce
1432      if (strpos($letexte, "\n- ") !== false)
1433          $puce = definir_puce();
1434      else $puce = '';
1435  
1436      // Proteger les caracteres actifs a l'interieur des tags html
1437      $protege = "{}_-";
1438      $illegal = "\x1\x2\x3\x4";
1439      if (preg_match_all(",</?[a-z!][^<>]*[".preg_quote($protege)."][^<>]*>,imsS",
1440      $letexte, $regs, PREG_SET_ORDER)) {
1441          foreach ($regs as $reg) {
1442              $insert = $reg[0];
1443              // hack: on transforme les caracteres a proteger en les remplacant
1444              // par des caracteres "illegaux". (cf corriger_caracteres())
1445              $insert = strtr($insert, $protege, $illegal);
1446              $letexte = str_replace($reg[0], $insert, $letexte);
1447          }
1448      }
1449  
1450      // autres raccourcis
1451      $cherche1 = array(
1452          /* 0 */     "/\n(----+|____+)/S",
1453          /* 1 */     "/\n-- */S",
1454          /* 2 */     "/\n- */S",
1455          /* 3 */     "/\n_ +/S",
1456          /* 4 */   "/(^|[^{])[{][{][{]/S",
1457          /* 5 */   "/[}][}][}]($|[^}])/S",
1458          /* 6 */     "/(( *)\n){2,}(<br\s*\/?".">)?/S",
1459          /* 7 */     "/[{][{]/S",
1460          /* 8 */     "/[}][}]/S",
1461          /* 9 */     "/[{]/S",
1462          /* 10 */    "/[}]/S",
1463          /* 11 */    "/(?:<br\s*\/?".">){2,}/S",
1464          /* 12 */    "/<p>\n*(?:<br\s*\/?".">\n*)*/S",
1465          /* 13 */    "/<quote>/S",
1466          /* 14 */    "/<\/quote>/S",
1467          /* 15 */    "/<\/?intro>/S"
1468      );
1469      $remplace1 = array(
1470          /* 0 */     "\n\n$ligne_horizontale\n\n",
1471          /* 1 */     "\n<br />&mdash;&nbsp;",
1472          /* 2 */     "\n<br />$puce&nbsp;",
1473          /* 3 */     "\n<br />",
1474          /* 4 */     "\$1\n\n$debut_intertitre",
1475          /* 5 */     "$fin_intertitre\n\n\$1",
1476          /* 6 */     "<p>",
1477          /* 7 */     "<strong class=\"spip\">",
1478          /* 8 */     "</strong>",
1479          /* 9 */     "<i class=\"spip\">",
1480          /* 10 */    "</i>",
1481          /* 11 */    "<p>",
1482          /* 12 */    "<p>",
1483          /* 13 */    "<blockquote class=\"spip\"><p>",
1484          /* 14 */    "</blockquote><p>",
1485          /* 15 */    ""
1486      );
1487      $letexte = preg_replace($cherche1, $remplace1, $letexte);
1488      $letexte = preg_replace("@^ <br />@S", "", $letexte);
1489  
1490      // Retablir les caracteres proteges
1491      $letexte = strtr($letexte, $illegal, $protege);
1492  
1493      // Fermer les paragraphes ; mais ne pas en creer si un seul
1494      $letexte = paragrapher($letexte, $GLOBALS['toujours_paragrapher']);
1495  
1496      // Appeler les fonctions de post-traitement
1497      $letexte = pipeline('post_propre', $letexte);
1498      // old style
1499      if (function_exists('apres_propre'))
1500          $letexte = apres_propre($letexte);
1501  
1502      if ($mes_notes) traiter_les_notes($mes_notes);
1503  
1504      return $letexte;
1505  }
1506  
1507  // http://doc.spip.org/@traiter_les_notes
1508  function traiter_les_notes($mes_notes) {
1509      $mes_notes = propre('<p>'.$mes_notes);
1510      $mes_notes = str_replace(
1511          '<p class="spip">', '<p class="spip_note">', $mes_notes);
1512      $GLOBALS['les_notes'] .= $mes_notes;
1513  }
1514  
1515  
1516  // Filtre a appliquer aux champs du type #TEXTE*
1517  // http://doc.spip.org/@propre
1518  function propre($letexte) {
1519      if (!$letexte) return $letexte;
1520  
1521      // Echapper les <a href>, <html>...< /html>, <code>...< /code>
1522      $letexte = echappe_html($letexte);
1523  
1524      // Traiter le texte
1525      $letexte = traiter_raccourcis($letexte);
1526  
1527      // Reinserer les echappements
1528      $letexte = echappe_retour($letexte);
1529  
1530      // Vider les espaces superflus
1531      $letexte = trim($letexte);
1532  
1533      // Dans l'espace prive, securiser ici
1534      if (!_DIR_RESTREINT)
1535          $letexte = interdire_scripts($letexte);
1536  
1537      // Reinserer le javascript de confiance (venant des modeles)
1538      if (defined('_PROTEGE_JS_MODELES'))
1539          $letexte = echappe_retour($letexte,"javascript"._PROTEGE_JS_MODELES);
1540  
1541      return $letexte;
1542  }
1543  
1544  ?>


Généré le : Wed Nov 21 10:20:27 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics