3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2011 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) return;
14 include_spip('base/abstract_sql');
16 // http://doc.spip.org/@formulaires_editer_objet_traiter
17 function formulaires_editer_objet_traiter($type, $id='new', $id_parent=0, $lier_trad=0, $retour='', $config_fonc='articles_edit_config', $row=array(), $hidden=''){
20 $action_editer = charger_fonction("editer_$type",'action');
21 list($id,$err) = $action_editer($id);
22 $id_table_objet = id_table_objet($type);
23 $res[$id_table_objet] = $id;
25 $res['message_erreur'] = ($err?
$err:_T('erreur'));
28 $res['message_ok'] = ""; // il faudrait faire mieux que cela !
30 $res['redirect'] = parametre_url($retour,$id_table_objet,$id);
35 // http://doc.spip.org/@formulaires_editer_objet_verifier
36 function formulaires_editer_objet_verifier($type,$id='new', $oblis = array()){
39 $conflits = controler_contenu($type,$id);
40 if (count($conflits)) {
41 foreach($conflits as $champ=>$conflit){
42 $erreurs[$champ] .= _T("alerte_modif_info_concourante")."<br /><textarea readonly='readonly' class='forml'>".$conflit['base']."</textarea>";
46 foreach($oblis as $obli){
48 $erreurs[$obli] .= _T("info_obligatoire");
53 // http://doc.spip.org/@formulaires_editer_objet_charger
54 function formulaires_editer_objet_charger($type, $id='new', $id_parent=0, $lier_trad=0, $retour='', $config_fonc='articles_edit_config', $row=array(), $hidden=''){
55 $table_objet = table_objet($type);
56 $table_objet_sql = table_objet_sql($type);
57 $id_table_objet = id_table_objet($type);
58 $new = !is_numeric($id);
60 // Appel direct dans un squelette
62 if (!$new OR $lier_trad) {
63 if ($select = charger_fonction($type."_select",'inc',true))
64 $row = $select($id, $id_parent, $lier_trad);
65 else $row = sql_fetsel('*',$table_objet_sql,$id_table_objet."=".intval($id));
67 $md5 = controles_md5($row);
70 $trouver_table = charger_fonction('trouver_table','base');
71 if ($desc = $trouver_table($table_objet))
72 foreach($desc['field'] as $k=>$v) $row[$k]='';
75 // Gaffe: sans ceci, on ecrase systematiquement l'article d'origine
76 // (et donc: pas de lien de traduction)
77 $id = ($new OR $lier_trad)
79 : $row[$id_table_objet];
80 $row[$id_table_objet] = $id;
83 if ($id_parent && (!isset($contexte['id_parent']) OR $new))
84 $contexte['id_parent']=$id_parent;
87 $contexte['config'] = $config = $config_fonc($contexte);
88 $att_text = " class='textarea' "
90 . ($config['lignes'] +
15)
92 list($contexte['texte'],$contexte['_texte_trop_long']) = editer_texte_recolle($contexte['texte'],$att_text);
94 // on veut conserver la langue de l'interface ;
95 // on passe cette donnee sous un autre nom, au cas ou le squelette
96 // voudrait l'exploiter
97 if (isset($contexte['lang'])) {
98 $contexte['langue'] = $contexte['lang'];
99 unset($contexte['lang']);
102 $contexte['_hidden'] = "<input type='hidden' name='editer_$type' value='oui' />\n" .
104 ("\n<input type='hidden' name='lier_trad' value='" .
107 "\n<input type='hidden' name='changer_lang' value='" .
114 if (isset($contexte['extra']))
115 $contexte['extra'] = unserialize($contexte['extra']);
116 // preciser que le formulaire doit passer dans un pipeline
117 $contexte['_pipeline'] = array('editer_contenu_objet',array('type'=>$type,'id'=>$id));
119 // preciser que le formulaire doit etre securise auteur/action
120 // n'est plus utile lorsque l'action accepte l'id en argument direct
121 // on le garde pour compat
122 $contexte['_action'] = array("editer_$type",$id);
128 // Gestion des textes trop longs (limitation brouteurs)
129 // utile pour les textes > 32ko
131 // http://doc.spip.org/@coupe_trop_long
132 function coupe_trop_long($texte){
133 $aider = charger_fonction('aider', 'inc');
134 if (strlen($texte) > 28*1024) {
135 $texte = str_replace("\r\n","\n",$texte);
136 $pos = strpos($texte, "\n\n", 28*1024); // coupe para > 28 ko
137 if ($pos > 0 and $pos < 32 * 1024) {
138 $debut = substr($texte, 0, $pos)."\n\n<!--SPIP-->\n";
139 $suite = substr($texte, $pos +
2);
141 $pos = strpos($texte, " ", 28*1024); // sinon coupe espace
142 if (!($pos > 0 and $pos < 32 * 1024)) {
143 $pos = 28*1024; // au pire (pas d'espace trouv'e)
144 $decalage = 0; // si y'a pas d'espace, il ne faut pas perdre le caract`ere
148 $debut = substr($texte,0,$pos +
$decalage); // Il faut conserver l'espace s'il y en a un
149 $suite = substr($texte,$pos +
$decalage);
151 return (array($debut,$suite));
154 return (array($texte,''));
157 // http://doc.spip.org/@editer_texte_recolle
158 function editer_texte_recolle($texte, $att_text)
160 if ((strlen($texte)<29*1024)
161 OR (include_spip('inc/layer') AND ($GLOBALS['browser_name']!="MSIE")) )
162 return array($texte,"");
164 include_spip('inc/barre');
165 $textes_supplement = "<br /><span style='color: red'>"._T('info_texte_long')."</span>\n";
168 while (strlen($texte)>29*1024) {
170 list($texte1,$texte) = coupe_trop_long($texte);
171 $textes_supplement .= "<br />" .
172 "<textarea id='texte$nombre' name='texte_plus[$nombre]'$att_text>$texte1</textarea>\n";
174 return array($texte,$textes_supplement);
177 // Produit la liste des md5 d'un tableau de donnees, sous forme
179 // http://doc.spip.org/@controles_md5
180 function controles_md5($data, $prefixe='ctr_', $format='html'){
181 if (!is_array($data))
185 foreach ($data as $key => $val) {
191 $ctr[$k] = "<input type='hidden' value='$m' name='$k' />";
199 if ($format == 'html')
200 return "\n\n<!-- controles md5 -->\n".join("\n", $ctr)."\n\n";
205 // http://doc.spip.org/@controler_contenu
206 function controler_contenu($type, $id, $options=array(), $c=false, $serveur='') {
207 include_spip('inc/filtres');
209 $table_objet = table_objet($type);
210 $spip_table_objet = table_objet_sql($type);
211 $id_table_objet = id_table_objet($type);
212 $trouver_table = charger_fonction('trouver_table', 'base');
213 $desc = $trouver_table($table_objet, $serveur);
215 // Appels incomplets (sans $c)
217 foreach($desc['field'] as $champ=>$ignore)
219 $c[$champ] = _request($champ);
222 // Securite : certaines variables ne sont jamais acceptees ici
223 // car elles ne relevent pas de autoriser(article, modifier) ;
224 // il faut passer par instituer_XX()
225 // TODO: faut-il passer ces variables interdites
226 // dans un fichier de description separe ?
228 unset($c['id_parent']);
229 unset($c['id_rubrique']);
230 unset($c['id_secteur']);
232 // Gerer les champs non vides
233 if (is_array($options['nonvide']))
234 foreach ($options['nonvide'] as $champ => $sinon)
235 if ($c[$champ] === '')
238 // N'accepter que les champs qui existent
239 // TODO: ici aussi on peut valider les contenus
240 // en fonction du type
242 foreach($desc['field'] as $champ => $ignore)
243 if (isset($c[$champ]))
244 $champs[$champ] = $c[$champ];
246 // Nettoyer les valeurs
247 $champs = array_map('corriger_caracteres', $champs);
249 // Envoyer aux plugins
250 $champs = pipeline('pre_edition',
253 'table' => $spip_table_objet, // compatibilite
254 'table_objet' => $table_objet,
255 'spip_table_objet' => $spip_table_objet,
258 'champs' => $options['champs'],
259 'action' => 'controler'
265 if (!$champs) return array();
267 // Verifier si les mises a jour sont pertinentes, datees, en conflit etc
268 $conflits = controler_md5($champs, $_POST, $type, $id, $serveur, $options['prefix']?
$options['prefix']:'ctr_');
273 // Controle la liste des md5 envoyes, supprime les inchanges,
274 // signale les modifies depuis telle date
275 // http://doc.spip.org/@controler_md5
276 function controler_md5(&$champs, $ctr, $type, $id, $serveur, $prefix = 'ctr_') {
277 $table_objet = table_objet($type);
278 $spip_table_objet = table_objet_sql($type);
279 $id_table_objet = id_table_objet($type);
281 // Controle des MD5 envoyes
282 // On elimine les donnees non modifiees par le formulaire (mais
283 // potentiellement modifiees entre temps par un autre utilisateur)
284 foreach ($champs as $key => $val) {
285 if ($m = $ctr[$prefix.$key]) {
287 unset ($champs[$key]);
290 if (!$champs) return array();
292 // On veut savoir si notre modif va avoir un impact
293 // par rapport aux donnees contenues dans la base
294 // (qui peuvent etre differentes de celles ayant servi a calculer le ctr)
295 $s = sql_fetsel(array_keys($champs), $spip_table_objet, "$id_table_objet=$id", $serveur);
297 foreach ($champs as $ch => $val)
298 $intact &= ($s[$ch] == $val);
299 if ($intact) return array();
301 // Detection de conflits :
302 // On verifie si notre modif ne provient pas d'un formulaire
303 // genere a partir de donnees modifiees dans l'intervalle ; ici
304 // on compare a ce qui est dans la base, et on bloque en cas
306 $ctrh = $ctrq = $conflits = array();
307 foreach (array_keys($champs) as $key) {
308 if ($m = $ctr[$prefix.$key]) {
314 $ctrq = sql_fetsel($ctrq, $spip_table_objet, "$id_table_objet=$id", $serveur);
315 foreach ($ctrh as $key => $m) {
316 if ($m != md5($ctrq[$key])
317 AND $champs[$key] !== $ctrq[$key]) {
318 $conflits[$key] = array(
319 'base' => $ctrq[$key],
320 'post' => $champs[$key]
322 unset($champs[$key]); # stocker quand meme les modifs ?
330 // http://doc.spip.org/@display_conflit_champ
331 function display_conflit_champ($x) {
332 if (strstr($x, "\n") OR strlen($x)>80)
333 return "<textarea style='width:99%; height:10em;'>".entites_html($x)."</textarea>\n";
335 return "<input type='text' size='40' style='width:99%' value=\"".entites_html($x)."\" />\n";
338 // http://doc.spip.org/@signaler_conflits_edition
339 function signaler_conflits_edition($conflits, $redirect='') {
340 include_spip('inc/minipres');
341 include_spip('inc/revisions');
342 include_spip('inc/suivi_versions');
343 include_spip('inc/diff');
344 foreach ($conflits as $champ=>$a) {
345 // probleme de stockage ou conflit d'edition ?
346 $base = isset($a['save']) ?
$a['save'] : $a['base'];
348 $diff = new Diff(new DiffTexte
);
349 $n = preparer_diff($a['post']);
350 $o = preparer_diff($base);
352 afficher_para_modifies(afficher_diff($diff->comparer($n,$o))));
354 $titre = isset($a['save']) ?
_L('Echec lors de l\'enregistrement du champ @champ@', array('champ' => $champ)) : $champ;
356 $diffs[] = "<h2>$titre</h2>\n"
357 . "<h3>"._T('info_conflit_edition_differences')."</h3>\n"
358 . "<div style='max-height:8em; overflow: auto; width:99%;'>".$d."</div>\n"
359 . "<h4>"._T('info_conflit_edition_votre_version')."</h4>"
360 . display_conflit_champ($a['post'])
361 . "<h4>"._T('info_conflit_edition_version_enregistree')."</h4>"
362 . display_conflit_champ($base);
366 $id = uniqid(rand());
367 $redirect = "<form action='$redirect' method='get'
369 style='float:".$GLOBALS['spip_lang_right']."; margin-top:2em;'>\n"
370 .form_hidden($redirect)
371 ."<input type='submit' value='"._T('icone_retour')."' />
374 // pour les documents, on est probablement en ajax : il faut ajaxer
376 $redirect .= '<script type="text/javascript">'
377 .'setTimeout(function(){$("#'.$id.'")
378 .ajaxForm({target:$("#'.$id.'").parent()});
385 _T('titre_conflit_edition'),
388 .diff-para-deplace { background: #e8e8ff; }
389 .diff-para-ajoute { background: #d0ffc0; color: #000; }
390 .diff-para-supprime { background: #ffd0c0; color: #904040; text-decoration: line-through; }
391 .diff-deplace { background: #e8e8ff; }
392 .diff-ajoute { background: #d0ffc0; }
393 .diff-supprime { background: #ffd0c0; color: #802020; text-decoration: line-through; }
394 .diff-para-deplace .diff-ajoute { background: #b8ffb8; border: 1px solid #808080; }
395 .diff-para-deplace .diff-supprime { background: #ffb8b8; border: 1px solid #808080; }
396 .diff-para-deplace .diff-deplace { background: #b8b8ff; border: 1px solid #808080; }
398 .'<p>'._T('info_conflit_edition_avis_non_sauvegarde').'</p>'
399 .'<p>'._T('texte_conflit_edition_correction').'</p>'
400 ."<div style='text-align:".$GLOBALS['spip_lang_left'].";'>"