inc-admin.php3 inc-arg-squel.php3 inc-balises.php3 inc-calcul-outils.php3 inc-calcul-squel.php3 inc-calcul.php3 inc-calcul_html4.php inc-calcul_mysql3.php inc-compilo-debug.php3 inc-compilo-index.php3 inc-compilo.php3 inc-criteres.php3 inc-f

Update of /home/spip-cvs/spip
In directory alan:/tmp/cvs-serv5332

Modified Files:
  inc-admin.php3 inc-calcul.php3 inc-reqsql-squel.php3
Added Files:
  inc-balises.php3 inc-calcul-outils.php3 inc-compilo-debug.php3
  inc-compilo-index.php3 inc-compilo.php3 inc-criteres.php3
Removed Files:
  inc-arg-squel.php3 inc-calcul-squel.php3 inc-calcul_html4.php
  inc-calcul_mysql3.php inc-form-squel.php3
Log Message:
codes mouvants (ce n'est qu'un début)
http://thread.gmane.org/gmane.comp.web.spip.devel/20211

à noter : critere {doublons a} indépendant de {doublons b}

--- NEW FILE: inc-criteres.php3 ---
<?php

//
// Definition des {criteres} d'une boucle
//

// Ce fichier ne sera execute qu'une fois
if (defined("_INC_CRITERES")) return;
define("_INC_CRITERES", "1");

// {racine}
// http://www.spip.net/@racine
function critere_racine_dist($param, $not, &$boucle, $infos) {
  global $table_des_tables;

  if ($param != 'racine' OR $not)
    return "erreur";

  $boucle->where[] = $infos['id_table'].".id_parent='0'";

}

// {exclus}
// http://www.spip.net/@exclus
function critere_exclus_dist($param, $not, &$boucle, $infos) {

  if ($param != 'exclus' OR $not)
    return "erreur";

  $boucle->where[] = $infos['id_field']."!='\"."
  . calculer_argument_precedent($infos['idb'],
    $infos['primary'], $infos['boucles']) . ".\"'";

}

// {doublons} ou {unique}
// http://www.spip.net/@doublons
function critere_doublons_dist($param, $not, &$boucle, $infos) {

  if (!preg_match("/(doublons|unique)[[:space:]]*([a-z_0-9]*)/i",
  $param, $match))
    return "erreur";

  $boucle->doublons = $infos['type'].$match[2];
  $boucle->where[] = '" .' .
    "calcul_mysql_in('".$infos['id_field']."', "
    .'"0".$doublons[\''.$boucle->doublons."'], 'NOT') . \"";
}

// {lang_select}
// http://www.spip.net/@lang_select
function critere_lang_select_dist($param, $not, &$boucle, $infos) {
  if (preg_match('/lang_select(=(oui|non))?$/i', $param, $match)) {
    if (!$lang_select = $match[3])
      $lang_select = 'oui';
    if ($not)
      $lang_select = ($lang_select=='oui')?'non':'oui';
    $boucle->lang_select = $lang_select;
  }
  else return "erreur";
}

// {debut_xxx}
// http://www.spip.net/@debut_
function critere_debut_dist($param, $not, &$boucle, $infos) {
  if (ereg('^debut([-_a-zA-Z0-9]+),([0-9]*)$', $param, $match)) {
    $debut_lim = "debut".$match[1];
    $boucle->limit =
      'intval($GLOBALS["'.$debut_lim.'"]).",'.$match[2] .'"' ;
  }
  else return "erreur";
}

// {recherche}
// http://www.spip.net/@recherche
function critere_recherche_dist($param, $not, &$boucle, $infos) {
  $boucle->from[] = "index_".$infos['id_table']." AS rec";
  $boucle->select[] = 'SUM(rec.points + 100*(" .' .
    'calcul_mysql_in("rec.hash",
    calcul_branche($hash_recherche_strict),"") . "))
    AS points';

  // horrible hack du aux id_forum = spip_forum et id_article=spip_articleS
  // en fait il faudrait la fonction inverse de table_objet()
  $id = 'id_'.preg_replace('/s$/', '', $infos['id_table']);

  // rec.id_article = articles.id_article
  $boucle->where[] = "rec.".$id
  . "=" . $infos['id_table'].'.'.$id;

  // group by articles.id_article
  $boucle->group = $infos['id_field'];

  // et la recherche trouve
  $boucle->where[] = '" .' . 'calcul_mysql_in("rec.hash",
    calcul_branche($hash_recherche),"") . "';

  // oui cette boucle est une boucle recherche, le noter dans la pile
  // (certes, c'est un peu lourd comme ecriture)
  $infos['boucles'][$infos['idb']]->hash = true;
}

// {inverse}
// http://www.spip.net/@inverse
function critere_inverse_dist($param, $not, &$boucle, $infos) {
  // Classement par ordre inverse
  if ($param == 'inverse' AND !$not) {
    if ($boucle->order)
      $boucle->order .= ".' DESC'";
    else
      return _L("&nbsp: inversion d'un ordre inexistant");
  } else
    return 'erreur';
}

// {traduction}
// http://www.spip.net/@traduction
function critere_traduction_dist($param, $not, &$boucle, $infos) {
  if ($param == 'traduction') {
    $boucle->where[] = $infos['id_table'].".id_trad > 0";
    $boucle->where[] = $infos['id_table'].".id_trad ='\"."
    . calculer_argument_precedent($infos['idb'], 'id_trad',
      $infos['boucles'])
    . ".\"'";

  } else
    return 'erreur';
}

// {origine_traduction}
// http://www.spip.net/@origine_traduction
function critere_origine_traduction_dist($param, $not, &$boucle, $infos) {
  if ($param == 'origine_traduction')
    $boucle->where[] = $infos['id_table'].".id_trad = "
    . $infos['id_field'];
  else
    return "erreur";
}

// {meme_parent}
// http://www.spip.net/@meme_parent
function critere_meme_parent_dist($param, $not, &$boucle, $infos) {
  if ($param != 'meme_parent')
    return "erreur";
  else {
    if ($infos['type'] == 'rubriques') {
      $boucle->where[] = $infos['id_table'].".id_parent='\"."
      . calculer_argument_precedent($infos['idb'], 'id_parent',
      $infos['boucles'])
      . ".\"'";
    } else if ($infos['type'] == 'forums') {
      $boucle->where[] = $infos['id_table'].".id_parent='\"."
      . calculer_argument_precedent($infos['idb'], 'id_parent',
      $infos['boucles'])
      . ".\"'";
      $boucle->where[] = $infos['id_table'].".id_parent > 0";
      $boucle->plat = true;
    } else
      return _L("erreur {meme_parent} ne s'applique pas &agrave;
      d'autres boucles que (FORUMS) ou (RUBRIQUES)");
  }
}

// {branche ?}
// http://www.spip.net/@branche
function critere_branche_dist($param, $not, &$boucle, $infos) {
  if (preg_match('/branche[[:space:]]*([?])?$/i', $param, $regs)) {
    $c = "calcul_mysql_in('".$infos['id_table'].".id_rubrique',
    calcul_branche(" . calculer_argument_precedent($infos['idb'],
    'id_rubrique', $infos['boucles']) . "), '')";
    if (!$regs[1])
      $where = "\". $c .\"" ;
    else
      $where = "\".("
      . calculer_argument_precedent($infos['idb'], 'id_rubrique',
      $infos['boucles'])."? $c : 1).\"";

    if ($not)
      $boucle->where[] = "NOT($where)";
    else
      $boucle->where[] = "$where";
  } else
    return "erreur";
}

// Tri : {par xxxx}
// http://www.spip.net/@par
function critere_par_dist($param, $not, &$boucle, $infos) {
  if ($not)
    return "erreur";

  preg_match('/par[[:space:]]*(.*)/ims', $param, $regs);
  $tri = trim($regs[1]);

  // par hasard
  if ($tri == 'hasard') {
    // on pourrait peut-etre passer a "RAND() AS alea" ?
    $boucle->select[] = "MOD(".$infos['id_field']." * UNIX_TIMESTAMP(),
    32767) & UNIX_TIMESTAMP() AS alea";
    $boucle->order = "'alea'";
  }

  // par titre_mot
  else if ($tri == 'titre_mot') {
    $boucle->order= "'mots.titre'";
  }

  // par type_mot
  else if ($tri == 'type_mot'){
    $boucle->order= "'mots.type'";
  }
  // par points
  else if ($tri == 'points'){
    $boucle->order= "'points'";
  }
  // par num champ(, suite)
  else if (ereg("^num[[:space:]]+([^,]*)(,.*)?",$tri, $match2)) {
    $boucle->select[] = "0+".$infos['id_table'].".".$match2[1]." AS num";
    $boucle->order = "'num".texte_script($match2[2])."'";
  }
  // par champ
  else if (ereg("^[a-z0-9]+$", $tri)) {
    if ($tri == 'date')
      $tri = $GLOBALS['table_date'][$infos['type']];
    $boucle->order = "'".$infos['id_table'].".".$tri."'";
  }
  // tris par critere bizarre
  // (formule composee, virgules, etc).
  else {
    $boucle->order = "'".texte_script($tri)."'";
  }
}

function calculer_criteres ($idb, &$boucles) {
  global $tables_relations, $table_primary, $table_des_tables, $table_date;
  $boucle = &$boucles[$idb]; # nom de la boucle
  $type = $boucle->type_requete; # articles
  $params = $boucle->param;
  $id_table = $table_des_tables[$type]; # articles -> 'table'
  $primary = $table_primary[$type]; # id_article -> 'id'
  $id_field = $id_table . "." . $primary; # articles.id_article -> 'table_id'

  // les infos complementaires a passer aux fonctions critere_xxx
  $infos = array(
    'type' => $type, # (articles)
    'id_table' => $id_table, # 'table'
    'id_field' => $id_field, # 'table_id'
    'primary' => $primary, # 'id'
    'boucles' => &$boucles, # boucles
    'idb' => $idb # 'nom_boucle'
  );

  // Cas specifique pour la hierarchie : on cree des criteres supplementaires
  // $hierarchie sera calculee par une fonction de inc-calcul-mysql
  if ($type == 'hierarchie') {
    $boucle->where[] = 'id_rubrique IN ($hierarchie)';
    $boucle->select[] = 'FIND_IN_SET(id_rubrique, \'$hierarchie\')-1 AS rang';
    $boucle->order = 'rang';

    // Supprimer le parametre id_article/id_rubrique/id_syndic
    // qui est superfetatoire (mais indique dans la doc)
    $params2 = array();
    foreach($params as $param)
      if (!ereg('^id_(article|syndic|rubrique)$', $param))
        $params2[]=$param;
    $params = $params2;

    $boucle->hierarchie = '$hierarchie = calculer_hierarchie('
    .calculer_argument_precedent($idb, 'id_rubrique', $boucles)
    .', false);';
  }

  //
  // Traitement de la liste des criteres
  //
  if (is_array($params)) {
    foreach($params as $param) {

      // Analyse du critere
      preg_match("/^([!]?)[[:space:]]*(debut|([a-z_]+))/ism",
        $param, $match);
      $critere = $match[2];
      $not = ($match[1] == '!');

      // synonymes ?
      $synonymes = array('unique'=>'doublons');
      if ($synonymes[$critere]) $critere = $synonymes[$critere];

      // critere personnalise ?
      $f = "critere_".$critere;
      if (!function_exists($f))
        $f .= '_dist';

      // fonction critere standard ?
      if (function_exists($f)) {
        if ($erreur = $f($param, $not, $boucle, $infos)) {
          include_local('inc-admin.php3');
          erreur_squelette(_T('info_erreur_squelette'),
          _L("&nbsp: erreur dans le critere {$param} : $erreur"),
          $idb);
        }
      }
      
      # Criteres a passer en fonction critere_xxx_dist
      else if (ereg('^([0-9]+)/([0-9]+)$', $param, $match)) {
        $boucle->partie = $match[1];
        $boucle->total_parties = $match[2];
        $boucle->mode_partie = '/';
      }
      else if (ereg('^(([0-9]+)|n)(-([0-9]+))?,(([0-9]+)|n)(-([0-9]+))?$', $param, $match)) {
        if (($match[2]!='') && ($match[6]!=''))
          $boucle->limit = $match[2].','.$match[6];
        else {
          $boucle->partie =
            ($match[1] != 'n') ? $match[1] :
            ($match[4] ? $match[4] : 0);
          $boucle->total_parties =
            ($match[5] != 'n') ? $match[5] :
            ($match[8] ? $match[8] : 0);
          $boucle->mode_partie =
          (($match[1]=='n')?'-':'+').(($match[5]=='n')?'-':'+');
        }
      }

      // Restriction de valeurs (implicite ou explicite)
      else if (eregi('^([a-z_]+) *(\??)((!?)(<=?|>=?|==?|IN) *"?([^<>=!"]*))?"?$', $param, $match)) {
        // Variable comparee
        $col = $match[1];
        $col_table = $id_table;
        // Valeur de comparaison
        if ($match[3])
          $val = calculer_param_dynamique($match[6], $boucles, $idb);
        else {
          $val = $match[1];
          // Si id_parent, comparer l'id_parent avec l'id_objet
          // de la boucle superieure
          if ($val == 'id_parent')
            $val = $table_primary[$type];
          // Si id_enfant, comparer l'id_objet avec l'id_parent
          // de la boucle superieure
          else if ($val == 'id_enfant')
            $val = 'id_parent';
          $val = calculer_argument_precedent($idb, $val, $boucles) ;
        }

        if (ereg('^\$',$val))
          $val = '" . addslashes(' . $val . ') . "';
        else
          $val = addslashes($val);

        // Traitement general des relations externes
        if ($s = $tables_relations[$type][$col]) {
          $col_table = $s;
          $boucle->from[] = "$col_table AS $col_table";
          $boucle->where[] = "$id_field=$col_table." . $table_primary[$type];
          $boucle->group = $id_field;
          $boucle->lien = true;
        }
        // Cas particulier pour les raccourcis 'type_mot' et 'titre_mot'
        else if ($type != 'mots'
        AND ($col == 'type_mot' OR $col == 'titre_mot'
        OR $col == 'id_groupe')) {
          if ($type == 'forums')
            $col_lien = "forum";
          else if ($type == 'syndication')
            $col_lien = "syndic";
          else
            $col_lien = $type;
          $boucle->from[] = "mots_$col_lien AS lien_mot";
          $boucle->from[] = 'mots AS mots';
          $boucle->where[] = "$id_field=lien_mot." . $table_primary[$type];
          $boucle->where[] = 'lien_mot.id_mot=mots.id_mot';
          $boucle->group = $id_field;
          $col_table = 'mots';

          $boucle->lien = true;
          if ($col == 'type_mot')
            $col = 'type';
          else if ($col == 'titre_mot')
            $col = 'titre';
          else if ($col == 'id_groupe')
            $col = 'id_groupe';
        }

        // Cas particulier : selection des documents selon l'extension
        if ($type == 'documents' AND $col == 'extension')
          $col_table = 'types_documents';
        // HACK : selection des documents selon mode 'image'
        // (a creer en dur dans la base)
        else if ($type == 'documents' AND $col == 'mode'
        AND $val == 'image')
          $val = 'vignette';
        // Cas particulier : lier les articles syndiques
        // au site correspondant
        else if ($type == 'syndic_articles' AND
        !ereg("^(id_syndic_article|titre|url|date|descriptif|lesauteurs)$",$col))
          $col_table = 'syndic';

        // Cas particulier : id_enfant => utiliser la colonne id_objet
        if ($col == 'id_enfant')
          $col = $table_primary[$type];
        // Cas particulier : id_secteur = id_rubrique pour certaines tables
        if (($type == 'breves' OR $type == 'forums') AND $col == 'id_secteur')
          $col = 'id_rubrique';

        // Cas particulier : expressions de date
        if (ereg("^(date|mois|annee|age|age_relatif|jour_relatif|mois_relatif|annee_relatif)(_redac)?$", $col, $regs)) {
          $col = $regs[1];
          if ($regs[2]) {
            $date_orig = $id_table . ".date_redac";
            $date_compare = '\'" . normaliser_date(' .
            calculer_argument_precedent($idb, 'date_redac', $boucles) .
            ') . "\'';
          }
          else {
            $date_orig = "$id_table." . $table_date[$type];
            $date_compare = '\'" . normaliser_date(' .
              calculer_argument_precedent($idb, 'date', $boucles) .
              ') . "\'';
          }

          if ($col == 'date')
            $col = $date_orig;
          else if ($col == 'mois') {
            $col = "MONTH($date_orig)";
            $col_table = '';
          }
          else if ($col == 'annee') {
            $col = "YEAR($date_orig)";
            $col_table = '';
          }
          else if ($col == 'age') {
            $col = calculer_param_date("now()", $date_orig);
            $col_table = '';
          }
          else if ($col == 'age_relatif') {
            $col = calculer_param_date($date_compare, $date_orig);
            $col_table = '';
          }
          else if ($col == 'jour_relatif') {
            $col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
            $date_orig . "), DAYOFMONTH(" . $date_compare .
            ")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
            $date_compare . ")-MONTH(" . $date_orig .
            "))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
            $date_orig . ")))";
            $col_table = '';
          }
          else if ($col == 'mois_relatif') {
            $col = "MONTH(" . $date_compare . ")-MONTH(" .
            $date_orig . ")+12*(YEAR(" . $date_compare .
            ")-YEAR(" . $date_orig . "))";
            $col_table = '';
          }
          else if ($col == 'annee_relatif') {
            $col = "YEAR(" . $date_compare . ")-YEAR(" .
            $date_orig . ")";
            $col_table = '';
          }
        }
  
        if ($type == 'forums' AND
        ($col == 'id_parent' OR $col == 'id_forum'))
          $boucle->plat = true;

        // Operateur de comparaison
        $op = $match[5];
        if (!$op)
          $op = '=';
        else if ($op == '==')
          $op = 'REGEXP';
        else if (strtoupper($op) == 'IN') {
          // traitement special des valeurs textuelles
          $val2 = split(",", $val);
          foreach ($val2 as $v) {
            $v = trim($v);
            if (ereg("^[0-9]+$",$v))
              $val3[] = $v;
            else
              $val3[] = "'$v'";
          }
          $val = join(',', $val3);
          $where = "$col IN ($val)";
          if ($match[4] == '!') {
            $where = "NOT ($where)";
          } else {
            if (!$boucle->order) {
              $boucle->order = 'rang';
              $boucle->select[] =
              "FIND_IN_SET($col, \\\"$val\\\") AS rang";
            }
          }
          $boucle->where[] = $where;
          $op = '';
        }
  
        if ($col_table)
          $col = "$col_table.$col";

        if ($op) {
          if ($match[4] == '!')
            $where = "NOT ($col $op '$val')";
          else
            $where = "($col $op '$val')";

          // operateur optionnel {lang?}
          if ($match[2]) {
            $champ = calculer_argument_precedent($idb, $match[1], $boucles) ;
            $where = "\".($champ ? \"$where\" : 1).\"";
          }

          $boucle->where[] = $where;
        }

      } // fin du if sur les restrictions de valeurs

      // Special rubriques
      else if ($param == 'meme_parent') {
        $boucle->where[] = "$id_table.id_parent='\"." .
          calculer_argument_precedent($idb, 'id_parent', $boucles) . ".\"'";
        if ($type == 'forums') {
          $boucle->where[] = "$id_table.id_parent > 0";
          $boucle->plat = true;
        }
      }
      else if (ereg("^branche *(\??)", $param, $regs)) {
        $c = "calcul_mysql_in('$id_table.id_rubrique',
        calcul_branche(" . calculer_argument_precedent($idb, 'id_rubrique',
        $boucles) . "), '')";
        if (!$regs[1])
          $boucle->where[] = "\". $c .\"" ;
        else
          $boucle->where[] = "\".(".calculer_argument_precedent($idb, 'id_rubrique', $boucles)."? $c : 1).\"";
      }
      // Selection du classement
      else if (ereg('^par[[:space:]]+([^}]*)$', $param, $match)) {
        $tri = trim($match[1]);
        if ($tri == 'hasard') { // par hasard
          $boucle->select[] = "MOD($id_field * UNIX_TIMESTAMP(),
          32767) & UNIX_TIMESTAMP() AS alea";
          $boucle->order = 'alea';
        }
        else if ($tri == 'titre_mot') { // par titre_mot
          $boucle->order= 'mots.titre';
        }
        else if ($tri == 'type_mot'){ // par type_mot
          $boucle->order= 'mots.type';
        }
        else if ($tri == 'points'){ // par points
          $boucle->order= 'points';
        }
        else if (ereg("^num[[:space:]]+([^,]*)(,.*)?",$tri, $match2)) {
          // par num champ
          $boucle->select[] = "0+$id_table.".$match2[1]." AS num";
          $boucle->order = "num".$match2[2];
        }
        else if (ereg("^[a-z0-9]+$", $tri)) { // par champ
          if ($tri == 'date')
            $tri = $table_date[$type];
          $boucle->order = "$id_table.$tri";
        }
        else {
          // tris par critere bizarre
          // (formule composee, virgules, etc).
          $boucle->order = $tri;
        }
      }
    }
  }
}

function calculer_param_date($date_compare, $date_orig) {
  return
  "LEAST((UNIX_TIMESTAMP(" .
  $date_compare .
  ")-UNIX_TIMESTAMP(" .
  $date_orig .
  "))/86400,\n\tTO_DAYS(" .
  $date_compare .
  ")-TO_DAYS(" .
  $date_orig .
  "),\n\tDAYOFMONTH(" .
  $date_compare .
  ")-DAYOFMONTH(" .
  $date_orig .
  ")+30.4368*(MONTH(" .
  $date_compare .
  ")-MONTH(" .
  $date_orig .
  "))+365.2422*(YEAR(" .
  $date_compare .
  ")-YEAR(" .
  $date_orig .
  ")))";
}

//
// Calculer les parametres
//
function calculer_param_dynamique($val, &$boucles, $idb) {
  if (ereg("^#([A-Za-z0-9_-]+)$", $val, $m)) {
    $c = calculer_champ('',$m[1], $idb, $boucles,$idb);
    if (ereg("[$]Pile[[][^]]+[]][[]'[^]]*'[]]", $c, $v))
      return $v[0];
    else {
      # erreur
      include_local("inc-admin.php3");
      erreur_squelette(_L("parametre dynamique inexistant ?"), '', $idb);
    }
  } else {
    if (ereg('^\$(.*)$',$val,$m))
      return '$Pile[0][\''. $m[1] ."']";
    else
      return $val;
  }
}

?>

Index: inc-calcul.php3

RCS file: /home/spip-cvs/spip/inc-calcul.php3,v
retrieving revision 1.96
retrieving revision 1.97
diff -u -d -r1.96 -r1.97
--- inc-calcul.php3 27 Aug 2004 04:27:02 -0000 1.96
+++ inc-calcul.php3 28 Aug 2004 23:17:42 -0000 1.97
@@ -15,8 +15,10 @@
include_ecrire("inc_lang.php3");
include_ecrire("inc_documents.php3");
include_ecrire("inc_forum.php3");
-include_local("inc-calcul_mysql3.php");
-include_local("inc-calcul_html4.php");
+include_local("inc-calcul-outils.php3");
+
+#include_local("inc-calcul_html"); # anciens noms des fichiers
+#include_local("inc-calcul_mysql");

// Ce fichier peut contenir une affectation de $dossier_squelettes indiquant
@@ -66,7 +68,12 @@
     }

     // sinon le compiler
- include_local("inc-calcul-squel.php3");
+ if ($GLOBALS['tradition']) {
+ include_local("inc-calcul-squel.php3");
+ }
+ else {
+ include_local("inc-compilo.php3");
+ }
     if (!lire_fichier ($sourcefile, $skel)) {
       // erreur webmaster : $fond ne correspond a rien
       include_ecrire ("inc_presentation.php3");
@@ -152,6 +159,7 @@
   // Calculer la page a partir du main() du skel compile
   $page = $fonc(array('cache' =>$cache),
     array($contexte),
+ /* obsolete avec les doublons de inc-compilo */
     array(
       'articles' => '0',
       'rubriques' => '0',
@@ -295,7 +303,7 @@
   serialize($page['signal']))." -->\n";

   // Enregistrer le fichier cache
- if ($delais > 0)
+ if ($delais > 0 AND empty($GLOBALS['HTTP_POST_VARS']))
     ecrire_fichier($chemin_cache, $signal.$page['texte']);

   return $page;
@@ -303,66 +311,6 @@

-# Fonctions appelees par les squelettes (insertion dans le code trop lourde)
-
-tester_variable('espace_logos',3); // HSPACE=xxx VSPACE=xxx pour les logos (#LOGO_ARTICLE)
-tester_variable('espace_images',3); // HSPACE=xxx VSPACE=xxx pour les images integrees
-
-//
-// Retrouver le logo d'un objet (et son survol)
-//
-
-function cherche_image($id_objet, $type_objet) {
- // cherche l'image liee a l'objet
- $on = cherche_image_nommee($type_objet.'on'.$id_objet);
-
- // cherche un survol
- $off =(!$on ? '' :
- cherche_image_nommee($type_objet.'off'.$id_objet));
-
- if (!$on)
- return false;
-
- return array($on, $off);
-}
-
-function cherche_logo_objet ($type, $id_objet, $on = false, $off = false, $flag_fichier=false) {
-
-spip_log("cherche logo $type $id_objet $on $off $flag_fichier");
- switch($type) {
- case 'ARTICLE':
- $logo = cherche_image($id_objet, 'art');
- break;
- case 'AUTEUR':
- $logo = cherche_image($id_objet, 'aut');
- break;
- case 'BREVE':
- $logo = cherche_image($id_objet, 'breve');
- break;
- case 'SITE':
- $logo = cherche_image($id_objet, 'site');
- break;
- case 'MOT':
- $logo = cherche_image($id_objet, 'mot');
- break;
- // recursivite
- case 'RUBRIQUE':
- if (!($logo = cherche_image ($id_objet, 'rub'))
- AND $id_objet > 0)
- $logo = cherche_logo_objet('RUBRIQUE',
- sql_parent($id_objet), true, true);
- break;
- default:
- spip_log("cherche_logo_objet: type '$type' inconnu");
- }
-
- // Quelles images sont demandees ?
- if (!$on) unset($logo[0]);
- if (!$off) unset($logo[1]);
-
- if ($logo[0] OR $logo[1])
- return $logo;
-}

// Fonction appelee par le skel pour assembler les balises
@@ -383,4 +331,59 @@
       return $texte;
}

+### A passer peut-etre dans inc_db_mysql
+// Cette fonction est systematiquement appelee par les squelettes
+// pour constuire une requete SQL de type "lecture" (SELECT) a partir
+// de chaque boucle.
+// Elle construit et exe'cute une reque^te SQL correspondant a` une balise
+// Boucle ; elle notifie une erreur SQL dans le flux de sortie et termine
+// le processus.
+// Sinon, retourne la ressource interrogeable par fetch_row ou fetch_array.
+// Elle peut etre re'de'finie pour s'interfacer avec d'autres serveurs SQL
+// Recoit en argument:
+// - le tableau des champs a` ramener
+// - le tableau des tables a` consulter
+// - le tableau des conditions a` remplir
+// - le crite`re de regroupement
+// - le crite`re de classement
+// - le crite`re de limite
+// - une sous-requete e'ventuelle (MySQL > 4.1)
+// - un compteur de sous-requete
+// - le nom de la table
+// - le nom de la boucle (pour le message d'erreur e'ventuel)
+
+
+function spip_abstract_select (
+ $select = array(), $from = array(), $where = '',
+ $groupby = '', $orderby = '', $limit = '',
+ $sousrequete = '', $cpt = '',
+ $table = '', $id = '') {
+
+ $DB = 'spip_';
+ $q = " FROM $DB" . join(", $DB", $from)
+ . (is_array($where) ? ' WHERE ' . join(' AND ', $where) : '')
+ . ($groupby ? " GROUP BY $groupby" : '')
+ . ($orderby ? "\nORDER BY $orderby" : '')
+ . ($limit ? "\nLIMIT $limit" : '');
+
+ if (!$sousrequete)
+ $q = " SELECT ". join(", ", $select) . $q;
+ else
+ $q = " SELECT S_" . join(", S_", $select)
+ . " FROM (" . join(", ", $select)
+ . ", COUNT(".$sousrequete.") AS compteur " . $q
+ .") AS S_$table WHERE compteur=" . $cpt;
+
+ //
+ // Erreur ? C'est du debug, ou une erreur du serveur
+ //
+ if (!($result = @spip_query($q))) {
+ include_local('inc-admin.php3');
+ echo erreur_requete_boucle($q, $id, $table);
+ }
+
+ # spip_log(spip_num_rows($result));
+ return $result;
+}
+
?>

--- NEW FILE: inc-compilo-index.php3 ---
<?php

// Definition des classes Boucle, Texte, Inclure, etc.,
// et fonctions de recherche et de reservation
// dans l'arborescence des boucles

// Ce fichier ne sera execute qu'une fois
if (defined("_INC_COMPILO_INDEX")) return;
define("_INC_COMPILO_INDEX", "1");

//
// encodage d'une boucle SPIP en un objet PHP
//
class Boucle {
  var $type = 'boucle';
  var $id_boucle, $id_parent;
  var $cond_avant, $milieu, $cond_apres, $cond_altern;
  var $lang_select;
  var $type_requete;
  var $param;
  var $separateur;
  var $doublons;
  var $partie, $total_parties,$mode_partie;
  var $externe = ''; # appel a partir d'une autre boucle (recursion)
  // champs pour la construction de la requete SQL
  var $tout = false;
  var $plat = false;
  var $select;
  var $from;
  var $where;
  var $limit;
  var $group = '';
  var $order = '';
  var $date = 'date' ;
  var $hash = false ;
  var $lien = false;
  var $sous_requete = false;
  var $compte_requete = 1;
  var $hierarchie = '';
  // champs pour la construction du corps PHP
  var $return;
  var $numrows = false;
}

class Texte {
  var $type = 'texte';
  var $texte;
}

class Inclure {
  var $type = 'include';
  var $fichier;
  var $params;
}

class Champ {
  var $type = 'champ';
  var $nom_champ;
  var $cond_avant, $cond_apres; // tableaux d'objets
  var $fonctions;
}

//
// Structure de donnees pour parler aux fonctions calcul_champ_TOTO
//
class ParamChamp {
  var $fonctions;
  var $nom_champ;
  var $id_boucle;
  var $boucles;
  var $id_mere;
  var $type_requete;
  var $code; // code du calcul
  var $process; // processeurs standards, exemple 'propre(%s)'
  var $etoile; // le champ a ete appele avec une etoile (booleen)
  var $type; // 'num'erique, 'h'=texte (html) ou 'p'=script (php) ?
            // -> definira les pre et post-traitements obligatoires

  function retour() {
    // Annuler les traitements si le champ est etoile
    if ($this->etoile) unset($this->process);

    $code_filtre = applique_filtres(
      $this->fonctions,
      $this->code,
      $this->id_boucle,
      $this->boucles,
      $this->id_mere,
      $this->type,
      $this->process
    );
    return $code_filtre;
  }
}

// index_pile retourne la position dans la pile du champ SQL $nom_champ
// en prenant la boucle la plus proche du sommet de pile (indique par $idb).
// Si on ne trouve rien, on considere que ca doit provenir du contexte
// (par l'URL ou l'include) qui a ete recopie dans Pile[0]
// (un essai d'affinage a debouche sur un bug vicieux)
// Si ca reference un champ SQL, on le memorise dans la structure $boucles
// afin de construire un requete SQL minimale (plutot qu'un brutal 'SELECT *')

include_ecrire('inc_serialbase.php3');

function index_pile($idb, $nom_champ, &$boucles) {
  global $exceptions_des_tables, $table_des_tables, $tables_principales;

  // Recherche d'un champ dans un etage superieur
  $i = 0;
  if ($c=strpos($nom_champ, ':')) {
    $idbs = substr($nom_champ, 0, $c);
    $nom_champ = substr($nom_champ, $c+1);
    while (($idb != $idbs) && $idb) {
      $i++;
      $idb = $boucles[$idb]->id_parent;
    }
  }

  $c = strtolower($nom_champ);
  // attention a la boucle nommee 0 ....
  while ($idb!== '') {
    #spip_log("Cherche: $nom_champ '$idb' '$c'");
    $r = $boucles[$idb]->type_requete;
    // indirection (pour les rares cas ou le nom de la table est /= du type)
    $t = $table_des_tables[$r];
    if (!$t)
      $t = $r; // pour les tables non Spip
    // $t est le nom PHP de cette table
    #spip_log("Go: idb='$idb' r='$r' c='$c' nom='$nom_champ'");
    $desc = $tables_principales[$t];
    if (!$desc) {
      include_local("inc-admin.php3");
      erreur_squelette(_L("Table SQL absente de \$tables_principales dans inc_serialbase"), $r, "'$idb'");
    }
    $excep = $exceptions_des_tables[$r][$c];
    if ($excep) {
      // entite SPIP alias d'un champ SQL
      if (!is_array($excep)) {
        $e = $excep;
      }
      // entite SPIP alias d'un champ dans une autre table SQL
      else {
        $t = $excep[0];
        $e = $excep[1];
      }
    }
    else {
      // $e est le type SQL de l'entree (ici utile comme booleen)
      // entite SPIP homonyme au champ SQL
      if ($desc['field'][$c])
        $e = $c;
      else
        $e = '';
    }

    #spip_log("Dans $idb ($t $e): $desc");

    // On l'a trouve
    if ($e) {
      $boucles[$idb]->select[] = $t . "." . $e;
      return '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $e . '\']';
    }

    // Sinon on remonte d'un cran
    $idb = $boucles[$idb]->id_parent;
    $i++;
  }

  #spip_log("Pas vu $nom_champ dans les " . count($boucles) . " boucles");
  // esperons qu'il y sera
  return('$Pile[0][\''.$nom_champ.'\']');
}

// cette fonction sert d'API pour demander le champ '$champ' dans la pile
function champ_sql($champ, $p) {
  return index_pile($p->id_boucle, $champ, $p->boucles);
}

# calculer_champ genere le code PHP correspondant a la balise Spip $nom_champ
# Retourne une EXPRESSION php
function calculer_champ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere, $etoile = false) {
  // Preparer les parametres
  $p = new ParamChamp;
  $p->fonctions = $fonctions;
  $p->nom_champ = $nom_champ;
  $p->id_boucle = $id_boucle;
  $p->boucles = &$boucles;
  $p->id_mere = $id_mere;
  $p->type = 'html';
  $p->process = '';
  $p->type_requete = $boucles[$id_boucle]->type_requete;

  // regarder s'il existe une fonction personnalisee balise_NOM()
  $f = 'balise_' . $nom_champ;
  if (function_exists($f))
    $p = $f($p);

  else {
  // regarder s'il existe une fonction standard balise_NOM_dist()
  $f = 'balise_' . $nom_champ . '_dist';
  if (function_exists($f))
    $p = $f($p);

  else {
  // S'agit-il d'un logo ? Une fonction speciale les traite tous
  if (ereg('^LOGO_', $nom_champ))
    $p = calcul_balise_logo($p);

  else {
  // On regarde ensuite s'il y a un champ SQL homonyme,
  // et on definit le type et les traitements
  $p->code = champ_sql($nom_champ, $p);
  if (($p->code) && ($p->code != '$Pile[0][\''.$nom_champ.'\']')) {

    // Par defaut basculer en numerique pour les #ID_xxx
    if (substr($nom_champ,0,3) == 'ID_') $p->type = 'num';
  }

  else {
  // si index_pile a ramene le choix par defaut,
  // ca doit plutot etre un champ SPIP non SQL,
  // ou ni l'un ni l'autre => on le renvoie sous la forme brute '#TOTO'
  $p->code = "'#$nom_champ'";
  $p->type = 'php'; // pas de traitement
  
  }}}}
  
  // Aller chercher les processeurs standards definis dans inc-champ-squel
  if (!$etoile)
    $p->process = champs_traitements($nom_champ);

  // Retourner l'expression php correspondant au champ + ses filtres
  return $p->retour();
}

// Genere l'application d'une liste de filtres
function applique_filtres ($fonctions, $code, $id_boucle, $boucles, $id_mere, $type ='html', $process='') {

  // pretraitements standards
  switch ($type) {
    case 'num':
      $code = "intval($code)";
      break;
    case 'php':
      break;
    case 'html':
    default:
      $code = "trim($code)";
      break;
  }

  // traitements standards
  if (strpos($process, '%s') !== false)
    $code = str_replace('%s', $code, $process);

  // Appliquer les filtres perso
  if ($fonctions) {
    foreach($fonctions as $fonc) {
      if ($fonc) {
        $arglist = '';
        if (ereg('([^\{\}]*)\{(.+)\}$', $fonc, $regs)) {
          $fonc = $regs[1];
          $args = $regs[2];
          while (ereg('([^,]+),?(.*)$', $args, $regs)) {
            $args = $regs[2];
            $arg = trim($regs[1]);
            if ($arg) {
              if ($arg[0] =='#')
                $arg = calculer_champ(array(), substr($arg,1),
                  $id_boucle, $boucles, $id_mere);
              else if ($arg[0] =='$')
                $arg = '$Pile[0][\'' . substr($arg,1) . "']";
              $arglist .= ','.$arg;
            }
          }
        }
        if (function_exists($fonc))
          $code = "$fonc($code$arglist)";
        else
          $code = "'".texte_script(
            _T('erreur_filtre', array('filtre' => $fonc))
          )."'";
      }
    }
  }

  // post-traitement securite
  if ($type == 'html')
    $code = "interdire_scripts($code)";

  return $code;
}

//
// Reserve les champs necessaires a la comparaison avec le contexte donne par
// la boucle parente ; attention en recursif il faut les reserver chez soi-meme
// ET chez sa maman
//
function calculer_argument_precedent($idb, $nom_champ, &$boucles) {

  // recursif ?
  if ($boucles[$idb]->externe)
    index_pile ($idb, $nom_champ, $boucles); // reserver chez soi-meme

  // reserver chez le parent et renvoyer l'habituel $Pile[$SP]['nom_champ']
  return index_pile ($boucles[$idb]->id_parent, $nom_champ, $boucles);
}

?>

Index: inc-admin.php3

RCS file: /home/spip-cvs/spip/inc-admin.php3,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -d -r1.23 -r1.24
--- inc-admin.php3 27 Aug 2004 04:54:12 -0000 1.23
+++ inc-admin.php3 28 Aug 2004 23:17:42 -0000 1.24
@@ -265,7 +265,8 @@
//
function erreur_squelette($message, $fautif, $lieu) {
   global $auteur_session, $debug_messages;
-
+ static $runs;
+
   // Drapeau pour interdire d'ecrire les fichiers dans le cache
   # define('spip_erreur_fatale', 'erreur_squelette');
   # En fait, a partir du moment ou l'erreur est dans le squelette,
@@ -289,6 +290,9 @@
     $debug_messages .= "<div style='position: fixed; top: 10px; left: 10px;
     z-index: 10000; background-color: pink;'>$message</div>";
   }
+
+ // Eviter les boucles infernales
+ if (++$runs > 4) die ($debug_messages);
}

?>

Index: inc-reqsql-squel.php3

RCS file: /home/spip-cvs/spip/inc-reqsql-squel.php3,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- inc-reqsql-squel.php3 27 Aug 2004 04:27:02 -0000 1.8
+++ inc-reqsql-squel.php3 28 Aug 2004 23:17:42 -0000 1.9
@@ -134,7 +134,7 @@
     "', $boucle->where) . '"')) .
     "), # WHERE
     '".addslashes($boucle->group)."', # GROUP
- '".addslashes($boucle->order)."', # ORDER
+ " . ($boucle->order ? $boucle->order : "''") .", # ORDER
     " . (strpos($boucle->limit, 'intval') === false ?
       "'$boucle->limit'" :
       $boucle->limit). ", # LIMIT

--- inc-calcul_mysql3.php DELETED ---

--- NEW FILE: inc-calcul-outils.php3 ---
(This appears to be a binary file; contents omitted.)

--- inc-arg-squel.php3 DELETED ---

--- inc-calcul_html4.php DELETED ---

--- inc-calcul-squel.php3 DELETED ---

--- NEW FILE: inc-compilo.php3 ---
<?php

//
// Fichier principal du compilateur de squelettes
//

// Ce fichier ne sera execute qu'une fois
if (defined("_INC_COMPILO")) return;
define("_INC_COMPILO", "1");

// Definition de la structure $p, et fonctions de recherche et de reservation
// dans l'arborescence des boucles
include_local("inc-compilo-index.php3"); # index ? structure ? pile ?
#include_local("inc-bcl-squel.php3"); # (anciens noms des fichiers)
#include_local("inc-index-squel.php3");

// definition des balises
include_local("inc-balises.php3");
#include_local("inc-logo-squel.php3");
#include_local("inc-vrac-squel.php3");
#include_local("inc-form-squel.php3");

// definition des criteres
include_local("inc-criteres.php3");
#include_local("inc-arg-squel.php3");

// gestion des balises de forums
include_local("inc-forum.php3");

// a traiter (essentiellement, ce sont des definitions standard de spip:
// inc-compilo-standard/spip/redac ?
include_local("inc-reqsql-squel.php3");
include_local("inc-champ-squel.php3");

// outils pour debugguer le compilateur
#include_local("inc-compilo-debug.php3"); # desactive

//
// Calculer un <INCLURE()>
//
function calculer_inclure($fichier, $params, $id_boucle, &$boucles) {
  global $dossier_squelettes;

  $criteres = '';
  if ($params) {
    foreach($params as $param) {
      if (ereg("^([_0-9a-zA-Z]+)[[:space:]]*(=[[:space:]]*([^}]+))?$", $param, $args)) {
        $var = $args[1];
        $val = ereg_replace('^["\'](.*)["\']$', "\\1", trim($args[3]));
        $val = addslashes(addslashes($val));

        // Cas de la langue : passer $spip_lang
        // et non table.lang (car depend de {lang_select})
        if ($var =='lang') {
          if ($val)
            $l[] = "\'lang\' => \'$val\'";
          else
            $l[] = "\'lang\' => \''.\$GLOBALS[spip_lang].'\'";
        }

        // Cas normal {var=val}
        else
        if ($val)
          $l[] = "\'$var\' => \'$val\'";
        else
          $l[] = "\'$var\' => \'' . addslashes(" . index_pile($id_boucle, $var, $boucles) . ") .'\'";
        }
    $criteres = join(", ",$l);
    }
  }
  return "\n'<".
    "?php\n\t\$contexte_inclus = array($criteres);\n\t".
    "\$fichier_inclus = \'$fichier\';\n" .
    (($dossier_squelettes) ?
    ("
      if (@file_exists(\'$dossier_squelettes/$fichier\')){
        include(\'$dossier_squelettes/$fichier\');
      } else {
        include(\'$fichier\');
      } " ) :
    ("\tinclude(\'$fichier\');")) .
    "\n?'." . "'>'";
}

//
// Traite une partie "texte" d'un squelette (c'est-a-dire tout element
// qui ne contient ni balise, ni boucle, ni <INCLURE()> ; le transforme
// en une EXPRESSION php (qui peut etre l'argument d'un Return ou la
// partie droite d'une affectation). Ici sont analyses les elements
// multilingues des squelettes : <:xxx:> et <multi>[fr]coucou</multi>
//
function calculer_texte($texte, $id_boucle, &$boucles, $id_mere) {
  $code = "'".ereg_replace("([\\\\'])", "\\\\1", $texte)."'";

  // bloc multi
  if (eregi('<multi>', $texte)) {
    $ouvre_multi = 'extraire_multi(';
    $ferme_multi = ')';
  } else {
    $ouvre_multi = $ferme_multi = '';
  }

  // Reperer les balises de traduction <:toto:>
  while (eregi("<:(([a-z0-9_]+):)?([a-z0-9_]+)(\|[^>]*)?:>", $code, $match)) {
    //
    // Traiter la balise de traduction multilingue
    //
    $chaine = strtolower($match[3]);
    if (!($module = $match[2]))
      // ordre standard des modules a explorer
      $module = 'local/public/spip';
    $c = applique_filtres(explode('|',
      substr($match[4],1)),
      "_T('$module:$chaine')",
      $id_boucle,
      $boucles,
      $id_mere,
      'php'); // ne pas manger les espaces avec trim()
    $code = str_replace($match[0], "'$ferme_multi.$c.$ouvre_multi'", $code);
  }

  return $ouvre_multi . $code . $ferme_multi;
}

//
// calculer_boucle() produit le corps PHP d'une boucle Spip,
// essentiellement une boucle while (ou une double en cas de hierarchie)
// remplissant une variable $t0 retourne'e en valeur
//
function calculer_boucle($id_boucle, &$boucles) {
  global $table_primary, $table_des_tables;

  $boucle = &$boucles[$id_boucle];
  $type_boucle = $boucle->type_requete;

  list($return,$corps) = $boucle->return;

  // Boucle recursive : simplement appeler la boucle interieure
  if ($type_boucle == 'boucle')
      return ("$corps\n return $return;");

  // La boucle doit-elle selectionner la langue ?
  // 1. par defaut
  $lang_select = (
    $type_boucle == 'articles' OR $type_boucle == 'rubriques'
    OR $type_boucle == 'hierarchie' OR $type_boucle == 'breves'
  );
  // 2. si forcer_lang, le defaut est non
  if ($GLOBALS['forcer_lang']) $lang_select = false;
  // 3. demande explicite
  if ($boucle->lang_select == 'oui') $lang_select = true;
  if ($boucle->lang_select == 'non') $lang_select = false;
  // 4. penser a demander le champ lang
  if ($lang_select)
    $boucle->select[] = (($id_table = $table_des_tables[$type_boucle]) ? $id_table.'.' : '') .'lang';

  // Qui sommes-nous ?
  $primary_key = $table_primary[$type_boucle];

  // Calculer les invalideurs si c'est une boucle non constante
  $constant = ereg("^'[^']*'$",$return);
  if ((!$primary_key) || $constant)
    $invalide = '';
  else {
    $id_table = $table_des_tables[$type_boucle];
    $boucle->select[] = "$id_table.$primary_key";

    $invalide = "\n \$Cache['$primary_key']";
    if ($primary_key != 'id_forum')
      $invalide .= "[\$Pile[\$SP]['$primary_key']] = 1;";
    else
      $invalide .= "[calcul_index_forum(" .
        // Retournera 4 [$SP] mais force la demande du champ a MySQL
        index_pile($id_boucle, 'id_article', $boucles) . ',' .
        index_pile($id_boucle, 'id_breve', $boucles) . ',' .
        index_pile($id_boucle, 'id_rubrique', $boucles) .',' .
        index_pile($id_boucle, 'id_syndic', $boucles) . ")] = 1;";
    $invalide .= ' // invalideurs';
  }

  // Cas {1/3} {1,4} {n-2,1}...
  $flag_parties = ($boucle->partie AND $boucle->total_parties);
  $flag_cpt = $flag_parties || // pas '$compteur' a cause du cas 0
    strpos($corps,'compteur_boucle') ||
    strpos($return,'compteur_boucle');

  //
  // Creer le debut du corps de la boucle :
  //
  if ($flag_cpt)
    $debut = "\n \$compteur_boucle++;";

  if ($flag_parties)
    $debut .= '
    if ($compteur_boucle >= $debut_boucle
    AND $compteur_boucle <= $fin_boucle) {';
  
  if ($lang_select AND !$constant)
    $debut .= '
      if ($x = $Pile[$SP]["lang"]) $spip_lang = $x; // lang_select';

  $debut .= $invalide;

  if ($boucle->doublons)
    $debut .= "\n \$doublons['".$boucle->doublons."'] .= ','. " .
    index_pile($id_boucle, $primary_key, $boucles)
    . "; // doublons";

  //
  // L'ajouter au corps
  //
  $corps = $debut . $corps;

  // Separateur ?
  if ($boucle->separateur) {
    $corps .= "\n \$t1 = $return;
    \$t0 .= ((\$t1 && \$t0) ? '"
    . $boucle->separateur
    . "' : '')
    . \$t1;";
  } else if ($constant && !$debut) {
    $corps .= $return;
  } else {
    $corps .= "\n \$t0 .= $return;";
  }

  // Fin de parties
  if ($flag_parties)
    $corps .= "\n }\n";

  // Gestion de la hierarchie (voir inc-arg-squel)
  if ($boucle->hierarchie)
    $texte .= "\n ".$boucle->hierarchie;

  // hack doublons documents : s'il y a quelque chose dans
  // $GLOBALS['doublons_documents'], c'est que des documents ont
  // ete vus par integre_image() ou autre fournisseur officiel de
  // doublons : on les transfere alors vers la vraie variable
  $texte .= '
  global $spip_lang, $doublons_documents;
  $doublons[\'documents\'].=$doublons_documents; $doublons_documents="";';

  // Recherche : recuperer les hash a partir de la chaine de recherche
  if ($boucle->hash) {
    $texte .= '
  // RECHERCHE
  list($hash_recherche, $hash_recherche_strict) = requete_hash($GLOBALS["recherche"]);';
  }

  // si le corps est une constante, ne plus appeler le serveur
  if (ereg("^'[^']*'$",$corps)) {
    // vide ?
    if ($corps == "''") {
      if (!$boucle->numrows)
        return 'return "";';
      else
        $corps = "";
    } else {
      $boucle->numrows = true;
      $corps = "\n ".'for($x=$Numrows["'.$id_boucle.'"];$x>0;$x--)
      $t0 .= ' . $corps .';';
      }
  } else {
    $corps = '

  // RESULTATS
  while ($objet = @spip_fetch_array($result)) {'
  . "\n\t\t\$Pile[\$SP] = \$objet;"
  . "\n$corps\n }\n";

    // Memoriser la langue avant la boucle pour la restituer apres
    if ($lang_select) {
      $texte .= "\n \$old_lang = \$spip_lang;";
      $corps .= "\n \$spip_lang = \$old_lang;";
    }
  }

  //
  // Requete
  //
  $init = "\n\n // REQUETE\n ";

  // hack critere recherche : ignorer la requete en cas de hash vide
  if ($boucle->hash)
    $init .= "if (\$hash_recherche) ";

  $init .= "\$result = " . calculer_requete($boucle);
  $init .= "\n ".'$t0 = "";
  $SP++;';
  if ($flag_cpt)
    $init .= "\n \$compteur_boucle = 0;";

  if ($flag_parties)
    $init .= calculer_parties($boucle->partie,
      $boucle->mode_partie,
      $boucle->total_parties,
      $id_boucle);
  else if ($boucle->numrows)
    $init .= "\n \$Numrows['$id_boucle'] = @spip_num_rows(\$result);";

  //
  // Conclusion et retour
  //
  $conclusion = "\n @spip_free_result(\$result);";
  $conclusion .= "\n return \$t0;";

  return $texte . $init . $corps . $conclusion;
}

//
// fonction traitant les criteres {1,n} (analyses dans inc-criteres)
//
## a deplacer dans inc-criteres ??
function calculer_parties($partie, $mode_partie, $total_parties, $id_boucle) {

  // Notes :
  // $debut_boucle et $fin_boucle sont les indices SQL du premier
  // et du dernier demandes dans la boucle : 0 pour le premier,
  // n-1 pour le dernier ; donc total_boucle = 1 + debut - fin

  // nombre total avant partition
  $retour = "\n\n // Partition\n "
  .'$nombre_boucle = @spip_num_rows($result);';

  ereg("([+-/])([+-/])?", $mode_partie, $regs);
  list(,$op1,$op2) = $regs;

  // {1/3}
  if ($op1 == '/') {
    $retour .= "\n "
      .'$debut_boucle = 1 + ceil(($nombre_boucle * '
      . ($partie - 1) . ')/' . $total_parties . ");\n "
      . '$fin_boucle = ceil (($nombre_boucle * '
      . $partie . ')/' . $total_parties . ");";
  }

  // {1,x}
  if ($op1 == '+') {
    $retour .= "\n "
      . '$debut_boucle = ' . $partie . ';';
  }
  // {n-1,x}
  if ($op1 == '-') {
    $retour .= "\n "
      . '$debut_boucle = $nombre_boucle - ' . $partie . ';';
  }
  // {x,1}
  if ($op2 == '+') {
    $retour .= "\n "
      . '$fin_boucle = $debut_boucle + ' . $partie . ' - 1;';
  }
  // {x,n-1}
  if ($op2 == '-') {
    $retour .= "\n "
      . '$fin_boucle = $debut_boucle+($nombre_boucle-'.$partie.')-1;';
  }

  // Rabattre $fin_boucle sur le maximum
  $retour .= "\n "
    .'$fin_boucle = min($fin_boucle, $nombre_boucle);';

  // calcul du total boucle final
  $retour .= "\n "
    .'$Numrows[\''.$id_boucle.'\'] = $fin_boucle - $debut_boucle + 1;';

  return $retour;
}

// Production du code PHP a partir de la sequence livree par le phraseur
// $boucles est passe par reference pour affectation par index_pile.
// Retourne un tableau de 2 elements:
// 1. 'code' = une expression PHP,
// 2. 'entete' = une suite d'instructions PHP a executer
// avant d'evaluer l'expression (a rendre obsolete tant que possible).

function calculer_liste($tableau, $prefix, $id_boucle, $niv, &$boucles, $id_mere) {
  if ((!$tableau))
    return array("''",'');
  $t = '$t' . ($niv+1);

  for ($i=0; $i<=$niv; $i++) $tab .= "\t";

  foreach ($tableau as $objet) {

    // c = 'code' ; m = 'entete'
    // rendu[0] = (code, entete) du principal
    // rendu[1] = (code, entete) du "avant"
    // rendu[2] = (code, entete) du "apres"
    // rendu[3] = (code, entete) du "alternatif"
    unset($rendu);
    unset($commentaire);

    switch($objet->type) {
    // texte seul
    case 'texte':
      $rendu[0][0] = calculer_texte($objet->texte, $id_boucle, $boucles, $id_mere);
      break;

    // inclure
    case 'include':
      $rendu[0][0] = calculer_inclure($objet->fichier,
        $objet->params,
        $id_boucle,
        $boucles);
      $commentaire = "<INCLURE($objet->fichier)>";
      break;

    // boucle
    case 'boucle':
      $nom = $objet->id_boucle;
      // avant
      $rendu[1] = calculer_liste($objet->cond_avant, $prefix,
        $id_boucle, $niv+2, $boucles, $nom);
      // apres
      $rendu[2] = calculer_liste($objet->cond_apres, $prefix,
        $id_boucle, $niv+2, $boucles, $nom);
      // alternatif
      $rendu[3] = calculer_liste($objet->cond_altern, $prefix,
        $id_boucle, $niv+1,$boucles, $nom);
      $rendu[0][0] = $prefix . ereg_replace("-","_", $nom)
      . '($Cache, $Pile, $doublons, $Numrows, $SP)';
      $commentaire = "BOUCLE$nom";
      break;

    // balise SPIP
    default:
      $rendu[0][0] = calculer_champ($objet->fonctions,
        $objet->nom_champ,
        $id_boucle,
        $boucles,
        $id_mere,
        $objet->etoile);
      $commentaire = "#$objet->nom_champ".($objet->etoile?'*':'');
      // avant
      $rendu[1] = calculer_liste($objet->cond_avant, $prefix,
        $id_boucle, $niv+2,$boucles, $id_mere);
      // apres
      $rendu[2] = calculer_liste($objet->cond_apres, $prefix,
        $id_boucle, $niv+2,$boucles, $id_mere);
      break;

    } // switch

    // Assembler les elements en simplifiant si possible
    // le resultat (lisibilite et rapidite)
    $utiliser_f = false;
    for ($i = 0; $i<=3; $i++) {
      if ($rendu[$i][0] == '' OR $rendu[$i][0] == "''") {
        $rendu[$i][0] = "''";
      } else {
        // Ajouter l'entete eventuel
        if ($rendu[$i][1])
          $rendu[$i][0] =
          "eval('".texte_script($rendu[$i][1])."')."
          ."/"."* entete *"."/"
          ."\n$tab".$rendu[$i][0];
        // Noter le recours eventuel ˆ _f
        if ($i>0)
          $utiliser_f = true;
      }
    }
    if ($commentaire)
      $rendu[0][0] = "/"."* $commentaire *"."/ ".$rendu[0][0];

    //
    // (_f(1,principal) ? avant._f().apres : _f().alternatif)
    // _f() fonctionne avec une pile ; cette structure logique permet
    // de n'evaluer que ce qui doit l'etre (ne pas evaluer avant/apres
    // si on veut utiliser sinon, et vice-versa)
    if ($utiliser_f)
      $code = "(_f(1,".$rendu[0][0].") ? "
      . (($rendu[1][0]=="''") ? "" :
        "\n$tab\t/"."* << *"."/".$rendu[1][0]." .")
      . "_f()"
      . (($rendu[2][0]=="''") ? "" :
        ". ".$rendu[2][0]."/"."* >> *"."/")
      . " : _f()"
      . (($rendu[3][0]=="''") ? "" :
        " /"."* sinon: *"."/.".$rendu[3][0])
      .")";
    else
    // eviter les conditionnelles qui forkent le resultat
    // si le code est '$a ? $b : $c', le parenthesage est obligatoire
    // quand on est lie a d'autres chaines par des . tout nus
    // NB/astuce: s'il y a un entete, la formule eval('...').$a ? $b : $c
    // fonctionne a l'identique de $a ? $b : $c
    if (strpos($rendu[0][0], '?'))
      $code = "(".$rendu[0][0].")";
    else
      $code = $rendu[0][0];

    $codes[] = $code;

  } // foreach

  return array(join ("\n$tab. ", $codes), '');
}

// Prend en argument le source d'un squelette, sa grammaire et un nom.
// Retourne une fonction PHP/SQL portant ce nom et calculant une page HTML.
// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
// - 1er: element 'cache' => nom (du fichier ou` mettre la page)
// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
// Elle retourne alors un tableau de 4 e'le'ments:
// - 'texte' => page HTML, application du squelette a` l'environnement;
// - 'squelette' => le nom du squelette
// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
// - 'invalideurs' => de'pendances de cette page, pour invalider son cache.
// (voir son utilisation, optionnelle, dans invalideur.php)
// En cas d'erreur, elle retourne un tableau des 2 premiers elements seulement

function calculer_squelette($squelette, $nom, $gram, $sourcefile) {

  // Phraser le squelette, selon sa grammaire
  // pour le moment: "html" seul connu (HTML+balises BOUCLE)
  $boucles = '';
  spip_timer('calcul_skel');
  include_local("inc-$gram-squel.php3");
  $racine = parser($squelette, '',$boucles);
  // include_local('inc-debug.php3');
  // afftable($racine);
  // affboucles($boucles);

  // Commencer par reperer les boucles appelees explicitement par d'autres
  // car elles indexent leurs arguments de maniere derogatoire

  if ($boucles) foreach($boucles as $id => $boucle) {
    if ($boucle->type_requete == 'boucle') {
      $rec = &$boucles[$boucle->param];
      if (!$rec) {
        include_local('inc-admin.php3');
        erreur_squelette(_T('info_erreur_squelette'),
        _L($boucle->param.'&nbsp: boucle recursive non definie'), $id);
      } else {
        $rec->externe = $id;
        $boucles[$id]->return =
          calculer_liste(array($rec),
            $nom,
            $boucle->param,
            1,
            $boucles,
            $id);
      }
    }
  }

  if ($boucles) foreach($boucles as $id => $boucle) {
    if ($boucle->type_requete != 'boucle') {
      calculer_criteres($id, $boucles);
      $boucles[$id]->return = calculer_liste($boucle->milieu,
        $nom,
        $id,
        1,
        $boucles,
        $id);
    }
  }

  // idem pour la racine
  list ($return,$corps) = calculer_liste($racine, $nom, '',0, $boucles, '');

  // Corps de toutes les fonctions PHP,
  // en particulier les requetes SQL et TOTAL_BOUCLE
  // de'terminables seulement maintenant
  // Les 3 premiers parame`tres sont passe's par re'fe'rence
  // (sorte d'environnements a` la Lisp 1.5)
  // sauf pour la fonction principale qui recoit les initialisations

  $code = '';
  if ($boucles) {
    foreach($boucles as $id => $boucle)
      $boucles[$id]->return = calculer_boucle($id, $boucles);

    foreach($boucles as $id => $boucle) {

      // Reproduire la boucle en commentaire
      $pretty = "BOUCLE$id(".strtoupper($boucle->type_requete).")";
      if (is_array($boucle->param))
        $pretty .= " {".join("} {", $boucle->param)."}";
      $pretty = ereg_replace("[\r\n]", " ", $pretty);

      // Puis envoyer son code
      $code .= "\n//\n// <$pretty>\n//\n"
      ."function $nom" . ereg_replace("-","_",$id) .
      '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
      $boucle->return .
      "\n}\n\n";
    }
  }

  $secondes = spip_timer('calcul_skel');
  spip_log("calcul skel $sourcefile ($secondes)");

  if (is_array($boucles))
    $aff_boucles = join (', ', array_keys($boucles));
  else
    $aff_boucles = "pas de boucle";

  return "
/*
* Squelette : $sourcefile
* Date : ".http_gmoddate(@filemtime($sourcefile))." GMT
* Compile : ".http_gmoddate(time())." GMT ($secondes)
* Boucles : ".$aff_boucles."
*/
$code

//
// Fonction principale du squelette $sourcefile
//
function $nom (\$Cache, \$Pile, \$ignore_les_doublons_inc_calcul_php3, \$Numrows='', \$SP=0) {
$corps
\$t0 = $return;

  return array(
    'texte' => \$t0,
    'squelette' => '$nom',
    'process_ins' => ((strpos(\$t0,'<'.'?')=== false) ? 'html' : 'php'),
    'invalideurs' => \$Cache
  );
}
";

}

?>

--- NEW FILE: inc-balises.php3 ---
<?php

//
// Ce fichier regroupe la quasi totalite des definitions de #BALISES de spip
// Pour chaque balise, il est possible de surcharger, dans mes_fonctions.php3,
// la fonction balise_TOTO_dist par une fonction balise_TOTO() respectant la
// meme API : recoit en entree plein de donnees, les modifie et les retourne.
// Le format du bloc de donnees $p est un objet de la class ParamChamp,
// definie dans inc-compilo-index.php3
//

## NB: les fonctions de forum sont definies dans inc-forum.php3

// Ce fichier ne sera execute qu'une fois
if (defined("_INC_BALISES")) return;
define("_INC_BALISES", "1");

function balise_NOM_SITE_SPIP_dist($p) {
  $p->code = "lire_meta('nom_site')";
  $p->type = 'php';
  return $p;
}

function balise_EMAIL_WEBMASTER_dist($p) {
  $p->code = "lire_meta('email_webmaster')";
  $p->type = 'php';
  return $p;
}

function balise_CHARSET_dist($p) {
  $p->code = "lire_meta('charset')";
  $p->type = 'php';
  return $p;
}

function balise_LANG_LEFT_dist($p) {
  $p->code = "lang_dir(\$spip_lang,'left','right')";
  $p->type = 'php';
  return $p;
}

function balise_LANG_RIGHT_dist($p) {
  $p->code = "lang_dir(\$spip_lang,'right','left')";
  $p->type = 'php';
  return $p;
}

function balise_LANG_DIR_dist($p) {
  $p->code = "lang_dir(\$spip_lang,'ltr','rtl')";
  $p->type = 'php';
  return $p;
}

function balise_PUCE_dist($p) {
  $p->code = "propre('- ')";
  $p->type = 'php';
  return $p;
}

// #DATE
// Cette fonction sait aller chercher dans le contexte general
// quand #DATE est en dehors des boucles
// http://www.spip.net/fr_article1971.html
function balise_DATE_dist ($p) {
  $_date = champ_sql('date', $p);
  $p->code = "$_date";
  $p->process = 'vider_date(%s)';
  $p->type = 'php';
  return $p;
}

// #DATE_REDAC
// http://www.spip.net/fr_article1971.html
function balise_DATE_REDAC_dist ($p) {
  $_date = champ_sql('date_redac', $p);
  $p->code = "$_date";
  $p->process = 'vider_date(%s)';
  $p->type = 'php';
  return $p;
}

// #DATE_MODIF
// http://www.spip.net/fr_article1971.html
function balise_DATE_MODIF_dist ($p) {
  $_date = champ_sql('date_modif', $p);
  $p->code = "$_date";
  $p->process = 'vider_date(%s)';
  $p->type = 'php';
  return $p;
}

// #DATE_NOUVEAUTES
// http://www.spip.net/fr_article1971.html
function balise_DATE_NOUVEAUTES_dist($p) {
  $p->code = "((lire_meta('quoi_de_neuf') == 'oui' AND lire_meta('majnouv')) ? normaliser_date(lire_meta('majnouv')) : \"'0000-00-00'\")";
  $p->process = 'vider_date(%s)';
  $p->type = 'php';
  return $p;
}

function balise_URL_SITE_SPIP_dist($p) {
  $p->code = "lire_meta('adresse_site')";
  $p->type = 'php';
  return $p;
}

function balise_URL_ARTICLE_dist($p) {
  $_type = $p->type_requete;

  // Cas particulier des boucles (SYNDIC_ARTICLES)
  if ($_type == 'syndic_articles') {
    $p->code = champ_sql('url', $p);
  }

  // Cas general : chercher un id_article dans la pile
  else {
    $_id_article = champ_sql('id_article', $p);
    $p->code = "generer_url_article($_id_article)";

    if ($p->boucles[$p->id_boucle]->hash)
      $p->code = "url_var_recherche(" . $p->code . ")";
  }

  $p->type = 'html';
  return $p;
}

function balise_URL_RUBRIQUE_dist($p) {
  $p->code = "generer_url_rubrique(" .
  champ_sql('id_rubrique',$p) .
  ")" ;
  if ($p->boucles[$p->id_boucle]->hash)
  $p->code = "url_var_recherche(" . $p->code . ")";

  $p->type = 'html';
  return $p;
}

function balise_URL_BREVE_dist($p) {
  $p->code = "generer_url_breve(" .
  champ_sql('id_breve',$p) .
  ")";
  if ($p->boucles[$p->id_boucle]->hash)
  $p->code = "url_var_recherche(" . $p->code . ")";

  $p->type = 'html';
  return $p;
}

function balise_URL_MOT_dist($p) {
  $p->code = "generer_url_mot(" .
  champ_sql('id_mot',$p) .
  ")";
  $p->code = "url_var_recherche(" . $p->code . ")";

  $p->type = 'html';
  return $p;
}

function balise_URL_FORUM_dist($p) {
  $p->code = "generer_url_forum(" .
  champ_sql('id_forum',$p) .")";

  $p->type = 'html';
  return $p;
}

function balise_URL_DOCUMENT_dist($p) {
  $p->code = "generer_url_document(" .
  champ_sql('id_document',$p) . ")";

  $p->type = 'html';
  return $p;
}

function balise_URL_AUTEUR_dist($p) {
  $p->code = "generer_url_auteur(" .
  champ_sql('id_auteur',$p) .")";
  if ($p->boucles[$p->id_boucle]->hash)
  $p->code = "url_var_recherche(" . $p->code . ")";

  $p->type = 'html';
  return $p;
}

function balise_NOTES_dist($p) {
  // Recuperer les notes
  $p->code = '$GLOBALS["les_notes"]';
  // Vider ensuite les globales des notes recuperees
  // avec une formule qui renvoit toujours ""
  $p->code .= '. ($GLOBALS["les_notes"] = $GLOBALS["compt_note"] = '
  . '($GLOBALS["marqueur_notes"]++)?"":"")';
  $p->type = 'html';
  return $p;
}

function balise_RECHERCHE_dist($p) {
  $p->code = 'htmlspecialchars($GLOBALS["recherche"])';
  $p->type = 'php';
  return $p;
}

function balise_COMPTEUR_BOUCLE_dist($p) {
  $p->code = '$compteur_boucle';
  $p->type = 'php';
  return $p;
}

function balise_TOTAL_BOUCLE_dist($p) {
  if ($p->id_mere === '') {
    include_local("inc-admin.php3");
    erreur_squelette(_L("Champ #TOTAL_BOUCLE hors boucle"), '', $p->id_boucle);
  }
  $p->code = "\$Numrows['$p->id_mere']";
  $p->boucles[$p->id_mere]->numrows = true;
  $p->type = 'php';
  return $p;
}

function balise_POINTS_dist($p) {
  $n = 0;
  $b = $p->id_boucle;
  $p->code = '';
  while ($b != '') {
  if ($s = $p->boucles[$b]->param) {
    foreach($s as $v) {
    if (strpos($v,'recherche') !== false) {
      $p->code = '$Pile[$SP' . (($n==0) ? "" : "-$n") .
      '][points]';
      $b = '';
      break;
    }
    }
  }
  $n++;
  $b = $p->boucles[$b]->id_parent;
  }
  if (!$p->code) {
    include_local("inc-admin.php3");
    erreur_squelette(_L("Champ #POINTS hors d'une recherche"), '', $p->id_boucle);
  }
  $p->type = 'php';
  return $p;
}

function balise_POPULARITE_ABSOLUE_dist($p) {
  $p->code = 'ceil(' .
  champ_sql('popularite', $p) .
  ')';
  $p->type = 'php';
  return $p;
}

function balise_POPULARITE_SITE_dist($p) {
  $p->code = 'ceil(lire_meta(\'popularite_total\'))';
  $p->type = 'php';
  return $p;
}

function balise_POPULARITE_MAX_dist($p) {
  $p->code = 'ceil(lire_meta(\'popularite_max\'))';
  $p->type = 'php';
  return $p;
}

function balise_EXPOSER_dist($p) {
  global $table_primary;
  $on = 'on';
  $off= '';
  if ($p->fonctions) {
  // Gerer la notation [(#EXPOSER|on,off)]
  reset($p->fonctions);
  list(, $onoff) = each($p->fonctions);
  ereg("([^,]*)(,(.*))?", $onoff, $regs);
  $on = addslashes($regs[1]);
  $off = addslashes($regs[3]);
  
  // autres filtres
  $filtres=Array();
  while (list(, $nom) = each($p->fonctions))
    $filtres[] = $nom;
  $p->fonctions = $filtres;
  }

  $type_boucle = $p->type_requete;
  $primary_key = $table_primary[$type_boucle];

  $p->code = '(calcul_exposer('
  .champ_sql($primary_key, $p)
  .', "'.$primary_key.'", $Pile[0]) ?'." '$on': '$off')";
  $p->type = 'php';
  return $p;
}

//
// Inserer directement un document dans le squelette
//
function balise_EMBED_DOCUMENT_dist($p) {
  $_id_document = champ_sql('id_document',$p);
  $p->code = "embed_document($_id_document, '" .
  texte_script($p->fonctions ? join($p->fonctions, "|") : "") .
  "', false)";
  unset ($p->fonctions);
  $p->type = 'html';
  return $p;
}

// Debut et fin de surlignage auto des mots de la recherche
// on insere une balise Span avec une classe sans spec:
// c'est transparent s'il n'y a pas de recherche,
// sinon elles seront remplacees par les fontions de inc_surligne
// flag_pcre est juste une flag signalant que preg_match est dispo.

function balise_DEBUT_SURLIGNE_dist($p) {
  global $flag_pcre;
  $p->code = ($flag_pcre ? ('\'<span class="spip_surligneconditionnel">\'') : "''");
  return $p;
}
function balise_FIN_SURLIGNE_dist($p) {
  global $flag_pcre;
  $p->code = ($flag_pcre ? ('\'</span class="spip_surligneconditionnel">\'') : "''");
  return $p;
}

// Formulaire de changement de langue
function balise_MENU_LANG_dist($p) {
  $p->code = '"<"."?php
include_ecrire(\"inc_lang.php3\");
echo menu_langues(\"var_lang\", \$menu_lang);
?".">"';
  $p->type = 'php';
  return $p;
}

// Formulaire de changement de langue / page de login
function balise_MENU_LANG_ECRIRE_dist($p) {
  $p->code = '"<"."?php
include_ecrire(\"inc_lang.php3\");
echo menu_langues(\"var_lang_ecrire\", \$menu_lang);
?".">"';
  $p->type = 'php';
  return $p;
}

//
// Formulaires de login
//
function balise_LOGIN_PRIVE_dist($p) {
  $p->code = '"<"."?php include(\'inc-login.php3\'); login(\'\', \'prive\'); ?".">"';
  $p->type = 'php';
  return $p;
}

function balise_LOGIN_PUBLIC_dist($p) {
  if ($nom = $p->fonctions[0])
  $lacible = "new Link('".$nom."')";
  else
  $lacible = '\$GLOBALS[\'clean_link\']';
  $p->code = '"<"."?php include(\'inc-login.php3\'); login(' . $lacible . ', false); ?".">"';
  $p->fonctions = array();
  $p->type = 'php';
  return $p;
}

function balise_URL_LOGOUT_dist($p) {
  if ($p->fonctions) {
  $url = "&url=".$p->fonctions[0];
  $p->fonctions = array();
  } else {
  $url = '&url=\'.urlencode(\$clean_link->getUrl()).\'';
  }
  $p->code = '"<"."?php if (\$GLOBALS[\'auteur_session\'][\'login\'])
{ echo \'spip_cookie.php3?logout_public=\'.\$GLOBALS[\'auteur_session\'][\'login\'].\'' . $url . '\'; } ?".">"';
  $p->type = 'php';
  return $p;
}

function balise_INTRODUCTION_dist ($p) {
  $_type = $p->type_requete;
  $_texte = champ_sql('texte', $p);
  $_chapo = champ_sql('chapo', $p);
  $_descriptif = champ_sql('descriptif', $p);
  $p->code = "calcul_introduction('$_type', $_texte, $_chapo, $_descriptif)";

  $p->type = 'html';
  return $p;
}

// #LANG
// non documente ?
function balise_LANG_dist ($p) {
  $_lang = champ_sql('lang', $p);
  $p->code = "($_lang ? $_lang : \$GLOBALS['spip_lang'])";
  $p->type = 'php';
  return $p;
}

// #LESAUTEURS
// les auteurs d'un article (ou d'un article syndique)
// http://www.spip.net/fr_article902.html
// http://www.spip.net/fr_article911.html
function balise_LESAUTEURS_dist ($p) {
  // Cherche le champ 'lesauteurs' dans la pile
  $_lesauteurs = champ_sql('lesauteurs', $p);

  // Si le champ n'existe pas (cas de spip_articles), on donne la
  // construction speciale sql_auteurs(id_article) ;
  // dans le cas contraire on prend le champ 'les_auteurs' (cas de
  // spip_syndic_articles)
  if ($_lesauteurs AND $_lesauteurs != '$Pile[0][lesauteurs]') {
    $p->code = $_lesauteurs;
  } else {
    $_id_article = champ_sql('id_article', $p);

    # On pourrait mieux faire qu'utiliser cette fonction assistante ?
    $p->code = "sql_auteurs($_id_article)";
  }

  $p->type = 'html';
  return $p;
}

// #PETITION
// Champ testant la presence d'une petition
// non documente ???
function balise_PETITION_dist ($p) {
  $_id_article = champ_sql('id_article', $p);
  $p->code = 'sql_petitions($_id_article)';
  $p->type = 'php';
  return $p;
}

// #POPULARITE
// http://www.spip.net/fr_article1846.html
function balise_POPULARITE_dist ($p) {
  $_popularite = champ_sql('popularite', $p);
  $p->code = "ceil(min(100, 100 * $_popularite
  / max(1 , 0 + lire_meta('popularite_max'))))";
  $p->type = 'php';
  return $p;
}

//
// Fonction commune aux balises #LOGO_XXXX
// (les balises portant ce type de nom sont traitees en bloc ici)
//
function calcul_balise_logo ($p) {

  // analyser la balise LOGO_xxx
  eregi("^LOGO_(([A-Z]+)(_.*)?)", $p->nom_champ, $regs);
  $type_logo = $regs[1]; // ARTICLE_RUBRIQUE
  $type_objet = $regs[2]; // ARTICLE
  $suite_logo = $regs[3]; // _RUBRIQUE
  $_id_objet = champ_sql("id_$type_objet", $p);

  // analyser les filtres
  $flag_fichier = 'false';
  $filtres = '';
  if (is_array($p->fonctions)) {
    foreach($p->fonctions as $nom) {
      if (ereg('^(left|right|center|top|bottom)$', $nom))
        $align = $nom;
      else if ($nom == 'lien') {
        $flag_lien_auto = 'oui';
        $flag_stop = true;
      }
      else if ($nom == 'fichier') {
        $flag_fichier = 'true';
        $flag_stop = true;
      }
      // double || signifie "on passe aux filtres"
      else if ($nom == '')
        $flag_stop = true;
      else if (!$flag_stop) {
        $lien = $nom;
        $flag_stop = true;
      }
      // apres un URL ou || ou |fichier ce sont
      // des filtres (sauf left...lien...fichier)
      else
        $filtres[] = $nom;
    }
    // recuperer les autres filtres s'il y en a
    $p->fonctions = $filtres;
  }

  //
  // Preparer le code du lien
  //
  // 1. filtre |lien
  if ($flag_lien_auto AND !$lien)
    $code_lien = '($lien = generer_url_'.$type_objet.'('.$_id_objet.')) ? $lien : ""';
  // 2. lien indique en clair (avec des balises : imprimer#ID_ARTICLE.html)
  else if ($lien) {
    $code_lien = "'".texte_script(trim($lien))."'";
    while (ereg("^([^#]*)#([A-Za-z_]+)(.*)$", $code_lien, $match)) {
      $c = calculer_champ(array(), $match[2], $p->id_boucle, $p->boucles, $p->id_mere);
      $code_lien = str_replace('#'.$match[2], "'.".$c.".'", $code_lien);
    }
    // supprimer les '' disgracieux
    $code_lien = ereg_replace("^''\.|\.''$", "", $code_lien);
  }
  if (!$code_lien)
    $code_lien = "''";

  switch ($suite_logo) {
    case '_NORMAL':
      $onoff = 'true, false';
      break;
    case '_SURVOL':
      $onoff = 'false, true';
      break;
    case '':
    default:
      $onoff = 'true, true';
      break;
  }

  // cas des documents
  if ($type_objet == 'DOCUMENT')
    $code_logo =
      "array(integre_image($_id_objet,'','fichier_vignette'), '')";
  else
    $code_logo = "cherche_logo_objet('$type_objet',
      $_id_objet, $onoff)";

  // cas des logo #BREVE_RUBRIQUE et #ARTICLE_RUBRIQUE
  if ($suite_logo == '_RUBRIQUE') {
    $_id_rubrique = champ_sql("id_rubrique", $p);
    $code_logo = "(\$logo = $code_logo) ? \$logo : ".
    "cherche_logo_objet('RUBRIQUE', $_id_rubrique, $onoff)";
  }

  $p->code = "affiche_logos($code_logo, $code_lien, '$align', $flag_fichier)";

  $p->type = 'php';
  return $p;
}

// #EXTRA [(#EXTRA|isbn)]
// Champs extra
// Non documentes, en voie d'obsolescence, cf. ecrire/inc_extra.php3
function balise_EXTRA_dist ($p) {
  $_extra = champ_sql('extra', $p);
  $p->code = $_extra;

  // Gerer la notation [(#EXTRA|isbn)]
  if ($p->fonctions) {
    include_ecrire("inc_extra.php3");
    list ($key, $champ_extra) = each($p->fonctions); // le premier filtre
    $type_extra = $p->type_requete;
      // ci-dessus est sans doute un peu buggue : si on invoque #EXTRA
      // depuis un sous-objet sans champ extra d'un objet a champ extra,
      // on aura le type_extra du sous-objet (!)
    if (extra_champ_valide($type_extra, $champ_extra)) {
      unset($p->fonctions[$key]);
      $p->code = "extra($p->code, '".addslashes($champ_extra)."')";

      // Appliquer les filtres definis par le webmestre
      $filtres = extra_filtres($type_extra, $champ_extra);
      if ($filtres) foreach ($filtres as $f)
        $p->code = "$f($p->code)";
    }
  }

  $p->type = 'html';
  return $p;
}

//
// Traduction des champs "formulaire"
//

//
// Note : les balises de gestion de forums (FORMULAIRE_FORUM et
// PARAMETRES_FORUM) sont definies dans le fichier inc-forum.php3
// qui centralise toute la gestion des forums
//

//
// Formulaire de recherche
//
function balise_FORMULAIRE_RECHERCHE_dist($p) {
  if ($p->fonctions) {
    list(, $lien) = each($p->fonctions); // le premier est un url
    while (list(, $filtre) = each($p->fonctions))
      $filtres[] = $filtre; // les suivants sont des filtres
    $p->fonctions = $filtres;
  }
  if (!$lien) $lien = 'recherche.php3';

  $formulaire_recherche = "\"<form action='$lien' method='get' class='formrecherche'><input type='text' id='formulaire_recherche' size='20' class='formrecherche' name='recherche' value='\" . _T('info_rechercher') . \"' /></form>\"";

  $p->code = "((lire_meta('activer_moteur') != 'oui') ? '' :
  $formulaire_recherche)";

  $p->type = 'html';
  return $p;
}

//
// Formulaire d'inscription comme redacteur (dans inc-formulaires.php3)
//
function balise_FORMULAIRE_INSCRIPTION_dist($p) {

  $p->code = '(lire_meta("accepter_inscriptions") != "oui") ? "" :
    ("<"."?php include(\'inc-formulaires.php3\'); lang_select(\"$spip_lang\"); formulaire_inscription(\"redac\"); lang_dselect(); ?".">")';

  $p->type = 'php';
  return $p;
}

//
// Formulaire ecrire auteur
//
function balise_FORMULAIRE_ECRIRE_AUTEUR_dist($p) {
  $_id_auteur = champ_sql('id_auteur', $p);
  $_mail_auteur = champ_sql('email', $p);

  $p->code = '!email_valide('.$_mail_auteur.') ? "" :
    ("<'.'?php include(\'inc-formulaires.php3\');
    lang_select(\'$spip_lang\');
    formulaire_ecrire_auteur(".'.$_id_auteur.'.", \'".texte_script('.$_mail_auteur.')."\');
    lang_dselect(); ?'.'>")';

  $p->type = 'php';
  return $p;
}

//
// Formulaire signature de petition
//
function balise_FORMULAIRE_SIGNATURE_dist($p) {
  $_id_article = champ_sql('id_article', $p);

  $p->code = '!($petition = sql_petitions('.$_id_article.')) ? "" :
    ("<"."?php include(\'inc-formulaires.php3\');
    lang_select(\'$spip_lang\');
    echo formulaire_signature(".'.$_id_article.'.",
      \'".texte_script(serialize($petition))."\');
    lang_dselect(); ?".">")';

  $p->type = 'php';
  return $p;
}

// Formulaire d'inscription de site dans l'annuaire
function balise_FORMULAIRE_SITE_dist($p) {
  $_id_rubrique = champ_sql('id_rubrique', $p);

  $p->code = '(lire_meta("proposer_sites") != 2) ? "":
    "<"."?php include(\'inc-formulaires.php3\');
    lang_select(\'".$GLOBALS[\'spip_lang\']."\');
    formulaire_site(".'.$_id_rubrique.'.");
    lang_dselect(); ?".">"';

  $p->type = 'php';
  return $p;
}

//
// Boutons d'administration:
//
function balise_FORMULAIRE_ADMIN_dist($p) {
  $p->code = "'<!-- @@formulaire_admin@@45609871@@ -->'";
  $p->type = "php";
  return $p;
}

?>

--- inc-form-squel.php3 DELETED ---

--- NEW FILE: inc-compilo-debug.php3 ---
<?php

//
// Outils pour debugguer le compilateur (pas inclus)
//

//
// Ce fichier ne sera execute qu'une fois
if (defined("_INC_COMPILO_DEBUG")) return;
define("_INC_COMPILO_DEBUG", "1");

//
// Fonctions debug
//

function affval($val) {

  echo "&ldquo;" . entites_html($val) . "&rdquo;";

}

function afftable($table) {

  if (!$table) return;
  reset($table);
  echo "<UL>";
  while (list($key, $val) = each($table)) {
    echo "<LI>";
    affobject($val);
    echo "</LI>";
  }
  echo "</UL>\n";
}

function affobject($val)
{
  if (!is_object($val))
    affval($val);
  else
    switch ($val->type) {
    case 'boucle':
      echo "<font color='red'><b>Boucle".$val->id_boucle."</b>";
      echo "<br><i><small>".affval($val->requete)."</small></i></font>";
      break;
    case 'texte':
      echo affval($val->texte);
      break;
    case 'include':
      echo affval($val->fichier);
      afftable($params);
      break;
        case 'champ':
      echo "<font color='blue'><i>#".$val->nom_champ;
      if ($val->fonctions) echo " <small>(".join(',', $val->fonctions).")</small>";
      echo "</i></font>";
      echo "<ul><li>";
      echo afftable($val->cond_avant);
      echo "</li><li>";
      echo afftable($val->cond_apres);
      echo "</li></ul>";
      break;
    }
}

function affboucle($val) {
  echo "<hr><ul>";
  foreach(get_object_vars($val) as $k => $v)
    {
      echo "<li><b>$k : </b>";
      if (is_array($v))
        if (!$v) echo "<i>Tableau vide</i>"; else afftable($v);
      elseif (is_object($v))
        echo afftable($v);
      else affval($v);
      echo "</li>"; }
  echo "</ul>\n";
}

function affboucles($boucles) {
  while (list($key, $val) = each($boucles)) affboucle($val);
}

?>