3 * Plugin Porte Plume pour SPIP 2
5 * Auteur Matthieu Marcillaud
8 if (!defined("_ECRIRE_INC_VERSION")) return;
10 include_spip('public/admin'); // pour stripos()
12 /* pour compat 2.0 (inutile a partir de 2.1) */
13 if (version_compare($GLOBALS['spip_version_branche'],'2.1.0 dev','<')) {
14 include_spip('inc/layer'); // pour effacer la globale browser_caret
15 $GLOBALS['browser_caret']="";
19 * La class Barre_outils est un objet contenant les differents
20 * parametres definissant une barre markitup
27 var $previewAutoRefresh = false;
28 var $previewParserPath = "";
29 var $onEnter = array();
30 var $onShiftEnter = array();
31 var $onCtrlEnter = array();
33 var $beforeInsert = "";
34 var $afterInsert = "";
35 var $markupSet = array();
37 // liste de fonctions supplementaires a mettre apres le json
41 var $_liste_params_autorises = array(
46 'placeHolder', // remplace par ce texte lorsqu'il n'y a pas de selection
48 'beforeInsert', // avant l'insertion
49 'afterInsert', // apres l'insertion
53 'dropMenu', // appelle un sous menu
55 'name', // nom affiche au survol
56 'key', // raccourcis clavier
57 'className', // classe css utilisee
58 'lang', // langues dont le bouton doit apparaitre - array
59 'lang_not', // langues dont le bouton ne doit pas apparaitre - array
60 'selectionType', // '','word','line' : type de selection (normale, aux mots les plus proches, a la ligne la plus proche)
61 'forceMultiline', // pour faire comme si on faisait systematiquement un control+shift (multi ligne)
69 // icon est ajoute pour eventuellement creer un jour
70 // la css automatiquement...
71 // mais ca pose des problemes la tout de suite
72 // car il faudrait generer une css differente des qu'un bouton change
73 // ce qui est assez idiot
74 // ou alors il faudrait que jquery mette l'icone en fond des boutons automatiquement
77 // cacher ou afficher facilement des boutons
79 // donner un identifiant unique au bouton (pour le php)
84 * Initialise les parametres
85 * @param array $params : param->valeur
87 function Barre_outils($params=array()){
88 foreach ($params as $p=>$v) {
89 if (isset($this->$p)) {
90 // si tableau, on verifie les entrees
92 $v = $this->verif_params($p,$v);
100 * Verifie que les parametres transmis existent
101 * et retourne un tableau des parametres valides
103 * @param string $nom : cle du parametre (eventuel)
104 * @param array $params : parametres du parametre (param->valeur)
106 function verif_params($nom, $params=array()) {
107 // si markupset, on boucle sur les items
108 if (stripos($nom, 'markupSet')!==false) {
109 foreach ($params as $i=>$v) {
110 $params[$i] = $this->verif_params($i, $v);
113 // sinon on teste la validite
115 foreach ($params as $p=>$v) {
116 if (!in_array($p, $this->_liste_params_autorises
)) {
125 * Permet d'affecter des parametres a un element de la barre
126 * La fonction retourne les parametres, de sorte qu'on peut s'en servir pour simplement recuperer ceux-ci.
128 * Il est possible d'affecter des parametres avant/apres l'element trouve
129 * en definisant une valeur differente pour le $lieu : 'dedans','avant','apres'
130 * par defaut 'dedans' (modifie l'element trouve).
132 * Lorsqu'on demande d'inserer avant ou apres, la fonction retourne les parametres inseres
134 * @param string $identifiant : identifiant du bouton a afficher
135 * @param array $params : parametres a affecter a la trouvaille
136 * @param string $lieu : lieu d'affectation des parametres (dedans, avant, apres)
137 * @param false/array $tableau : tableau ou chercher les elements (sert pour la recursion)
139 function affecter(&$tableau, $identifiant, $params=array(), $lieu='dedans'){
140 static $cle_de_recherche = 'id'; // ou className ?
142 if ($tableau === null)
143 $tableau = &$this->markupSet
;
145 if (!in_array($lieu, array('dedans','avant','apres')))
148 // present en premiere ligne ?
150 foreach ($tableau as $i=>$v){
151 if (isset($v[$cle_de_recherche]) and ($v[$cle_de_recherche] == $identifiant)) {
156 // si trouve, affectations
157 if (($trouve !== false)) {
159 $params = $this->verif_params($identifiant, $params);
161 if ($lieu == 'dedans') {
162 return $tableau[$trouve] = array_merge($tableau[$trouve], $params);
164 // avant ou apres, on insere
165 elseif ($lieu == 'avant') {
166 array_splice($tableau, $trouve, 0, array($params));
169 elseif ($lieu == 'apres') {
170 array_splice($tableau, $trouve+
1, 0, array($params));
174 return $tableau[$trouve];
177 // recursivons sinon !
178 foreach ($tableau as $i=>$v){
180 foreach ($v as $m=>$n) {
181 if (is_array($n) AND ($r = $this->affecter($tableau[$i][$m], $identifiant, $params, $lieu)))
191 * Permet d'affecter des parametres toutes les elements de la barre
193 * @param array $params : parametres a affecter a la trouvaille
194 * @param array $ids : tableau identifiants particuliers a qui on affecte les parametres
195 * si vide, tous les identifiants seront modifies
196 * @param false/array $tableau : tableau ou chercher les elements (sert pour la recursion)
198 function affecter_a_tous(&$tableau, $params=array(), $ids=array()){
202 if ($tableau === null)
203 $tableau = &$this->markupSet
;
205 $params = $this->verif_params('divers', $params);
207 // merge de premiere ligne
208 foreach ($tableau as $i=>$v){
209 if (!$ids OR in_array($v['id'], $ids)) {
210 $tableau[$i] = array_merge($tableau[$i], $params);
212 // recursion si sous-menu
213 if (isset($tableau[$i]['dropMenu'])) {
214 $this->affecter_a_tous($tableau[$i]['dropMenu'], $params, $ids);
222 * Affecte les valeurs des parametres indiques au bouton demande
223 * et retourne l'ensemble des parametres du bouton (sinon false)
225 * @param string/array $identifiant : id du ou des boutons a afficher
226 * @param array $params : param->valeur
229 function set($identifiant, $params=array()) {
230 // prudence tout de meme a pas tout modifier involontairement (si array)
231 if (!$identifiant) return false;
233 if (is_string($identifiant)) {
234 return $this->affecter($this->markupSet
, $identifiant, $params);
236 elseif (is_array($identifiant)) {
237 return $this->affecter_a_tous($this->markupSet
, $params, $identifiant);
243 * Retourne les parametres du bouton demande
245 * @param string $identifiant : nom (de la classe du) bouton
248 function get($identifiant) {
249 if ($a = $this->affecter($this->markupSet
, $identifiant)) {
257 * Affiche le bouton demande
259 * @param string $identifiant : nom (de la classe du) bouton a afficher
262 function afficher($identifiant){
263 return $this->set($identifiant,array('display'=>true));
268 * Cache le bouton demande
270 * @param string $identifiant : nom (de la classe du) bouton a afficher
273 function cacher($identifiant){
274 return $this->set($identifiant,array('display'=>false));
279 * Affiche tous les boutons
281 * @param string $identifiant : nom (de la classe du) bouton a afficher
284 function afficherTout(){
285 return $this->affecter_a_tous($this->markupSet
, array('display'=>true));
289 * Cache tous les boutons
291 * @param string $identifiant : nom (de la classe du) bouton a afficher
294 function cacherTout(){
295 return $this->affecter_a_tous($this->markupSet
, array('display'=>false));
300 * ajouter un bouton ou quelque chose, avant un autre deja present
302 * @param string $identifiant : identifiant du bouton ou l'on doit se situer
303 * @param array $params : parametres de l'ajout
305 function ajouterAvant($identifiant, $params){
306 return $this->affecter($this->markupSet
, $identifiant, $params, 'avant');
310 * ajouter un bouton ou quelque chose, apres un autre deja present
312 * @param string $identifiant : identifiant du bouton ou l'on doit se situer
313 * @param array $params : parametres de l'ajout
315 function ajouterApres($identifiant, $params){
316 return $this->affecter($this->markupSet
, $identifiant, $params, 'apres');
321 * ajouter une fonction js pour etre utilises dans les boutons
323 * @param string $fonction : code de la fonction js
326 function ajouterFonction($fonction){
327 if (false === strpos($this->functions
, $fonction)){
328 $this->functions
.= "\n" . $fonction . "\n";
333 * Supprimer les elements non affiches (display:false)
335 * @param false/array $tableau : tableau a analyser (sert pour la recursion)
337 function enlever_elements_non_affiches(&$tableau){
338 if ($tableau === null)
339 $tableau = &$this->markupSet
;
341 foreach ($tableau as $p=>$v){
343 if (isset($v['display']) AND !$v['display']) {
345 $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.
347 // sinon, on lance une recursion sur les sous-menus
349 if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {
350 $this->enlever_elements_non_affiches($tableau[$p]['dropMenu']);
351 // si le sous-menu est vide, on enleve l'icone
352 if (!$tableau[$p]['dropMenu']) {
354 $tableau = array_values($tableau);
362 * Supprime les elements vides (uniquement a la racine de l'objet)
363 * et uniquement si chaine ou tableau.
365 * Supprime les parametres prives
366 * Supprime les parametres inutiles a markitup/json dans les parametres markupSet
367 * (id, display, icone)
369 function enlever_parametres_inutiles() {
370 foreach($this as $p=>$v){
372 if (is_array($v) or is_string($v)) {
375 } elseif ($p == 'functions') {
379 foreach($this->markupSet
as $p=>$v) {
380 foreach ($v as $n=>$m) {
381 if (in_array($n, array('id', 'display', 'icon'))) {
382 unset($this->markupSet
[$p][$n]);
386 unset ($this->_liste_params_autorises
);
391 * Cree la sortie json pour le javascript des parametres de la barre
394 * @return string : declaration json de la barre
396 function creer_json(){
398 $type = $barre->nameSpace;
399 $fonctions = $barre->functions
;
401 $barre->enlever_elements_non_affiches($this->markupSet
);
402 $barre->enlever_parametres_inutiles();
404 $json = Barre_outils
::json_export($barre);
406 // on lance la transformation des &chose; en veritables caracteres
407 // sinon markitup restitue « au lieu de « directement
408 // lorsqu'on clique sur l'icone
409 include_spip('inc/charsets');
410 $json = unicode2charset(html2unicode($json));
411 return "\n\nbarre_outils_$type = ".$json . "\n\n $fonctions";
415 * Transform a variable into its javascript equivalent (recursive)
416 * (depuis ecrire/inc/json, mais modifie pour que les fonctions
417 * js ne soient pas mises dans un string
420 * @param mixed the variable
421 * @return string js script | boolean false if error
423 function json_export($var) {
428 case is_string($var) :
429 if (strtolower(substr(ltrim($var),0,8))=='function')
431 return '"' . addcslashes($var, "\"\\\n\r") . '"';
433 return $var ?
'true' : 'false';
434 case is_scalar($var) :
436 case is_object( $var) :
437 $var = get_object_vars($var);
439 case is_array($var) :
440 $keys = array_keys($var);
441 $ikey = count($keys);
442 while (!$asso && $ikey--) {
443 $asso = $ikey !== $keys[$ikey];
448 foreach ($var as $key => $elt) {
449 $ret .= $sep . '"' . $key . '":' . Barre_outils
::json_export($elt);
455 foreach ($var as $elt) {
456 $ret .= $sep . Barre_outils
::json_export($elt);
470 * Cette fonction cree la css pour les images
471 * des icones des barres d'outils
472 * en s'appuyant sur la description des jeux de barres disponibles.
474 * elle cherche une fonction barre_outils_($barre)_icones pour chaque
475 * barre et l'appelle si existe.
477 * @return string : declaration css des icones
479 function barre_outils_css_icones(){
480 // recuperer la liste, extraire les icones
484 if (!$barres = barre_outils_liste())
487 // liste des classes css et leur correspondance avec une icone
488 $classe2icone = array();
489 foreach ($barres as $barre) {
490 include_spip('barre_outils/' . $barre);
491 if ($f = charger_fonction($barre . '_icones', 'barre_outils', true)) {
492 if (is_array($icones = $f())) {
493 $classe2icone = array_merge($classe2icone, $icones);
498 // passer le tout dans un pipeline pour ceux qui ajoute de simples icones a des barres existantes
499 $classe2icone = pipeline('porte_plume_lien_classe_vers_icone',$classe2icone);
502 foreach ($classe2icone as $n=>$i) {
505 $pos = "background-position:".end($i);
508 $css .= "\n.markItUp .$n a b {background-image:url(".url_absolue(find_in_path("icones_barre/$i")).");$pos}";
516 * Retourne une instance de Barre_outils
517 * cree a partir du type de barre demande
519 * @param string $set : type de barre
520 * @return object/false : objet de type barre_outil
522 function barre_outils_initialiser($set){
523 if ($f = charger_fonction($set, 'barre_outils')) {
524 // retourne une instance de l'objet Barre_outils
531 * Retourne la liste des barres d'outils connues
533 * @return array/false : tableau des noms de barres trouvees
535 function barre_outils_liste(){
540 // on recupere l'ensemble des barres d'outils connues
541 if (!$sets = find_all_in_path('barre_outils/','.*[.]php')
542 or !is_array($sets)) {
543 spip_log("[Scandale] Porte Plume ne trouve pas de barre d'outils !");
548 foreach($sets as $fichier=>$adresse) {
549 $sets[$fichier] = substr($fichier,0,-4); // juste le nom
555 * filtre appliquant les traitements SPIP d'un champ (et eventuellement d'un type d'objet) sur un texte
556 * (voir la fonction champs_traitements($p) dans : public/references.php)
557 * ce mecanisme est a preferer au traditionnel #TEXTE*|propre
558 * traitements_previsu() consulte la globale $table_des_traitements et applique le traitement adequat
559 * si aucun traitement n'est trouve, alors propre() est applique
561 * @param string $texte : texte source
562 * @param string $nom_champ : champ (en majuscules)
563 * @param string $type_objet : objet (en minuscules)
564 * @return string : texte traite
566 function traitements_previsu($texte, $nom_champ='', $type_objet='', $connect=null) {
567 include_spip('public/interfaces'); // charger les traitements
569 global $table_des_traitements;
570 if(!strlen($nom_champ) ||
!isset($table_des_traitements[$nom_champ])) {
571 $texte = propre($texte, $connect);
574 include_spip('base/abstract_sql');
575 $table = table_objet($type_objet);
576 $ps = $table_des_traitements[$nom_champ];
578 $ps = $ps[(strlen($table) && isset($ps[$table])) ?
$table : 0];
580 $texte = propre($texte, $connect);
582 // remplacer le placeholder %s par le texte fourni
583 eval('$texte=' . str_replace('%s', '$texte', $ps) . ';');
585 // il faut toujours securiser le texte preivusalise car il peut contenir n'importe quoi
586 // et servir de support a une attaque xss ou vol de cookie admin
587 // on ne peut donc se fier au statut de l'auteur connecte car le contenu ne vient pas
589 return safehtml($texte);