--- /dev/null
+<?php\r
+/*\r
+ * Plugin Porte Plume pour SPIP 2\r
+ * Licence GPL\r
+ * Auteur Matthieu Marcillaud\r
+ */\r
+\r
+if (!defined("_ECRIRE_INC_VERSION")) return;\r
+\r
+include_spip('public/admin'); // pour stripos()\r
+\r
+/* pour compat 2.0 (inutile a partir de 2.1) */\r
+if (version_compare($GLOBALS['spip_version_branche'],'2.1.0 dev','<')) {\r
+ include_spip('inc/layer'); // pour effacer la globale browser_caret\r
+ $GLOBALS['browser_caret']="";\r
+}\r
+\r
+/**\r
+ * La class Barre_outils est un objet contenant les differents\r
+ * parametres definissant une barre markitup\r
+ * \r
+ */\r
+class Barre_outils{\r
+ var $id = "";\r
+ var $nameSpace = "";\r
+ var $lang = "";\r
+ var $previewAutoRefresh = false;\r
+ var $previewParserPath = "";\r
+ var $onEnter = array();\r
+ var $onShiftEnter = array();\r
+ var $onCtrlEnter = array();\r
+ var $onTab = array();\r
+ var $beforeInsert = "";\r
+ var $afterInsert = "";\r
+ var $markupSet = array();\r
+ \r
+ // liste de fonctions supplementaires a mettre apres le json\r
+ var $functions = "";\r
+ \r
+ // private\r
+ var $_liste_params_autorises = array(\r
+ \r
+ 'replaceWith',\r
+ 'openWith',\r
+ 'closeWith',\r
+ 'placeHolder', // remplace par ce texte lorsqu'il n'y a pas de selection\r
+ \r
+ 'beforeInsert', // avant l'insertion\r
+ 'afterInsert', // apres l'insertion\r
+ 'beforeMultiInsert',\r
+ 'afterMultiInsert',\r
+\r
+ 'dropMenu', // appelle un sous menu\r
+ \r
+ 'name', // nom affiche au survol\r
+ 'key', // raccourcis clavier\r
+ 'className', // classe css utilisee\r
+ 'lang', // langues dont le bouton doit apparaitre - array\r
+ 'lang_not', // langues dont le bouton ne doit pas apparaitre - array\r
+ 'selectionType', // '','word','line' : type de selection (normale, aux mots les plus proches, a la ligne la plus proche) \r
+ 'forceMultiline', // pour faire comme si on faisait systematiquement un control+shift (multi ligne)\r
+ \r
+ 'separator',\r
+ \r
+ 'call',\r
+ 'keepDefault',\r
+ \r
+ \r
+ // icon est ajoute pour eventuellement creer un jour\r
+ // la css automatiquement...\r
+ // mais ca pose des problemes la tout de suite\r
+ // car il faudrait generer une css differente des qu'un bouton change\r
+ // ce qui est assez idiot\r
+ // ou alors il faudrait que jquery mette l'icone en fond des boutons automatiquement\r
+ 'icon', \r
+ \r
+ // cacher ou afficher facilement des boutons\r
+ 'display',\r
+ // donner un identifiant unique au bouton (pour le php)\r
+ 'id',\r
+ );\r
+ \r
+ /**\r
+ * Initialise les parametres\r
+ * @param array $params : param->valeur\r
+ */\r
+ function Barre_outils($params=array()){\r
+ foreach ($params as $p=>$v) {\r
+ if (isset($this->$p)) {\r
+ // si tableau, on verifie les entrees\r
+ if (is_array($v)) {\r
+ $v = $this->verif_params($p,$v);\r
+ }\r
+ $this->$p = $v;\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Verifie que les parametres transmis existent\r
+ * et retourne un tableau des parametres valides\r
+ * \r
+ * @param string $nom : cle du parametre (eventuel)\r
+ * @param array $params : parametres du parametre (param->valeur)\r
+ */\r
+ function verif_params($nom, $params=array()) {\r
+ // si markupset, on boucle sur les items\r
+ if (stripos($nom, 'markupSet')!==false) {\r
+ foreach ($params as $i=>$v) {\r
+ $params[$i] = $this->verif_params($i, $v);\r
+ }\r
+ } \r
+ // sinon on teste la validite\r
+ else {\r
+ foreach ($params as $p=>$v) {\r
+ if (!in_array($p, $this->_liste_params_autorises)) {\r
+ unset($params[$p]);\r
+ }\r
+ }\r
+ }\r
+ return $params;\r
+ }\r
+ \r
+ /**\r
+ * Permet d'affecter des parametres a un element de la barre\r
+ * La fonction retourne les parametres, de sorte qu'on peut s'en servir pour simplement recuperer ceux-ci.\r
+ * \r
+ * Il est possible d'affecter des parametres avant/apres l'element trouve\r
+ * en definisant une valeur differente pour le $lieu : 'dedans','avant','apres'\r
+ * par defaut 'dedans' (modifie l'element trouve).\r
+ * \r
+ * Lorsqu'on demande d'inserer avant ou apres, la fonction retourne les parametres inseres\r
+ * \r
+ * @param string $identifiant : identifiant du bouton a afficher\r
+ * @param array $params : parametres a affecter a la trouvaille\r
+ * @param string $lieu : lieu d'affectation des parametres (dedans, avant, apres)\r
+ * @param false/array $tableau : tableau ou chercher les elements (sert pour la recursion)\r
+ */\r
+ function affecter(&$tableau, $identifiant, $params=array(), $lieu='dedans'){\r
+ static $cle_de_recherche = 'id'; // ou className ?\r
+ \r
+ if ($tableau === null)\r
+ $tableau = &$this->markupSet;\r
+ \r
+ if (!in_array($lieu, array('dedans','avant','apres'))) \r
+ $lieu = 'dedans';\r
+ \r
+ // present en premiere ligne ?\r
+ $trouve = false;\r
+ foreach ($tableau as $i=>$v){\r
+ if (isset($v[$cle_de_recherche]) and ($v[$cle_de_recherche] == $identifiant)) {\r
+ $trouve = $i;\r
+ break;\r
+ }\r
+ }\r
+ // si trouve, affectations\r
+ if (($trouve !== false)) {\r
+ if ($params) {\r
+ $params = $this->verif_params($identifiant, $params);\r
+ // dedans on merge\r
+ if ($lieu == 'dedans') {\r
+ return $tableau[$trouve] = array_merge($tableau[$trouve], $params);\r
+ }\r
+ // avant ou apres, on insere\r
+ elseif ($lieu == 'avant') {\r
+ array_splice($tableau, $trouve, 0, array($params));\r
+ return $params;\r
+ }\r
+ elseif ($lieu == 'apres') {\r
+ array_splice($tableau, $trouve+1, 0, array($params));\r
+ return $params;\r
+ }\r
+ }\r
+ return $tableau[$trouve];\r
+ }\r
+ \r
+ // recursivons sinon !\r
+ foreach ($tableau as $i=>$v){\r
+ if (is_array($v)) {\r
+ foreach ($v as $m=>$n) {\r
+ if (is_array($n) AND ($r = $this->affecter($tableau[$i][$m], $identifiant, $params, $lieu))) \r
+ return $r;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ \r
+ /**\r
+ * Permet d'affecter des parametres toutes les elements de la barre\r
+ * \r
+ * @param array $params : parametres a affecter a la trouvaille\r
+ * @param array $ids : tableau identifiants particuliers a qui on affecte les parametres\r
+ * si vide, tous les identifiants seront modifies\r
+ * @param false/array $tableau : tableau ou chercher les elements (sert pour la recursion)\r
+ */\r
+ function affecter_a_tous(&$tableau, $params=array(), $ids=array()){\r
+ if (!$params)\r
+ return false;\r
+ \r
+ if ($tableau === null)\r
+ $tableau = &$this->markupSet;\r
+\r
+ $params = $this->verif_params('divers', $params);\r
+\r
+ // merge de premiere ligne\r
+ foreach ($tableau as $i=>$v){\r
+ if (!$ids OR in_array($v['id'], $ids)) {\r
+ $tableau[$i] = array_merge($tableau[$i], $params);\r
+ }\r
+ // recursion si sous-menu\r
+ if (isset($tableau[$i]['dropMenu'])) {\r
+ $this->affecter_a_tous($tableau[$i]['dropMenu'], $params, $ids);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Affecte les valeurs des parametres indiques au bouton demande\r
+ * et retourne l'ensemble des parametres du bouton (sinon false)\r
+ * \r
+ * @param string/array $identifiant : id du ou des boutons a afficher\r
+ * @param array $params : param->valeur\r
+ * @return mixed\r
+ */\r
+ function set($identifiant, $params=array()) {\r
+ // prudence tout de meme a pas tout modifier involontairement (si array)\r
+ if (!$identifiant) return false;\r
+ \r
+ if (is_string($identifiant)) {\r
+ return $this->affecter($this->markupSet, $identifiant, $params);\r
+ }\r
+ elseif (is_array($identifiant)) {\r
+ return $this->affecter_a_tous($this->markupSet, $params, $identifiant);\r
+ }\r
+ return false; \r
+ }\r
+ \r
+ /**\r
+ * Retourne les parametres du bouton demande\r
+ * \r
+ * @param string $identifiant : nom (de la classe du) bouton\r
+ * @return mixed\r
+ */\r
+ function get($identifiant) {\r
+ if ($a = $this->affecter($this->markupSet, $identifiant)) {\r
+ return $a;\r
+ }\r
+ return false; \r
+ }\r
+ \r
+ \r
+ /**\r
+ * Affiche le bouton demande\r
+ * \r
+ * @param string $identifiant : nom (de la classe du) bouton a afficher\r
+ * @return true/false\r
+ */\r
+ function afficher($identifiant){\r
+ return $this->set($identifiant,array('display'=>true));\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Cache le bouton demande\r
+ * \r
+ * @param string $identifiant : nom (de la classe du) bouton a afficher\r
+ * @return true/false\r
+ */\r
+ function cacher($identifiant){\r
+ return $this->set($identifiant,array('display'=>false));\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Affiche tous les boutons\r
+ * \r
+ * @param string $identifiant : nom (de la classe du) bouton a afficher\r
+ * @return true/false\r
+ */\r
+ function afficherTout(){\r
+ return $this->affecter_a_tous($this->markupSet, array('display'=>true));\r
+ }\r
+ \r
+ /**\r
+ * Cache tous les boutons\r
+ * \r
+ * @param string $identifiant : nom (de la classe du) bouton a afficher\r
+ * @return true/false\r
+ */\r
+ function cacherTout(){\r
+ return $this->affecter_a_tous($this->markupSet, array('display'=>false));\r
+ }\r
+ \r
+\r
+ /**\r
+ * ajouter un bouton ou quelque chose, avant un autre deja present\r
+ * \r
+ * @param string $identifiant : identifiant du bouton ou l'on doit se situer\r
+ * @param array $params : parametres de l'ajout\r
+ */\r
+ function ajouterAvant($identifiant, $params){\r
+ return $this->affecter($this->markupSet, $identifiant, $params, 'avant');\r
+ }\r
+ \r
+ /**\r
+ * ajouter un bouton ou quelque chose, apres un autre deja present\r
+ * \r
+ * @param string $identifiant : identifiant du bouton ou l'on doit se situer\r
+ * @param array $params : parametres de l'ajout\r
+ */\r
+ function ajouterApres($identifiant, $params){\r
+ return $this->affecter($this->markupSet, $identifiant, $params, 'apres');\r
+ \r
+ }\r
+ \r
+ /**\r
+ * ajouter une fonction js pour etre utilises dans les boutons\r
+ * \r
+ * @param string $fonction : code de la fonction js\r
+ * @return null\r
+ */\r
+ function ajouterFonction($fonction){\r
+ if (false === strpos($this->functions, $fonction)){\r
+ $this->functions .= "\n" . $fonction . "\n";\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Supprimer les elements non affiches (display:false)\r
+ * \r
+ * @param false/array $tableau : tableau a analyser (sert pour la recursion)\r
+ */\r
+ function enlever_elements_non_affiches(&$tableau){\r
+ if ($tableau === null)\r
+ $tableau = &$this->markupSet;\r
+ \r
+ foreach ($tableau as $p=>$v){\r
+\r
+ if (isset($v['display']) AND !$v['display']) {\r
+ unset($tableau[$p]);\r
+ $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.\r
+ }\r
+ // sinon, on lance une recursion sur les sous-menus\r
+ else {\r
+ if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {\r
+ $this->enlever_elements_non_affiches($tableau[$p]['dropMenu']);\r
+ // si le sous-menu est vide, on enleve l'icone\r
+ if (!$tableau[$p]['dropMenu']) {\r
+ unset($tableau[$p]);\r
+ $tableau = array_values($tableau);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Supprime les elements vides (uniquement a la racine de l'objet)\r
+ * et uniquement si chaine ou tableau.\r
+ * \r
+ * Supprime les parametres prives\r
+ * Supprime les parametres inutiles a markitup/json dans les parametres markupSet\r
+ * (id, display, icone)\r
+ */\r
+ function enlever_parametres_inutiles() {\r
+ foreach($this as $p=>$v){\r
+ if (!$v) {\r
+ if (is_array($v) or is_string($v)) {\r
+ unset($this->$p);\r
+ }\r
+ } elseif ($p == 'functions') {\r
+ unset($this->$p);\r
+ }\r
+ }\r
+ foreach($this->markupSet as $p=>$v) {\r
+ foreach ($v as $n=>$m) {\r
+ if (in_array($n, array('id', 'display', 'icon'))) {\r
+ unset($this->markupSet[$p][$n]);\r
+ }\r
+ }\r
+ }\r
+ unset ($this->_liste_params_autorises);\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Cree la sortie json pour le javascript des parametres de la barre\r
+ * et la retourne\r
+ * \r
+ * @return string : declaration json de la barre\r
+ */\r
+ function creer_json(){\r
+ $barre = $this;\r
+ $type = $barre->nameSpace;\r
+ $fonctions = $barre->functions;\r
+ \r
+ $barre->enlever_elements_non_affiches($this->markupSet);\r
+ $barre->enlever_parametres_inutiles();\r
+ \r
+ $json = Barre_outils::json_export($barre);\r
+\r
+ // on lance la transformation des &chose; en veritables caracteres\r
+ // sinon markitup restitue « au lieu de « directement\r
+ // lorsqu'on clique sur l'icone\r
+ include_spip('inc/charsets');\r
+ $json = unicode2charset(html2unicode($json));\r
+ return "\n\nbarre_outils_$type = ".$json . "\n\n $fonctions"; \r
+ }\r
+ \r
+ /**\r
+ * Transform a variable into its javascript equivalent (recursive)\r
+ * (depuis ecrire/inc/json, mais modifie pour que les fonctions\r
+ * js ne soient pas mises dans un string\r
+ * \r
+ * @access private\r
+ * @param mixed the variable\r
+ * @return string js script | boolean false if error\r
+ */\r
+ function json_export($var) {\r
+ $asso = false;\r
+ switch (true) {\r
+ case is_null($var) :\r
+ return 'null';\r
+ case is_string($var) :\r
+ if (strtolower(substr(ltrim($var),0,8))=='function')\r
+ return $var;\r
+ return '"' . addcslashes($var, "\"\\\n\r") . '"';\r
+ case is_bool($var) :\r
+ return $var ? 'true' : 'false';\r
+ case is_scalar($var) :\r
+ return $var;\r
+ case is_object( $var) :\r
+ $var = get_object_vars($var);\r
+ $asso = true;\r
+ case is_array($var) :\r
+ $keys = array_keys($var);\r
+ $ikey = count($keys);\r
+ while (!$asso && $ikey--) {\r
+ $asso = $ikey !== $keys[$ikey];\r
+ }\r
+ $sep = '';\r
+ if ($asso) {\r
+ $ret = '{';\r
+ foreach ($var as $key => $elt) {\r
+ $ret .= $sep . '"' . $key . '":' . Barre_outils::json_export($elt);\r
+ $sep = ',';\r
+ }\r
+ return $ret ."}\n";\r
+ } else {\r
+ $ret = '[';\r
+ foreach ($var as $elt) {\r
+ $ret .= $sep . Barre_outils::json_export($elt);\r
+ $sep = ',';\r
+ }\r
+ return $ret ."]\n";\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ \r
+}\r
+\r
+\r
+\r
+/**\r
+ * Cette fonction cree la css pour les images\r
+ * des icones des barres d'outils\r
+ * en s'appuyant sur la description des jeux de barres disponibles.\r
+ * \r
+ * elle cherche une fonction barre_outils_($barre)_icones pour chaque\r
+ * barre et l'appelle si existe.\r
+ * \r
+ * @return string : declaration css des icones\r
+ */\r
+function barre_outils_css_icones(){\r
+ // recuperer la liste, extraire les icones\r
+ $css = "";\r
+ \r
+ // liste des barres\r
+ if (!$barres = barre_outils_liste()) \r
+ return null;\r
+ \r
+ // liste des classes css et leur correspondance avec une icone\r
+ $classe2icone = array();\r
+ foreach ($barres as $barre) {\r
+ include_spip('barre_outils/' . $barre);\r
+ if ($f = charger_fonction($barre . '_icones', 'barre_outils', true)) {\r
+ if (is_array($icones = $f())) {\r
+ $classe2icone = array_merge($classe2icone, $icones);\r
+ }\r
+ }\r
+ }\r
+ \r
+ // passer le tout dans un pipeline pour ceux qui ajoute de simples icones a des barres existantes\r
+ $classe2icone = pipeline('porte_plume_lien_classe_vers_icone',$classe2icone);\r
+ \r
+ // passage en css\r
+ foreach ($classe2icone as $n=>$i) {\r
+ $pos="";\r
+ if (is_array($i)){\r
+ $pos = "background-position:".end($i);\r
+ $i = reset($i);\r
+ }\r
+ $css .= "\n.markItUp .$n a b {background-image:url(".url_absolue(find_in_path("icones_barre/$i")).");$pos}";\r
+ }\r
+\r
+ return $css;\r
+}\r
+\r
+\r
+/**\r
+ * Retourne une instance de Barre_outils\r
+ * cree a partir du type de barre demande\r
+ * \r
+ * @param string $set : type de barre\r
+ * @return object/false : objet de type barre_outil\r
+ */\r
+function barre_outils_initialiser($set){\r
+ if ($f = charger_fonction($set, 'barre_outils')) {\r
+ // retourne une instance de l'objet Barre_outils\r
+ return $f();\r
+ }\r
+ return false;\r
+}\r
+\r
+/**\r
+ * Retourne la liste des barres d'outils connues\r
+ *\r
+ * @return array/false : tableau des noms de barres trouvees\r
+ */\r
+function barre_outils_liste(){\r
+ static $sets = -1;\r
+ if ($sets !== -1) \r
+ return $sets;\r
+ \r
+ // on recupere l'ensemble des barres d'outils connues\r
+ if (!$sets = find_all_in_path('barre_outils/','.*[.]php')\r
+ or !is_array($sets)) {\r
+ spip_log("[Scandale] Porte Plume ne trouve pas de barre d'outils !");\r
+ $sets = false;\r
+ return $sets;\r
+ }\r
+ \r
+ foreach($sets as $fichier=>$adresse) {\r
+ $sets[$fichier] = substr($fichier,0,-4); // juste le nom\r
+ }\r
+ return $sets; \r
+}\r
+\r
+/**\r
+ * filtre appliquant les traitements SPIP d'un champ (et eventuellement d'un type d'objet) sur un texte\r
+ * (voir la fonction champs_traitements($p) dans : public/references.php)\r
+ * ce mecanisme est a preferer au traditionnel #TEXTE*|propre\r
+ * traitements_previsu() consulte la globale $table_des_traitements et applique le traitement adequat\r
+ * si aucun traitement n'est trouve, alors propre() est applique\r
+ * \r
+ * @param string $texte : texte source\r
+ * @param string $nom_champ : champ (en majuscules)\r
+ * @param string $type_objet : objet (en minuscules)\r
+ * @return string : texte traite\r
+ */\r
+function traitements_previsu($texte, $nom_champ='', $type_objet='', $connect=null) {\r
+ include_spip('public/interfaces'); // charger les traitements\r
+ safehtml($t);\r
+ global $table_des_traitements;\r
+ if(!strlen($nom_champ) || !isset($table_des_traitements[$nom_champ])) {\r
+ $texte = propre($texte, $connect);\r
+ }\r
+ else {\r
+ include_spip('base/abstract_sql');\r
+ $table = table_objet($type_objet);\r
+ $ps = $table_des_traitements[$nom_champ];\r
+ if(is_array($ps))\r
+ $ps = $ps[(strlen($table) && isset($ps[$table])) ? $table : 0];\r
+ if(!$ps)\r
+ $texte = propre($texte, $connect);\r
+ else\r
+ // remplacer le placeholder %s par le texte fourni\r
+ eval('$texte=' . str_replace('%s', '$texte', $ps) . ';');\r
+ }\r
+ // il faut toujours securiser le texte preivusalise car il peut contenir n'importe quoi\r
+ // et servir de support a une attaque xss ou vol de cookie admin\r
+ // on ne peut donc se fier au statut de l'auteur connecte car le contenu ne vient pas\r
+ // forcement de lui\r
+ return safehtml($texte);\r
+}\r
+?>\r