X-Git-Url: http://git.cyclocoop.org/?p=velocampus%2Fweb%2Fwww.git;a=blobdiff_plain;f=www%2Fecrire%2Fpublic%2Fcomposer.php;fp=www%2Fecrire%2Fpublic%2Fcomposer.php;h=c2690eba8967ef2bfce37ebd6731e5ee7d1111a0;hp=0000000000000000000000000000000000000000;hb=80b4d3e85f78d402ed2e73f8f5d1bf4c19962eed;hpb=aaf970bf4cdaf76689ecc10609048e18d073820c diff --git a/www/ecrire/public/composer.php b/www/ecrire/public/composer.php new file mode 100644 index 0000000..c2690eb --- /dev/null +++ b/www/ecrire/public/composer.php @@ -0,0 +1,725 @@ + 'oui', 'phpcheck' => 'oui'))) + eval('?'.'>'.$skel_code); +# spip_log($skel_code, 'comp') + if (@file_exists($lib = $squelette . '_fonctions'.'.php')) + include_once $lib; + + // tester si le eval ci-dessus a mis le squelette en memoire + + if (function_exists($nom)) return array($nom, $skel_code); + + // charger le source, si possible, et compiler + if (lire_fichier ($source, $skel)) { + $compiler = charger_fonction('compiler', 'public'); + $skel_code = $compiler($skel, $nom, $gram, $source, $connect); + } + + // Ne plus rien faire si le compilateur n'a pas pu operer. + if (!$skel_code) return false; + + foreach($skel_code as $id => $boucle) { + $f = $boucle->return; + if (@eval("return true; $f ;") === false) { + // Code syntaxiquement faux (critere etc mal programme') + $msg = array('zbug_erreur_compilation'); + erreur_squelette($msg, $boucle); + // continuer pour trouver d'autres fautes eventuelles + // mais prevenir que c'est mort + $nom = ''; + } + // Contexte de compil inutile a present + // (mais la derniere valeur de $boucle est utilisee ci-dessous) + $skel_code[$id] = $f; + } + + if ($nom) { + // Si le code est bon, concatener et mettre en cache + if (function_exists($nom)) + $code = squelette_traduit($skel, $source, $phpfile, $skel_code); + else { + // code semantiquement faux: bug du compilateur + // $boucle est en fait ici la fct principale du squelette + $msg = array('zbug_erreur_compilation'); + erreur_squelette($msg, $boucle); + $nom = ''; + } + } + + if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode'] == 'debug') { + + // Tracer ce qui vient d'etre compile + $GLOBALS['debug_objets']['code'][$nom . 'tout'] = $code; + + // si c'est ce que demande le debusqueur, lui passer la main + if ($GLOBALS['debug_objets']['sourcefile'] + AND (_request('var_mode_objet') == $nom) + AND (_request('var_mode_affiche') == 'code') ) + erreur_squelette(); + } + return $nom ? array($nom, $code) : false; +} + +function squelette_traduit($squelette, $sourcefile, $phpfile, $boucles) +{ + + // Le dernier index est '' (fonction principale) + $noms = substr(join (', ', array_keys($boucles)), 0, -2); + if (CODE_COMMENTE) + $code = " +/* + * Squelette : $sourcefile + * Date : ".gmdate("D, d M Y H:i:s", @filemtime($sourcefile))." GMT + * Compile : ".gmdate("D, d M Y H:i:s", time())." GMT + * " . (!$boucles ? "Pas de boucle" : ("Boucles : " . $noms)) ." + */ " ; + + $code = '<'. "?php\n" . $code . join('', $boucles) . "\n?" .'>'; + if (!isset($GLOBALS['var_nocache']) OR !$GLOBALS['var_nocache']) + ecrire_fichier($phpfile, $code); + return $code; +} + +// Le squelette compile est-il trop vieux ? +// http://doc.spip.org/@squelette_obsolete +function squelette_obsolete($skel, $squelette) { + static $date_change = null; + // ne verifier la date de mes_fonctions et mes_options qu'une seule fois + // par hit + if (is_null($date_change)){ + if (@file_exists($fonc = 'mes_fonctions.php') + OR @file_exists($fonc = 'mes_fonctions.php3')) + $date_change = @filemtime($fonc); # compatibilite + if (defined('_FILE_OPTIONS')) + $date_change = max($date_change,@filemtime(_FILE_OPTIONS)); + } + return ( + (isset($GLOBALS['var_mode']) AND in_array($GLOBALS['var_mode'], array('recalcul','preview','debug'))) + OR !@file_exists($skel) + OR ((@file_exists($squelette)?@filemtime($squelette):0) + > ($date = @filemtime($skel))) + OR ($date_change > $date) + ); +} + +// Activer l'invalideur de session +// http://doc.spip.org/@invalideur_session +function invalideur_session(&$Cache, $code=NULL) { + $Cache['session']=spip_session(); + return $code; +} + + +// +// Des fonctions diverses utilisees lors du calcul d'une page ; ces fonctions +// bien pratiques n'ont guere de logique organisationnelle ; elles sont +// appelees par certaines balises au moment du calcul des pages. (Peut-on +// trouver un modele de donnees qui les associe physiquement au fichier +// definissant leur balise ??? +// + +// http://doc.spip.org/@echapper_php_callback +function echapper_php_callback($r) { + static $src = array(); + static $dst = array(); + + // si on recoit un tableau, on est en mode echappement + // on enregistre le code a echapper dans dst, et le code echappe dans src + if (is_array($r)) { + $dst[] = $r[0]; + return $src[] = '___'.md5($r[0]).'___'; + } + + // si on recoit une chaine, on est en mode remplacement + $r = str_replace($src, $dst, $r); + $src = $dst = array(); // raz de la memoire + return $r; +} + +// http://doc.spip.org/@analyse_resultat_skel +function analyse_resultat_skel($nom, $cache, $corps, $source='') { + $headers = array(); + + // Recupere les < ?php header('Xx: y'); ? > pour $page['headers'] + // note: on essaie d'attrapper aussi certains de ces entetes codes + // "a la main" dans les squelettes, mais evidemment sans exhaustivite + if (preg_match_all( + '/(<[?]php\s+)@?header\s*\(\s*.([^:\']*):?\s*([^)]*)[^)]\s*\)\s*[;]?\s*[?]>/ims', + $corps, $regs, PREG_SET_ORDER)){ + foreach ($regs as $r) { + $corps = str_replace($r[0], '', $corps); + # $j = Content-Type, et pas content-TYPE. + $j = join('-', array_map('ucwords', explode('-', strtolower($r[2])))); + + if ($j=='X-Spip-Filtre' AND isset($headers[$j])) + $headers[$j].="|".$r[3]; + else + $headers[$j] = $r[3]; + } + } + // S'agit-il d'un resultat constant ou contenant du code php + $process_ins = ( + strpos($corps,'<'.'?') === false + OR strpos(str_replace('<'.'?xml', '', $corps),'<'.'?') === false + ) + ? 'html' + : 'php'; + + // traiter #FILTRE{} ? + if (isset($headers['X-Spip-Filtre']) + AND strlen($headers['X-Spip-Filtre'])) { + // proteger les et tous les morceaux de php + if ($process_ins == 'php') + $corps = preg_replace_callback(',<[?](\s|php|=).*[?]>,UimsS', + 'echapper_php_callback', $corps); + foreach (explode('|', $headers['X-Spip-Filtre']) as $filtre) { + if ($f = chercher_filtre($filtre)) + $corps = $f($corps); + } + // restaurer les echappements + $corps = echapper_php_callback($corps); + unset($headers['X-Spip-Filtre']); + } + + return array('texte' => $corps, + 'squelette' => $nom, + 'source' => $source, + 'process_ins' => $process_ins, + 'invalideurs' => $cache, + 'entetes' => $headers, + 'duree' => isset($headers['X-Spip-Cache']) ? intval($headers['X-Spip-Cache']) : 0 + ); +} + + +// +// fonction standard de calcul de la balise #INTRODUCTION +// on peut la surcharger en definissant dans mes_fonctions : +// function filtre_introduction() +// +// http://doc.spip.org/@filtre_introduction_dist +function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) { + // Si un descriptif est envoye, on l'utilise directement + if (strlen($descriptif)) + return propre($descriptif,$connect); + + // De preference ce qui est marque ... + $intro = ''; + $texte = preg_replace(",(,i", "\\1intro>", $texte); // minuscules + while ($fin = strpos($texte, "")) { + $zone = substr($texte, 0, $fin); + $texte = substr($texte, $fin + strlen("")); + if ($deb = strpos($zone, "") OR substr($zone, 0, 7) == "") + $zone = substr($zone, $deb + 7); + $intro .= $zone; + } + + // [12025] On ne *PEUT* pas couper simplement ici car c'est du texte brut, + // qui inclus raccourcis et modeles + // un simple peut etre ensuite transforme en 1000 lignes ... + // par ailleurs le nettoyage des raccourcis ne tient pas compte + // des surcharges et enrichissement de propre + // couper doit se faire apres propre + //$texte = nettoyer_raccourcis_typo($intro ? $intro : $texte, $connect); + + // Cependant pour des questions de perfs on coupe quand meme, en prenant + // large et en se mefiant des tableaux #1323 + + if (strlen($intro)) + $texte = $intro; + + else + if (strpos("\n".$texte, "\n|")===false + AND strlen($texte) > 2.5*$longueur) + $texte = couper($texte, 2*$longueur); + + // ne pas tenir compte des notes + $notes = charger_fonction('notes', 'inc'); + $notes('','empiler'); + $texte = propre($texte,$connect); + $notes('','depiler'); + + if (!defined('_INTRODUCTION_SUITE')) define('_INTRODUCTION_SUITE', ' (...)'); + $texte = couper($texte, $longueur, _INTRODUCTION_SUITE); + + return $texte; +} + +// +// Balises dynamiques +// + +// elles sont traitees comme des inclusions +// http://doc.spip.org/@synthetiser_balise_dynamique + +define('CODE_INCLURE_BALISE', '<' . '?php +include_once("./" . _DIR_RACINE . "%s"); +if ($lang_select = "%s") $lang_select = lang_select($lang_select); +inserer_balise_dynamique(balise_%s_dyn(%s), array(%s)); +if ($lang_select) lang_select(); +?' + .'>'); + + +function synthetiser_balise_dynamique($nom, $args, $file, $context_compil) { + $r = sprintf(CODE_INCLURE_BALISE, + $file, + $context_compil[4]?$context_compil[4]:'', + $nom, + join(', ', array_map('argumenter_squelette', $args)), + join(', ', array_map('_q', $context_compil))); + return $r; +} + +// http://doc.spip.org/@argumenter_squelette +function argumenter_squelette($v) { + + if (!is_array($v)) + return "'" . texte_script($v) . "'"; + else { + $out = array(); + foreach($v as $k=>$val) + $out [] = argumenter_squelette($k) . '=>' . argumenter_squelette($val); + return 'array(' . join(", ", $out) . ')'; + } +} + +// verifier leurs arguments et filtres, et calculer le code a inclure +// http://doc.spip.org/@executer_balise_dynamique +function executer_balise_dynamique($nom, $args, $context_compil) { + $p = strpos($nom,"_"); + $nomfonction = $nom; + $nomfonction_generique = substr($nom,0,$p+1); + if (!$file = include_spip("balise/". strtolower($nomfonction))) { + // pas de fichier associe, passer au traitement generique + $file = include_spip("balise/" .strtolower($nomfonction_generique)); + if ($file) { + // et injecter en premier arg le nom de la balise + array_unshift($args,$nom); + // et passer sur la fonction generique pour la suite + $nomfonction = $nomfonction_generique; + } + else { + $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom)); + erreur_squelette($msg, $context_compil); + return ''; + } + } + // Y a-t-il une fonction de traitement des arguments ? + $f = 'balise_' . $nomfonction . '_stat'; + + $r = !function_exists($f) ? $args : $f($args, $context_compil); + + if (!is_array($r)) return $r; + + // verifier que la fonction dyn est la, + // sinon se replier sur la generique si elle existe + if (!function_exists('balise_' . $nomfonction . '_dyn')) { + $file = include_spip("balise/" .strtolower($nomfonction_generique)); + if (function_exists('balise_' . $nomfonction_generique . '_dyn')) { + // et lui injecter en premier arg le nom de la balise + array_unshift($r,$nom); + $nomfonction = $nomfonction_generique; + } else { + $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom)); + erreur_squelette($msg, $context_compil); + return ''; + } + } + + if (!_DIR_RESTREINT) + $file = _DIR_RESTREINT_ABS . $file; + return synthetiser_balise_dynamique($nomfonction, $r, $file, $context_compil); +} + +// http://doc.spip.org/@lister_objets_avec_logos +function lister_objets_avec_logos ($type) { + global $formats_logos; + $logos = array(); + $chercher_logo = charger_fonction('chercher_logo', 'inc'); + $type = '/' + . type_du_logo($type) + . "on(\d+)\.(" + . join('|',$formats_logos) + . ")$/"; + + if ($d = @opendir(_DIR_LOGOS)) { + while($f = readdir($d)) { + if (preg_match($type, $f, $r)) + $logos[] = $r[1]; + } + } + @closedir($d); + return join(',',$logos); +} + +// fonction appelee par la balise #NOTES +// Renvoyer l'etat courant des notes, le purger et en preparer un nouveau +// http://doc.spip.org/@calculer_notes +function calculer_notes() { + $notes = charger_fonction('notes', 'inc'); + $r = $notes(array()); + $notes('','depiler'); + $notes('','empiler'); + return $r; +} + +// Selectionner la langue de l'objet dans la boucle, sauf dans les +// cas ou il ne le faut pas :-) +function lang_select_public($lang, $lang_select, $titre=null) { + // Cas 1. forcer_lang = true et pas de critere {lang_select} + if (isset($GLOBALS['forcer_lang']) AND $GLOBALS['forcer_lang'] + AND $lang_select !== 'oui') + return; + + // Cas 2. l'objet n'a pas de langue definie (ou definie a '') + if (!strlen($lang)) + return; + + // Cas 3. l'objet est multilingue ! + if ($lang_select !== 'oui' + AND strlen($titre) > 10 + AND strpos($titre, '') !== false + AND strpos(echappe_html($titre), '') !== false) + return; + + // Tous les cas ayant ete elimines, faire le job + $GLOBALS['spip_lang'] = $lang; + return; +} + + +// Si un tableau &doublons[articles] est passe en parametre, +// il faut le nettoyer car il pourrait etre injecte en SQL +// http://doc.spip.org/@nettoyer_env_doublons +function nettoyer_env_doublons($envd) { + foreach ($envd as $table => $liste) { + $n = ''; + foreach(explode(',',$liste) as $val) { + if ($a = intval($val) AND $val === strval($a)) + $n.= ','.$val; + } + if (strlen($n)) + $envd[$table] = $n; + else + unset($envd[$table]); + } + return $envd; +} + +// http://doc.spip.org/@match_self +function match_self($w){ + if (is_string($w)) return false; + if (is_array($w)) { + if (in_array(reset($w),array("SELF","SUBSELECT"))) return $w; + foreach($w as $sw) + if ($m=match_self($sw)) return $m; + } + return false; +} +// http://doc.spip.org/@remplace_sous_requete +function remplace_sous_requete($w,$sousrequete){ + if (is_array($w)) { + if (in_array(reset($w),array("SELF","SUBSELECT"))) return $sousrequete; + foreach($w as $k=>$sw) + $w[$k] = remplace_sous_requete($sw,$sousrequete); + } + return $w; +} +// http://doc.spip.org/@trouver_sous_requetes +function trouver_sous_requetes($where){ + $where_simples = array(); + $where_sous = array(); + foreach($where as $k=>$w){ + if (match_self($w)) $where_sous[$k] = $w; + else $where_simples[$k] = $w; + } + return array($where_simples,$where_sous); +} + +// La fonction presente dans les squelettes compiles + +// http://doc.spip.org/@calculer_select +function calculer_select ($select = array(), $from = array(), + $from_type = array(), + $where = array(), $join=array(), + $groupby = array(), $orderby = array(), $limit = '', + $having=array(), $table = '', $id = '', $serveur='', $requeter=true) { + +// retirer les criteres vides: +// {X ?} avec X absent de l'URL +// {par #ENV{X}} avec X absent de l'URL +// IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil) + + $menage = false; + foreach($where as $k => $v) { + if (is_array($v)){ + if ((count($v)>=2) && ($v[0]=='REGEXP') && ($v[2]=="'.*'")) $op= false; + elseif ((count($v)>=2) && ($v[0]=='LIKE') && ($v[2]=="'%'")) $op= false; + else $op = $v[0] ? $v[0] : $v; + } else $op = $v; + if ((!$op) OR ($op==1) OR ($op=='0=0')) { + unset($where[$k]); + $menage = true; + } + } + + // evacuer les eventuels groupby vide issus d'un calcul dynamique + $groupby = array_diff($groupby,array('')); + + // remplacer les sous requetes recursives au calcul + list($where_simples,$where_sous) = trouver_sous_requetes($where); + //var_dump($where_sous); + foreach($where_sous as $k=>$w) { + $menage = true; + // on recupere la sous requete + $sous = match_self($w); + if ($sous[0]=='SELF') { + // c'est une sous requete identique a elle meme sous la forme (SELF,$select,$where) + array_push($where_simples,$sous[2]); + $where[$k] = remplace_sous_requete($w,"(".calculer_select( + $sous[1], + $from, + $from_type, + array($sous[2],'0=0'), // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where + $join, + array(),array(),'', + $having,$table,$id,$serveur,false).")"); + } + if ($sous[0]=='SUBSELECT') { + // c'est une sous requete explicite sous la forme identique a sql_select : (SUBSELECT,$select,$from,$where,$groupby,$orderby,$limit,$having) + array_push($where_simples,$sous[3]); // est-ce utile dans ce cas ? + $where[$k] = remplace_sous_requete($w,"(".calculer_select( + $sous[1], # select + $sous[2], #from + array(), #from_type + $sous[3]?(is_array($sous[3])?$sous[3]:array($sous[3])):array(), #where, qui peut etre de la forme string comme dans sql_select + array(), #join + $sous[4]?$sous[4]:array(), #groupby + $sous[5]?$sous[5]:array(), #orderby + $sous[6], #limit + $sous[7]?$sous[7]:array(), #having + $table,$id,$serveur,false + ).")"); + } + array_pop($where_simples); + } + + foreach($having as $k => $v) { + if ((!$v) OR ($v==1) OR ($v=='0=0')) { + unset($having[$k]); + } + } + +// Installer les jointures. +// Retirer celles seulement utiles aux criteres finalement absents mais +// parcourir de la plus recente a la moins recente pour pouvoir eliminer Ln +// si elle est seulement utile a Ln+1 elle meme inutile + + $afrom = array(); + $equiv = array(); + $k = count($join); + foreach(array_reverse($join,true) as $cledef=>$j) { + $cle = $cledef; + // le format de join est : + // array(table depart, cle depart [,cle arrivee[,condition optionnelle and ...]]) + if (count($join[$cle])==2) $join[$cle][] = $join[$cle][1]; + if (count($join[$cle])==3) $join[$cle][] = ''; + list($t,$c,$carr,$and) = $join[$cle]; + // si le nom de la jointure n'a pas ete specifiee, on prend Lx avec x sont rang dans la liste + // pour compat avec ancienne convention + if (is_numeric($cle)) + $cle = "L$k"; + if (!$menage + OR isset($afrom[$cle]) + OR calculer_jointnul($cle, $select) + OR calculer_jointnul($cle, array_diff($join,array($cle=>$join[$cle]))) + OR calculer_jointnul($cle, $having) + OR calculer_jointnul($cle, $where_simples)) { + // on garde une ecriture decomposee pour permettre une simplification ulterieure si besoin + // sans recours a preg_match + // un implode(' ',..) est fait dans reinjecte_joint un peu plus bas + $afrom[$t][$cle] = array("\n" . + (isset($from_type[$cle])?$from_type[$cle]:"INNER")." JOIN", + $from[$cle], + "AS $cle", + "ON (", + "$cle.$c", + "=", + "$t.$carr", + ($and ? "AND ". $and:"") . + ")"); + if (isset($afrom[$cle])){ + $afrom[$t] = $afrom[$t] + $afrom[$cle]; + unset($afrom[$cle]); + } + $equiv[]= $carr; + } else { unset($join[$cledef]);} + unset($from[$cle]); + $k--; + } + + if (count($afrom)) { + // Regarder si la table principale ne sert finalement a rien comme dans + // class='on' + //#TOTAL_BOUCLE + //#TOTAL_BOUCLE + // ou dans + //#TOTAL_BOUCLE + // qui comporte plusieurs jointures + // ou dans + // #TOTAL_BOUCLE + // 0}{statut?} />#TOTAL_BOUCLE + // penser a regarder aussi la clause orderby pour ne pas simplifier abusivement + // #ID_ARTICLE + // penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement + // #TOTAL_BOUCLE + + list($t,$c) = each($from); + reset($from); + $e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/'; + if (!(strpos($t, ' ') OR // jointure des le depart cf boucle_doc + calculer_jointnul($t, $select, $e) OR + calculer_jointnul($t, $join, $e) OR + calculer_jointnul($t, $where, $e) OR + calculer_jointnul($t, $orderby, $e) OR + calculer_jointnul($t, $groupby, $e) OR + calculer_jointnul($t, $having, $e)) + && count($afrom[$t])) { + reset($afrom[$t]); + list($nt,$nfrom) = each($afrom[$t]); + unset($from[$t]); + $from[$nt] = $nfrom[1]; + unset($afrom[$t][$nt]); + $afrom[$nt] = $afrom[$t]; + unset($afrom[$t]); + $e = '/\b'.preg_quote($nfrom[6]).'\b/'; + $t = $nfrom[4]; + $alias = ""; + // verifier que les deux cles sont homonymes, sinon installer un alias dans le select + $oldcle = explode('.',$nfrom[6]); + $oldcle = end($oldcle); + $newcle = explode('.',$nfrom[4]); + $newcle = end($newcle); + if ($newcle!=$oldcle){ + $alias = ", ".$nfrom[4]." AS $oldcle"; + } + $select = remplacer_jointnul($t . $alias, $select, $e); + $join = remplacer_jointnul($t, $join, $e); + $where = remplacer_jointnul($t, $where, $e); + $having = remplacer_jointnul($t, $having, $e); + $groupby = remplacer_jointnul($t, $groupby, $e); + $orderby = remplacer_jointnul($t, $orderby, $e); + } + $from = reinjecte_joint($afrom, $from); + } + $GLOBALS['debug']['aucasou'] = array ($table, $id, $serveur, $requeter); + $r = sql_select($select, $from, $where, + $groupby, array_filter($orderby), $limit, $having, $serveur, $requeter); + unset($GLOBALS['debug']['aucasou']); + return $r; +} + +//condition suffisante (mais non necessaire) pour qu'une table soit utile + +// http://doc.spip.org/@calculer_jointnul +function calculer_jointnul($cle, $exp, $equiv='') +{ + if (!is_array($exp)) { + if ($equiv) $exp = preg_replace($equiv, '', $exp); + return preg_match("/\\b$cle\\./", $exp); + } else { + foreach($exp as $v) { + if (calculer_jointnul($cle, $v, $equiv)) return true; + } + return false; + } +} + +// http://doc.spip.org/@reinjecte_joint +function reinjecte_joint($afrom, $from) +{ + $from_synth = array(); + foreach($from as $k=>$v){ + $from_synth[$k]=$from[$k]; + if (isset($afrom[$k])) { + foreach($afrom[$k] as $kk=>$vv) $afrom[$k][$kk] = implode(' ',$afrom[$k][$kk]); + $from_synth["$k@"]= implode(' ',$afrom[$k]); + unset($afrom[$k]); + } + } + return $from_synth; +} + +// http://doc.spip.org/@remplacer_jointnul +function remplacer_jointnul($cle, $exp, $equiv='') +{ + if (!is_array($exp)) { + return preg_replace($equiv, $cle, $exp); + } else { + foreach($exp as $k => $v) { + $exp[$k] = remplacer_jointnul($cle, $v, $equiv); + } + return $exp; + } +} + +// calcul du nom du squelette +// http://doc.spip.org/@calculer_nom_fonction_squel +function calculer_nom_fonction_squel($skel, $mime_type='html', $connect='') +{ + // ne pas doublonner les squelette selon qu'ils sont calcules depuis ecrire/ ou depuis la racine + if (strlen(_DIR_RACINE) AND substr($skel,0,strlen(_DIR_RACINE))==_DIR_RACINE) + $skel = substr($skel,strlen(_DIR_RACINE)); + return $mime_type + . (!$connect ? '' : preg_replace('/\W/',"_", $connect)) . '_' + . md5($GLOBALS['spip_version_code'] . ' * ' . $skel); +} + +?>