From a4242faef06ec228d34bdbc17da1eaf32d16847e Mon Sep 17 00:00:00 2001
From: Fil <fil@rezo.net>
Date: Mon, 24 Mar 2008 22:29:21 +0000
Subject: [PATCH] Plusieurs changements qui permettent d'ajouter un champ a
 n'importe quelle table (y compris les tables 'officielles') et d'avoir tout
 de suite la balise #TOTO, et les crayons correspondants (mais pas encore
 l'interface dans l'espace prive): - on fait confiance a sql_showtable() pour
 nous indiquer les champs, plutot qu'a la version php de tables_principales
 etc - inc/modifier troque sa liste limitative de champs autorises contre une
 liste de champs interdits

---
 ecrire/action/editer_article.php |  87 ++++++++++------------
 ecrire/action/instituer_mot.php  |  11 ++-
 ecrire/action/legender.php       |   3 +-
 ecrire/base/trouver_table.php    |  23 ++++--
 ecrire/inc/forum_insert.php      |  17 +++--
 ecrire/inc/modifier.php          | 120 ++++++++++++++++++++-----------
 ecrire/public/balises.php        |   3 +-
 ecrire/public/references.php     |  47 +-----------
 8 files changed, 159 insertions(+), 152 deletions(-)

diff --git a/ecrire/action/editer_article.php b/ecrire/action/editer_article.php
index 71806e41cd..78e51b7502 100644
--- a/ecrire/action/editer_article.php
+++ b/ecrire/action/editer_article.php
@@ -46,17 +46,37 @@ function action_editer_article_dist() {
 // Appelle toutes les fonctions de modification d'un article
 // $err est de la forme '&trad_err=1'
 // http://doc.spip.org/@articles_set
-function articles_set($id_article, $c=false) {
+function articles_set($id_article) {
 	$err = '';
 
-	// Edition du contenu ?
-	$err .= revisions_articles($id_article, $c);
+	// unifier $texte en cas de texte trop long
+	trop_longs_articles();
+
+	$c = array();
+	foreach (array(
+		'surtitre', 'titre', 'soustitre', 'descriptif',
+		'nom_site', 'url_site', 'chapo', 'texte', 'ps'
+	) as $champ)
+		$c[$champ] = _request($champ);
+
+	if (_request('changer_virtuel') == 'oui') {
+		$r = _request('virtuel');
+		$c['chapo'] = (strlen($r) ? '='.$r : '');
+	}
+
+	include_spip('inc/modifier');
+	revision_article($id_article, $c);
 
 	// Modification de statut, changement de rubrique ?
+	$c = array();
+	foreach (array(
+		'date', 'statut', 'id_parent'
+	) as $champ)
+		$c[$champ] = _request($champ);
 	$err .= instituer_article($id_article, $c);
 
 	// Un lien de trad a prendre en compte
-	$err .= article_referent($id_article, $c);
+	$err .= article_referent($id_article, _request('id_trad'));
 
 	return $err;
 }
@@ -110,44 +130,6 @@ function insert_article($id_rubrique) {
 	return $id_article;
 }
 
-// Enregistre une revision d'article
-// $c est un contenu (par defaut on prend le contenu via _request())
-// http://doc.spip.org/@revisions_articles
-function revisions_articles ($id_article, $c=false) {
-	include_spip('inc/modifier');
-
-	// unifier $texte en cas de texte trop long (sur methode POST seulement)
-	if (!is_array($c)) trop_longs_articles();
-
-	// Si l'article est publie, invalider les caches et demander sa reindexation
-	$t = sql_getfetsel("statut", "spip_articles", "id_article=$id_article");
-	if ($t == 'publie') {
-		$invalideur = "id='id_article/$id_article'";
-		$indexation = true;
-	}
-
-	if (_request('changer_virtuel') == 'oui') {
-		$r = _request('virtuel');
-		if ($r) $r = "=$r";
-		set_request('chapo', $r);
-	}
-
-	modifier_contenu('article', $id_article,
-		array(
-			'champs' => array(
-				'surtitre', 'titre', 'soustitre', 'descriptif',
-				'nom_site', 'url_site', 'chapo', 'texte', 'ps','date','date_redac'
-			),
-			'nonvide' => array('titre' => _T('info_sans_titre')),
-			'invalideur' => $invalideur,
-			'indexation' => $indexation,
-			'date_modif' => 'date_modif' // champ a mettre a NOW() s'il y a modif
-		),
-		$c);
-
-	return ''; // pas d'erreur
-}
-
 
 // $c est un array ('statut', 'id_parent' = changement de rubrique)
 //
@@ -164,9 +146,9 @@ function instituer_article($id_article, $c, $calcul_rub=true) {
 	$id_rubrique = $row['id_rubrique'];
 	$statut_ancien = $statut = $row['statut'];
 	$champs = array();
-	$date = _request('date', $c);
+	$date = $c['date'];
 
-	$s = _request('statut', $c);
+	$s = $c['statut'];
 
 	// cf autorisations dans inc/instituer_article
 	if ($s AND $s != $statut) {
@@ -184,7 +166,7 @@ function instituer_article($id_article, $c, $calcul_rub=true) {
 		OR ($champs['statut'] == 'prop'
 			AND !in_array($statut_ancien, array('publie', 'prop'))
 		)) {
-			if ($date)
+			if (!is_null($date))
 				$champs['date'] = $date;
 			else {
 				# on prend la date de MySQL pour eviter un decalage cf. #975
@@ -196,7 +178,7 @@ function instituer_article($id_article, $c, $calcul_rub=true) {
 
 	// Verifier que la rubrique demandee existe et est differente
 	// de la rubrique actuelle
-	if ($id_parent = _request('id_parent', $c)
+	if ($id_parent = $c['id_parent']
 	AND $id_parent != $id_rubrique
 	AND (sql_fetsel('1', "spip_rubriques", "id_rubrique=$id_parent"))) {
 		$champs['id_rubrique'] = $id_parent;
@@ -306,9 +288,9 @@ function trop_longs_articles() {
 
 // Poser un lien de traduction vers un article de reference
 // http://doc.spip.org/@article_referent
-function article_referent ($id_article, $c) {
+function article_referent ($id_article, $lier_trad) {
 
-	if (!$lier_trad = intval(_request('lier_trad', $c))) return;
+	if (!$lier_trad = intval($c['lier_trad'])) return;
 
 	// selectionner l'article cible, qui doit etre different de nous-meme,
 	// et quitter s'il n'existe pas
@@ -336,4 +318,13 @@ function article_referent ($id_article, $c) {
 }
 
 
+
+// obsolete, utiliser revision_article dans inc/modifier
+// http://doc.spip.org/@revisions_articles
+function revisions_articles ($id_article, $c=false) {
+	include_spip('inc/modifier');
+	return revision_article($id_article,$c);
+}
+
+
 ?>
diff --git a/ecrire/action/instituer_mot.php b/ecrire/action/instituer_mot.php
index ef5b07c3b7..72ad81133b 100644
--- a/ecrire/action/instituer_mot.php
+++ b/ecrire/action/instituer_mot.php
@@ -41,10 +41,17 @@ function action_instituer_mot_post($r)
 
 	// modifier le contenu via l'API
 	include_spip('inc/modifier');
-	revision_mot($id_mot);
+
+	$c = array();
+	foreach (array(
+		'titre', 'descriptif', 'texte', 'id_groupe', 'type', 'id_groupe'
+	) as $champ)
+		$c[$champ] = _request($champ);
+
+	revision_mot($id_mot, $c);
 	if ($redirect = _request('redirect'))
 		redirige_par_entete(parametre_url(urldecode($redirect),
-						  'id_mot', $id_mot, '&'));
+			'id_mot', $id_mot, '&'));
 
 }
 
diff --git a/ecrire/action/legender.php b/ecrire/action/legender.php
index 8714902d7d..3d5aede413 100644
--- a/ecrire/action/legender.php
+++ b/ecrire/action/legender.php
@@ -69,7 +69,8 @@ function action_legender_post($r)
 		$modifs['descriptif'] = $t;
 
 	include_spip('inc/modifier');
-
 	revision_document($id_document, $modifs);
+
 }
+
 ?>
diff --git a/ecrire/base/trouver_table.php b/ecrire/base/trouver_table.php
index 34d0b2cac0..1e2ef7d11e 100644
--- a/ecrire/base/trouver_table.php
+++ b/ecrire/base/trouver_table.php
@@ -35,9 +35,12 @@ function base_trouver_table_dist($nom, $serveur='')
 	$nom_sql = $nom;
 	if (preg_match('/\.(.*)$/', $nom, $s))
 		$nom_sql = $s[1];
-	else $nom_sql = $nom;
+	else
+		$nom_sql = $nom;
+
 	$desc = '';
 	$connexion = $GLOBALS['connexions'][$serveur ? $serveur : 0];
+
 	// base sous SPIP: gerer les abreviations des noms de table
 	if ($connexion['spip_connect_version']) {
 		include_spip('public/interfaces');
@@ -46,7 +49,7 @@ function base_trouver_table_dist($nom, $serveur='')
 			$nom_sql = 'spip_' . $t;
 			if (!isset($connexion['tables'][$nom_sql])) {
 				include_spip('base/serial');
-				$desc = $tables_principales[$nom_sql];
+				$fdesc = $tables_principales[$nom_sql];
 				$nom = $t;
 			}
 		} else {
@@ -54,18 +57,25 @@ function base_trouver_table_dist($nom, $serveur='')
 			if (isset($tables_auxiliaires['spip_' .$nom])) {
 				$nom_sql = 'spip_' . $nom;
 				if (!isset($connexion['tables'][$nom_sql])) {
-					$desc = $tables_auxiliaires[$nom_sql];
+					$fdesc = $tables_auxiliaires[$nom_sql];
 				}
 			}  # table locale a cote de SPIP, comme non SPIP:
 		}
 	}
+
 	if (!isset($connexion['tables'][$nom_sql])) {
-		if (!$desc) {
+		// La *vraie* base a la priorite
+		if (true /*  !$bdesc OR !$bdesc['field']  */) {
 			$t = ($nom_sql != $nom);
 			$desc = sql_showtable($nom_sql, $t, $serveur);
 			if (!$desc OR !$desc['field']) {
-				spip_log("table inconnue $serveur $nom");
-				return null;
+				if (!$fdesc) {
+					spip_log("table inconnue $serveur $nom");
+					return null;
+				}
+				// on ne sait pas lire la structure de la table :
+				// on retombe sur la description donnee dans les fichiers spip
+				$desc = $fdesc;
 			}
 		}
 		$desc['table']= $nom_sql;
@@ -73,6 +83,7 @@ function base_trouver_table_dist($nom, $serveur='')
 		$desc['connexion']= $serveur;
 		$connexion['tables'][$nom_sql] = $desc;
 	}
+
 	return $connexion['tables'][$nom_sql];
 }
 ?>
diff --git a/ecrire/inc/forum_insert.php b/ecrire/inc/forum_insert.php
index a803c0541e..f859ac3573 100644
--- a/ecrire/inc/forum_insert.php
+++ b/ecrire/inc/forum_insert.php
@@ -192,14 +192,19 @@ function inc_forum_insert_dist($force_statut = NULL) {
 	// Entree du contenu et invalidation des caches
 	//
 	include_spip('inc/modifier');
-	// Injecter les bonnes valeurs dans le contexte $c
-	$auteur = sinon($GLOBALS['visiteur_session']['nom'],
+
+	$c = array();
+	foreach (array(
+		'titre', 'texte', 'nom_site', 'url_site'
+	) as $champ)
+		$c[$champ] = _request($champ);
+
+	$c['auteur'] = sinon($GLOBALS['visiteur_session']['nom'],
 		$GLOBALS['visiteur_session']['session_nom']);
-	$email_auteur = sinon($GLOBALS['visiteur_session']['email'],
+	$c['email_auteur'] = sinon($GLOBALS['visiteur_session']['email'],
 		$GLOBALS['visiteur_session']['session_email']);
-	set_request('auteur', $auteur);
-	set_request('email_auteur', $email_auteur);
-	revision_forum($id_message);
+
+	revision_forum($id_message, $c);
 
 	// Notification
 	if ($notifications = charger_fonction('notifications', 'inc'))
diff --git a/ecrire/inc/modifier.php b/ecrire/inc/modifier.php
index 2fe3034e65..725b87f817 100644
--- a/ecrire/inc/modifier.php
+++ b/ecrire/inc/modifier.php
@@ -16,37 +16,59 @@ if (!defined("_ECRIRE_INC_VERSION")) return;
 // Une fonction generique pour l'API de modification de contenu
 // $options est un array() avec toutes les options
 //
-// Pour l'instant fonctionne pour les types :
-//   article, auteur, document, forum
 // renvoie false si rien n'a ete modifie, true sinon
 //
+// Attention, pour eviter des hacks on interdit les champs
+// (statut, id_secteur, id_rubrique, id_parent),
+// mais la securite doit etre assuree en amont
+//
 // http://doc.spip.org/@modifier_contenu
-function modifier_contenu($type, $id, $options, $c=false) {
+function modifier_contenu($type, $id, $options, $c=false, $serveur='') {
 	include_spip('inc/filtres');
 
 	$table_objet = table_objet($type);
 	$id_table_objet = id_table_objet($type);
+	$trouver_table = charger_fonction('trouver_table', 'base');
+	$desc = $trouver_table($table_objet, $serveur);
+
+	// Appels incomplets (sans $c)
+	if (!is_array($c)) {
+		spip_log('erreur appel modifier_contenu('.$type.'), manque $c');
+		return false;
+	}
+
+	// Securite : certaines variables ne sont jamais acceptees ici
+	// car elles ne relevent pas de autoriser(article, modifier) ;
+	// il faut passer par instituer_XX()
+	// TODO: faut-il passer ces variables interdites
+	// dans un fichier de description separe ?
+	unset($c['statut']);
+	unset($c['id_parent']);
+	unset($c['id_rubrique']);
+	unset($c['id_secteur']);
 
 	// Gerer les champs non vides
 	if (is_array($options['nonvide']))
-	foreach ($options['nonvide'] as $champ => $sinon) {
-		if (_request($champ, $c) === '') {
-			$c = set_request($champ, $sinon, $c);
-		}
-	}
+	foreach ($options['nonvide'] as $champ => $sinon)
+		if ($c[$champ] === '')
+			$c[$champ] = $sinon;
+
 
+	// N'accepter que les champs qui existent
+	// TODO: ici aussi on peut valider les contenus
+	// en fonction du type
 	$champs = array();
-	if (is_array($options['champs']))
-	foreach ($options['champs'] as $champ) {
-		$val = _request($champ, $c);
-		if ($val !== NULL)
-			$champs[$champ] = corriger_caracteres($val);
-	}
+	foreach($desc['field'] as $champ => $ignore)
+		if (isset($c[$champ]))
+			$champs[$champ] = $c[$champ];
+
+	// Nettoyer les valeurs
+	$champs = array_map('corriger_caracteres', $champs);
 
 	// recuperer les extras
 	if ($GLOBALS['champs_extra']) {
 		include_spip('inc/extra');
-		if ($extra = extra_update($table_objet, $id, $c))
+		if ($extra = extra_update($table_objet, $id, $champs))
 			$champs['extra'] = $extra;
 	}
 
@@ -80,18 +102,21 @@ function modifier_contenu($type, $id, $options, $c=false) {
 	$verifier = array();
 	foreach ($champs as $ch => $val)
 		$verifier[] = $ch.'='.$val;
+
 	$verifier = "$id_table_objet=$id AND NOT (".join(' AND ', $verifier).')';
-	if (!sql_countsel("spip_$table_objet", $verifier))
+	if (!sql_countsel("spip_$table_objet", $verifier,
+	null,null,null, null, $serveur))
 		return false;
 
 	// la modif peut avoir lieu
 
 	// faut-il ajouter date_modif ?
-	if ($options['date_modif'])
+	if ($options['date_modif']
+	AND !isset($champs[$options['date_modif']]))
 		$champs[$options['date_modif']] = 'NOW()';
 
 	// allez on commit la modif
-	sql_update("spip_$table_objet", $champs, "$id_table_objet=$id");
+	sql_update("spip_$table_objet", $champs, "$id_table_objet=$id", $serveur);
 
 	// Invalider les caches
 	if ($options['invalideur']) {
@@ -149,13 +174,35 @@ function marquer_doublons_documents($champs,$id,$id_table_objet,$table_objet){
 	}
 }
 
+// Enregistre une revision d'article
+// http://doc.spip.org/@revision_article
+function revision_article ($id_article, $c=false) {
+
+	// Si l'article est publie, invalider les caches et demander sa reindexation
+	$t = sql_getfetsel("statut", "spip_articles", "id_article=$id_article");
+	if ($t == 'publie') {
+		$invalideur = "id='id_article/$id_article'";
+		$indexation = true;
+	}
+
+	modifier_contenu('article', $id_article,
+		array(
+			'nonvide' => array('titre' => _T('info_sans_titre')),
+			'invalideur' => $invalideur,
+			'indexation' => $indexation,
+			'date_modif' => 'date_modif' // champ a mettre a NOW() s'il y a modif
+		),
+		$c);
+
+	return ''; // pas d'erreur
+}
+
 // http://doc.spip.org/@revision_document
 function revision_document($id_document, $c=false) {
 
 	return modifier_contenu('document', $id_document,
 		array(
-			'champs' => array('titre', 'descriptif', 'date', 'largeur', 'hauteur')
-			//,'nonvide' => array('titre' => _T('info_sans_titre'))
+			// 'nonvide' => array('titre' => _T('info_sans_titre'))
 		),
 		$c);
 }
@@ -165,7 +212,6 @@ function revision_signature($id_signature, $c=false) {
 
 	return modifier_contenu('signature', $id_signature,
 		array(
-			'champs' => array('nom_email', 'ad_email', 'nom_site', 'url_site', 'message'),
 			'nonvide' => array('nom_email' => _T('info_sans_titre'))
 		),
 		$c);
@@ -177,7 +223,6 @@ function revision_auteur($id_auteur, $c=false) {
 
 	modifier_contenu('auteur', $id_auteur,
 		array(
-			'champs' => array('nom', 'bio', 'pgp', 'nom_site', 'url_site', 'email', 'login'),
 			'nonvide' => array('nom' => _T('ecrire:item_nouvel_auteur'))
 		),
 		$c);
@@ -188,19 +233,16 @@ function revision_auteur($id_auteur, $c=false) {
 function revision_mot($id_mot, $c=false) {
 
 	// regler le groupe
-	if (NULL !== ($id_groupe = _request('id_groupe',$c))
-	OR NULL !== ($type = _request('type',$c))) {
-		$result = sql_select("titre", "spip_groupes_mots", "id_groupe=".sql_quote($id_groupe));
+	if (isset($c['id_groupe']) OR isset($c['type'])) {
+		$result = sql_select("titre", "spip_groupes_mots", "id_groupe=".intval($id_groupe));
 		if ($row = sql_fetch($result))
-			$type = $row['titre'];
+			$c['type'] = $row['titre'];
 		else
-			$type = NULL;
-		$c = set_request('type', $type, $c);
+			unset($c['type']);
 	}
 
 	modifier_contenu('mot', $id_mot,
 		array(
-			'champs' => array('titre', 'descriptif', 'texte', 'id_groupe', 'type'),
 			'nonvide' => array('titre' => _T('info_sans_titre'))
 		),
 		$c);
@@ -210,9 +252,7 @@ function revision_mot($id_mot, $c=false) {
 function revision_petition($id_article, $c=false) {
 
 	modifier_contenu('petition', $id_article,
-		array(
-			'champs' => array('texte')
-		),
+		array(),
 		$c);
 }
 
@@ -243,27 +283,23 @@ function revision_forum($id_forum, $c=false) {
 		$invalideur = '';
 
 	// Supprimer 'http://' tout seul
-	$u = _request('url_site', $c);
-	if ($u !== NULL) {
+	if (isset($c['url_site'])) {
 		include_spip('inc/filtres');
-		$c = set_request('url_site', vider_url($u, false), $c);
+		$c['url_site'] = vider_url($c['url_site'], false);
 	}
 
 	$r = modifier_contenu('forum', $id_forum,
 		array(
-			'champs' => array('titre', 'texte', 'auteur', 'email_auteur', 'nom_site', 'url_site'),
 			'nonvide' => array('titre' => _T('info_sans_titre')),
 			'invalideur' => $invalideur
 		),
 		$c);
 
 
-	// Modification des id_article etc: ce n'est pas autorise en standard
-	// mais ca peut servir pour des crayons ; du coup on teste ici que
-	// la donnee provient bien de $c, pour eviter tout hack lors d'un envoi
-	// normal de forum ; et on deplace tout le thread {sauf les originaux}.
-	if (is_array($c)
-	AND count($cles = array_intersect(array_keys($c),
+	// Modification des id_article etc
+	// (non autorise en standard mais utile pour des crayons)
+	// on deplace tout le thread {sauf les originaux}.
+	if (count($cles = array_intersect(array_keys($c),
 		array('id_article', 'id_rubrique', 'id_syndic', 'id_breve')))
 	) {
 		$thread = sql_fetsel("id_thread", "spip_forum", "id_forum=$id_forum");
diff --git a/ecrire/public/balises.php b/ecrire/public/balises.php
index 331513b520..6d5c8a1d75 100644
--- a/ecrire/public/balises.php
+++ b/ecrire/public/balises.php
@@ -924,7 +924,8 @@ function balise_EVAL_dist($p) {
 // http://doc.spip.org/@balise_CHAMP_SQL_dist
 function balise_CHAMP_SQL_dist($p){
 	$p->code = '';
-	if (isset($p->param[0][1][0]) AND $champ = ($p->param[0][1][0]->texte))
+	if (isset($p->param[0][1][0])
+	AND $champ = ($p->param[0][1][0]->texte))
 		$p->code = champ_sql($champ, $p);
 
 	#$p->interdire_scripts = true;
diff --git a/ecrire/public/references.php b/ecrire/public/references.php
index fc48e1fbdb..b5a5cff660 100644
--- a/ecrire/public/references.php
+++ b/ecrire/public/references.php
@@ -42,6 +42,7 @@ function index_pile($idb, $nom_champ, &$boucles, $explicite='') {
 	// il y a incoherences qu'il vaut mieux eviter
 	while (isset($boucles[$idb])) {
 		list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles);
+
 		if ($t) {
 		  if (!in_array($t, $boucles[$idb]->select)) {
 		    $boucles[$idb]->select[] = $t;
@@ -205,52 +206,6 @@ function calculer_balise($nom, $p) {
 	return $p;
 }
 
-/*
-
-L'appel direct de #ARTICLE_TRADUCTIONS devient #MODELE{article_traductions}
-
-// fonction speciale d'appel a un modele modeles/truc.html pour la balise #TRUC
-// exemples : #TRADUCTIONS, #DOC, #IMG...
-// http://doc.spip.org/@calculer_balise_modele_dist
-function calculer_balise_modele_dist($p){
-	$nom = strtolower($p->nom_champ);
-	$contexte = array();
-
-	if (isset($p->param[0])){
-		while (count($p->param[0])>2){
-			$p->param[]=array($p->param[0][0],array_pop($p->param[0]));
-		}
-	}
-print_r($p->param);
-	$champ = phraser_arguments_inclure($p, true); 
-	// a priori true
-	// si false, le compilo va bloquer sur des syntaxes avec un filtre sans argument qui suit la balise
-	// si true, les arguments simples (sans truc=chose) vont degager
-	$code_contexte = argumenter_inclure($champ, $p->descr, $p->boucles, $p->id_boucle, false);
-
-	// Si le champ existe dans la pile, on le met dans le contexte
-	// (a priori c'est du code mort ; il servait pour #LESAUTEURS dans
-	// le cas spip_syndic_articles)
-	#$code_contexte[] = "'$nom='.".champ_sql($nom, $p);
-
-	// Reserver la cle primaire de la boucle courante
-	if ($primary = $p->boucles[$p->id_boucle]->primary) {
-		$id = champ_sql($primary, $p);
-		$code_contexte[] = "'$primary='.".$id;
-	}
-
-#print_r($code_contexte);
-
-	$p->code = "( ((\$recurs=(isset(\$Pile[0]['recurs'])?\$Pile[0]['recurs']:0))<5)?
-	recuperer_fond('modeles/".$nom."',
-		creer_contexte_de_modele(array(".join(',', $code_contexte).",'recurs='.++\$recurs, \$GLOBALS['spip_lang']))):'')";
-	$p->interdire_scripts = false; // securite assuree par le squelette
-
-print $p->code."\n<hr/>\n";
-
-	return $p;
-}
-*/
 
 //
 // Traduction des balises dynamiques, notamment les "formulaire_*"
-- 
GitLab