--- /dev/null
+<?php
+/**
+ * Crayons
+ * plugin for spip
+ * (c) Fil, toggg 2006-2013
+ * licence GPL
+ */
+
+if (!defined("_ECRIRE_INC_VERSION")) return;
+
+function verif_secu($w, $secu) {
+ return (
+ $secu == md5($GLOBALS['meta']['alea_ephemere'].'='.$w)
+ OR
+ $secu == md5($GLOBALS['meta']['alea_ephemere_ancien'].'='.$w)
+ );
+}
+
+function post_crayons() {
+ $results = array();
+
+ if (isset($_POST['crayons']) AND is_array($_POST['crayons']))
+ foreach ($_POST['crayons'] as $crayon) {
+ $name = $_POST['name_'.$crayon];
+ $content = array();
+ if ($_POST['fields_'.$crayon]) {
+ foreach (explode(',', $_POST['fields_'.$crayon]) as $field) {
+ // cas particulier d'un envoi de fichier
+ if (isset($_FILES['content_'.$crayon.'_'.$field])) {
+ if ($_FILES['content_'.$crayon.'_'.$field]['size']>0)
+ $content[$field] = $_FILES['content_'.$crayon.'_'.$field];
+ else
+ $content[$field] = false;
+ // cf. valeur passee dans crayon->md5() : false ou filemtime() du logo
+ } else {
+ /**
+ * le changement de charset n'est plus necessaire
+ * depuis jquery 1.5 (feature non documentee de jquery!)
+ */
+ if (isset($_POST['content_'.$crayon.'_'.$field])) {
+ $content[$field] = is_array($_POST['content_'.$crayon.'_'.$field])
+ ?implode(',',$_POST['content_'.$crayon.'_'.$field])
+ :$_POST['content_'.$crayon.'_'.$field];
+ } else {
+ $content[$field] = null;
+ }
+ }
+ }
+ }
+
+ // Si les donnees POSTees ne correspondent pas a leur md5,
+ // il faut les traiter
+ if (isset($name)
+ AND md5(serialize($content)) != $_POST['md5_'.$crayon]) {
+ if (!isset($_POST['secu_'.$crayon])
+ OR verif_secu($name, $_POST['secu_'.$crayon])) {
+ $results[] = array($name, $content, $_POST['md5_'.$crayon], $crayon);
+ }
+ else
+ return false; // erreur secu
+ }
+ // cas inchange
+ else
+ $results[] = array($name, $content, false, $crayon);
+ }
+
+ return $results;
+}
+
+
+function crayons_store($options = array()) {
+ // permettre de surcharger les fonctions de recuperation des valeurs
+ // et de sauvegardes de celles-ci
+ $options = array_merge(array(
+ 'f_get_valeur' => 'crayons_store_get_valeur',
+ 'f_set_modifs' => 'crayons_store_set_modifs',
+ ), $options);
+
+ include_spip('inc/crayons');
+ $wdgcfg = wdgcfg();
+
+ $return = array('$erreur'=>'');
+
+ $postees = post_crayons();
+
+ $modifs = $updates = array();
+ if (!is_array($postees)) {
+ $return['$erreur'] = _U('crayons:donnees_mal_formatees');
+ } else {
+ foreach ($postees as $postee)
+ if ($postee[2] !== false) {
+ $name = $postee[0];
+ $content = $postee[1];
+
+ if ($content && preg_match(_PREG_CRAYON, 'crayon '.$name, $regs)) {
+ list(,$crayon,$type,$modele,$id) = $regs;
+ $wid = $postee[3];
+
+ spip_log("autoriser('crayonner', $type, $id, NULL, array('modele'=>$modele)","crayons_distant");
+ if (!autoriser('crayonner', $type, $id, NULL, array('modele'=>$modele))) {
+ $return['$erreur'] =
+ "$type $id: " . _U('crayons:non_autorise');
+ } else {
+
+ // recuperer l'existant pour calculer son md5 et verifier
+ // qu'il n'a pas ete modifie entre-temps
+ $get_valeur = $options['f_get_valeur'];
+ $data = $get_valeur($content, $regs);
+
+ $md5 = md5(serialize($data));
+
+ // est-ce que le champ a ete modifie dans la base entre-temps ?
+ if ($md5 != $postee[2]) {
+ // si oui, la modif demandee correspond peut-etre
+ // a la nouvelle valeur ? dans ce cas on procede
+ // comme si "pas de modification", sinon erreur
+ if ($md5 != md5(serialize($content))) {
+ $return['$erreur'] = "$type $id $modele: " .
+ _U('crayons:modifie_par_ailleurs');
+ }
+ }
+
+ $modifs[] = array($type, $modele, $id, $content, $wid);
+
+ /* aiguillage pour verification de la saisie
+ Pour traitement ulterieur les fonctions de verifications doivent renvoyer $invalides :
+ $invalides[wid_champ]['msg'] -> message de saisie invalide
+ $invalides[wid_champ]['retour'] -> caracteres invalides */
+ $f = 'verifier_'.$type.'_'.$modele;
+ if (function_exists($f)) {
+ if (count( $invalides = $f($modifs) )) {
+ $return['$invalides'] = $invalides;
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ if (!$modifs AND !$return['$erreur']) {
+ $return['$erreur'] = $wdgcfg['msgNoChange'] ?
+ _U('crayons:pas_de_modification') : ' ';
+ $return['$annuler'] = true;
+ }
+
+ // un champ invalide ... ou rien ==> on ne fait rien !
+ if (isset($return['$invalides']) && $return['$invalides'])
+ return $return;
+
+ // une quelconque erreur ... ou rien ==> on ne fait rien !
+ if (isset($return['$erreur']) && $return['$erreur'])
+ return $return;
+
+ // on traite toutes les modifications
+ // en appelant la fonction adequate de traitement
+ $set_modifs = $options['f_set_modifs'];
+ $return = $set_modifs($modifs, $return);
+
+ // une quelconque erreur ... ou rien ==> on ne fait rien !
+ if ($return['$erreur'])
+ return $return;
+
+ // et maintenant refaire l'affichage des crayons modifies
+ include_spip('inc/texte');
+ foreach ($modifs as $m) {
+ list($type, $modele, $id, $content, $wid) = $m;
+ $f = charger_fonction($type.'_'.$modele, 'vues', true)
+ OR $f = charger_fonction($modele, 'vues', true)
+ OR $f = charger_fonction($type, 'vues', true)
+ OR $f = 'vues_dist';
+ $return[$wid] = $f($type, $modele, $id, $content, $wid);
+ }
+ return $return;
+}
+
+// recuperer une valeur en fonction des parametres recuperes
+// cette fonction cherche une valeur d'une colonne d'une table SQL
+function crayons_store_get_valeur($content, $regs) {
+ list(,$crayon,$type,$modele,$id) = $regs;
+ return valeur_colonne_table($type, array_keys($content), $id);
+}
+
+// stocke les valeurs envoyees dans des colonnes de table SQL
+function crayons_store_set_modifs($modifs, $return) {
+ // sinon on bosse : toutes les modifs ont ete acceptees
+ // verifier qu'on a tout ce qu'il faut pour mettre a jour la base
+ // et regrouper les mises a jour par type/id
+ foreach ($modifs as $modif) {
+ list($type, $modele, $id, $content, $wid) = $modif;
+
+ $fun = '';
+ // si le crayon est un MODELE avec une fonction xxx_revision associee
+ // cas ou une fonction xxx_revision existe
+ if (function_exists($f = $type.'_'. $modele . "_revision")
+ OR function_exists($f = $modele . "_revision")
+ OR function_exists($f = $type . "_revision"))
+ $fun = $f;
+
+ // si on est en SPIP 3+ et qu'on edite un objet editorial bien declare
+ // passer par l'API objet_modifier
+ elseif (function_exists('lister_tables_objets_sql')
+ AND $tables_objet = lister_tables_objets_sql()
+ AND isset($tables_objet[table_objet_sql($type)])) {
+ $fun = 'crayons_objet_modifier';
+ }
+
+ // sinon spip < 3 (ou pas un objet edito)
+ // on teste les objets connus et on route sur les fonctions correspondantes
+ else switch($type) {
+ case 'article':
+ $fun = 'crayons_update_article';
+ break;
+ case 'breve':
+ include_spip('action/editer_breve');
+ $fun = 'revisions_breves';
+ break;
+ case 'forum':
+ include_spip('inc/forum');
+ $fun = 'enregistre_et_modifie_forum';
+ break;
+ case 'rubrique':
+ include_spip('action/editer_rubrique');
+ $fun = 'revisions_rubriques';
+ break;
+ case 'syndic':
+ case 'site':
+ include_spip('action/editer_site');
+ $fun = 'revisions_sites';
+ break;
+ case 'document':
+ include_spip('plugins/installer');
+ include_spip('inc/plugin');
+ if (spip_version_compare($GLOBALS['spip_version_branche'], '3.0.0alpha', '>=')) {
+ include_spip('action/editer_document');
+ $fun = 'document_modifier';
+ } else {
+ include_spip('inc/modifier');
+ $fun = 'revision_document';
+ }
+ break;
+ // cas geres de la maniere la plus standard
+ case 'auteur':
+ case 'mot':
+ case 'signature':
+ case 'petition':
+ default:
+ include_spip('inc/modifier');
+ $fun = 'revision_'.$type;
+ break;
+ }
+
+ // si on a pas reussi on passe par crayons_update() qui fera un update sql brutal
+ if (!$fun or !function_exists($fun)) {
+ $fun = 'crayons_update';
+ // $return['$erreur'] = "$type: " . _U('crayons:non_implemente');
+ // break;
+ }
+
+ if (!isset($updates[$type][$fun])) {
+ $updates[$type][$fun] = array();
+ }
+ if (!isset($updates[$type][$fun][$id])) {
+ $updates[$type][$fun][$id] = array('wdg'=>array(), 'chval'=>array());
+ }
+ // pour reaffecter le retour d'erreur sql au cas ou
+ $updates[$type][$fun][$id]['wdg'][] = $wid;
+ foreach ($content as $champtable => $val) {
+ $updates[$type][$fun][$id]['chval'][$champtable] = $val;
+ }
+ }
+
+ // il manque une fonction de mise a jour ==> on ne fait rien !
+ if ($return['$erreur'])
+ return $return;
+
+ // hop ! mises a jour table par table et id par id
+ foreach ($updates as $type => $idschamps)
+ foreach ($idschamps as $fun => $ids) {
+ foreach ($ids as $id => $champsvaleurs) {
+ /* cas particulier du logo dans un crayon complexe :
+ ce n'est pas un champ de la table */
+ if (isset($champsvaleurs['chval']['logo'])) {
+ spip_log('revision logo', 'crayons');
+ logo_revision($id, $champsvaleurs['chval'], $type, $champsvaleurs['wdg']);
+ unset($champsvaleurs['chval']['logo']);
+ }
+ if (count($champsvaleurs['chval'])) {
+ // -- revisions_articles($id_article, $c) --
+ spip_log("$fun($id ...)", 'crayons');
+ $updok = $fun($id, $champsvaleurs['chval'], $type, $champsvaleurs['wdg']);
+ // Renvoyer erreur si update base distante echoue, on ne regarde pas les updates base local car ils ne renvoient rien
+ list($distant,$table) = distant_table($type);
+ if ($distant AND !$updok)
+ $return['$erreur'] = "$type: " . _U('crayons:update_impossible');
+ }
+ }
+ }
+
+ return $return;
+}
+
+//
+// VUE
+//
+function vues_dist($type, $modele, $id, $content, $wid){
+ // pour ce qui a une {lang_select} par defaut dans la boucle,
+ // la regler histoire d'avoir la bonne typo dans le propre()
+ // NB: ceci n'a d'impact que sur le "par defaut" en bas
+ list($distant,$table) = distant_table($type);
+ if (colonne_table($type, 'lang')) {
+ $b = valeur_colonne_table($type, 'lang', $id);
+ lang_select($a = array_pop($b));
+ } else {
+ lang_select($a = $GLOBALS['meta']['langue_site']);
+ }
+
+ // chercher vues/article_toto.html
+ // sinon vues/toto.html
+ if (find_in_path( ($fond = 'vues/' . $type . '_' . $modele) . '.html')
+ OR find_in_path( ($fond = 'vues/' . $modele) .'.html')
+ OR find_in_path( ($fond = 'vues/' . $type) .'.html')) {
+ $contexte = array(
+ 'id_' . $table => $id,
+ 'crayon_type' => $type,
+ 'crayon_modele' => $modele,
+ 'champ' => $modele,
+ 'class' => _request('class_'.$wid),
+ 'self' => _request('self'),
+ 'lang' => $GLOBALS['spip_lang']
+ );
+ $contexte = array_merge($contexte, $content);
+ include_spip('public/assembler');
+ return recuperer_fond($fond, $contexte);
+ }
+ // vue par defaut
+ else {
+ // Par precaution on va rechercher la valeur
+ // dans la base de donnees (meme si a priori la valeur est
+ // ce qu'on vient d'envoyer, il y a nettoyage des caracteres et
+ // eventuellement d'autres filtres de saisie...)
+ $bdd = valeur_colonne_table($type, $modele, $id);
+ if (count($bdd)) {
+ $valeur = array_pop($bdd);
+ } else {
+ // les champs n'ont pas ete retrouves dans la base
+ // ce qui signifie a priori que nous sommes en face d'une cle primaire compose
+ // et qu'un crayon a modifie un element de cette cle (c'est pas malin !)
+ // dans ce cas, on reaffiche a minima ce qu'on vient de publier
+ // mais il sera impossible de le reediter dans la foulee avec le meme crayon
+ // (car l'identifiant du crayon se base sur l'id).
+ // Il faudra donc recharger la page pour pouvoir reediter.
+ if (is_scalar($id)) {
+ $valeur = $content[$modele];
+ }
+ }
+
+ // seul spip core sait rendre les donnees
+ if (in_array($modele,
+ array('chapo', 'texte', 'descriptif', 'ps', 'bio'))) {
+ return propre($valeur);
+ } else {
+ return typo($valeur);
+ }
+ }
+}
+
+
+/**
+ * Fonction de mise a jour par API editer_objet
+ * @param $id
+ * @param $data
+ * @param $type
+ * @param $ref
+ * @return bool|mixed|string
+ */
+function crayons_objet_modifier($id, $data, $type, $ref) {
+ if (include_spip('action/editer_objet')
+ AND function_exists('objet_modifier')) {
+ return objet_modifier(objet_type($type),$id,$data);
+ }
+ // fallback
+ return crayons_update($id, $data, $type);
+}
+
+//
+// Fonctions de mise a jour generique
+//
+function crayons_update($id, $colval = array(), $type = ''){
+ if (!$colval OR !count($colval))
+ return false;
+ list($distant,$table) = distant_table($type);
+
+ if ($distant) {
+ list($nom_table, $where) = table_where($type, $id);
+ if (!$nom_table)
+ return false;
+
+ $update = $sep = '';
+ foreach ($colval as $col => $val) {
+ $update .= $sep . '`' . $col . '`=' . _q($val);
+ $sep = ', ';
+ }
+
+ $a = spip_query($q =
+ 'UPDATE `' . $nom_table . '` SET ' . $update . ' WHERE ' . $where , $distant );
+
+ #spip_log($q);
+ include_spip('inc/invalideur');
+ suivre_invalideur($cond, $modif=true);
+ }
+ else {
+ // cle primaire composee : 3-4-rubrique
+ // calculer un where approprie
+ // et modifier sans passer par la fonction destinee aux tables principales
+ // on limite a SPIP 2 mini car sql_updateq n'est pas mappe dans les crayons_compat
+ if (is_scalar($id) and ($GLOBALS['spip_version_code'] >= '1.93')) {
+ list($nom_table, $where) = table_where($type, $id, true); // where sous forme de tableau
+ $a = sql_updateq($nom_table, $colval, $where);
+ } else {
+ // modification d'une table principale
+ include_spip('inc/modifier');
+ $a = modifier_contenu($type, $id, array(), $colval);
+ }
+ }
+
+ return $a;
+}
+
+//
+// Fonctions de mise a jour
+//
+function crayons_update_article($id_article, $c = false) {
+ include_spip('action/editer_article');
+
+ // Enregistrer les nouveaux contenus
+ revisions_articles($id_article, $c);
+
+ // En cas de statut ou de id_rubrique
+ // NB: instituer_article veut id_parent, et pas id_rubrique !
+ if (isset($c['id_rubrique'])) {
+ $c['id_parent'] = $c['id_rubrique'];
+ unset ($c['id_rubrique']);
+ }
+ instituer_article($id_article, $c);
+}
+
+/**
+ * Enregistre les modifications sur une configuration
+ * suite à un crayon sur une meta
+ *
+ * La colonne est toujours 'valeur' pour ces données.
+ * La donnée à enregistrer peut-être une sous partie de configuration.
+ * Si c'est le cas, on gère l'enregistrement via ecrire_config.
+ *
+ * @param string $a
+ * Nom ou clé de la meta (descriptif_site ou demo__truc pour demo/truc)
+ * @param bool|array $c
+ * Liste des champs modifiés
+ * Ici, 'valeur' normalement.
+ * @return void
+**/
+function revision_meta($a, $c = false) {
+ if (isset($c['valeur'])) {
+ // Certaines cles de configuration sont echapées ici (cf #EDIT_CONFIG{demo/truc})
+ $a = str_replace('__', '/', $a);
+ spip_log("meta '$a' = '$c[valeur]'", 'crayons');
+ // eviter de planter les vieux SPIP
+ if (false === strpos($a, '/')) {
+ ecrire_meta($a, $c['valeur']);
+ // SPIP 3 ou Bonux 2 ou CFG
+ } else {
+ include_spip('inc/config');
+ ecrire_config($a, $c['valeur']);
+ }
+ include_spip('inc/invalideur');
+ suivre_invalideur('meta');
+ }
+}
+
+
+// TODO:
+// Ce modele est cense enregistrer les tags sous forme de ??
+// une ligne dans un champ spip_articles.tags, et/ou des mots-cles...
+function modeles_tags($id, $c) {
+ var_dump($id); #id_article
+ var_dump($c); # perturbant : ici on a array('id_article'=>'valeur envoyee')
+}
+
+function action_crayons_store_dist() {
+ return action_crayons_store_args();
+}
+
+// permettre de passer une autre fonction de stockage des informations
+function action_crayons_store_args($store = 'crayons_store') {
+ header("Content-Type: text/plain; charset=".$GLOBALS['meta']['charset']);
+ lang_select($GLOBALS['auteur_session']['lang']);
+
+ $r = $store();
+
+ // Si on a ete appeles par jQuery, on renvoie tout, c'est le client
+ // crayons.js qui va traiter l'affichage du resultat et status
+ # Attention le test $_SERVER["HTTP_X_REQUESTED_WITH"] === "XMLHttpRequest"
+ # n'est pas bon car le cas d'un fichier uploade via iframe n'est pas detecte
+
+ // S'il y a une adresse de redirection, on renvoie vers elle
+ // En cas d'erreur il faudrait ajouter &err=... dans l'url ?
+ if (_request('redirect')) {
+ if (!$r['$erreur']
+ OR $r['$annuler']) {
+ include_spip('inc/headers');
+ redirige_par_entete(_request('redirect'));
+ } else {
+ echo "<h4 class='status'>".$r['$erreur']."</h4>\n";
+
+ foreach ($r as $wid => $v) {
+ if ($wid !== '$erreur')
+ echo "<div id='$wid'>$v</div><hr />\n";
+ }
+ echo "<a href='".quote_amp(_request('redirect'))."'>"
+ .quote_amp(_request('redirect'))
+ ."</a>\n";
+ }
+ }
+
+ // Cas normal : JSON
+ else {
+ echo crayons_json_export($r);
+ }
+
+ exit;
+}
+
+?>