[SPIP] +2.1.12
[velocampus/web/www.git] / www / extensions / porte_plume / inc / barre_outils.php
1 <?php
2 /*
3 * Plugin Porte Plume pour SPIP 2
4 * Licence GPL
5 * Auteur Matthieu Marcillaud
6 */
7
8 if (!defined("_ECRIRE_INC_VERSION")) return;
9
10 include_spip('public/admin'); // pour stripos()
11
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']="";
16 }
17
18 /**
19 * La class Barre_outils est un objet contenant les differents
20 * parametres definissant une barre markitup
21 *
22 */
23 class Barre_outils{
24 var $id = "";
25 var $nameSpace = "";
26 var $lang = "";
27 var $previewAutoRefresh = false;
28 var $previewParserPath = "";
29 var $onEnter = array();
30 var $onShiftEnter = array();
31 var $onCtrlEnter = array();
32 var $onTab = array();
33 var $beforeInsert = "";
34 var $afterInsert = "";
35 var $markupSet = array();
36
37 // liste de fonctions supplementaires a mettre apres le json
38 var $functions = "";
39
40 // private
41 var $_liste_params_autorises = array(
42
43 'replaceWith',
44 'openWith',
45 'closeWith',
46 'placeHolder', // remplace par ce texte lorsqu'il n'y a pas de selection
47
48 'beforeInsert', // avant l'insertion
49 'afterInsert', // apres l'insertion
50 'beforeMultiInsert',
51 'afterMultiInsert',
52
53 'dropMenu', // appelle un sous menu
54
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)
62
63 'separator',
64
65 'call',
66 'keepDefault',
67
68
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
75 'icon',
76
77 // cacher ou afficher facilement des boutons
78 'display',
79 // donner un identifiant unique au bouton (pour le php)
80 'id',
81 );
82
83 /**
84 * Initialise les parametres
85 * @param array $params : param->valeur
86 */
87 function Barre_outils($params=array()){
88 foreach ($params as $p=>$v) {
89 if (isset($this->$p)) {
90 // si tableau, on verifie les entrees
91 if (is_array($v)) {
92 $v = $this->verif_params($p,$v);
93 }
94 $this->$p = $v;
95 }
96 }
97 }
98
99 /**
100 * Verifie que les parametres transmis existent
101 * et retourne un tableau des parametres valides
102 *
103 * @param string $nom : cle du parametre (eventuel)
104 * @param array $params : parametres du parametre (param->valeur)
105 */
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);
111 }
112 }
113 // sinon on teste la validite
114 else {
115 foreach ($params as $p=>$v) {
116 if (!in_array($p, $this->_liste_params_autorises)) {
117 unset($params[$p]);
118 }
119 }
120 }
121 return $params;
122 }
123
124 /**
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.
127 *
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).
131 *
132 * Lorsqu'on demande d'inserer avant ou apres, la fonction retourne les parametres inseres
133 *
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)
138 */
139 function affecter(&$tableau, $identifiant, $params=array(), $lieu='dedans'){
140 static $cle_de_recherche = 'id'; // ou className ?
141
142 if ($tableau === null)
143 $tableau = &$this->markupSet;
144
145 if (!in_array($lieu, array('dedans','avant','apres')))
146 $lieu = 'dedans';
147
148 // present en premiere ligne ?
149 $trouve = false;
150 foreach ($tableau as $i=>$v){
151 if (isset($v[$cle_de_recherche]) and ($v[$cle_de_recherche] == $identifiant)) {
152 $trouve = $i;
153 break;
154 }
155 }
156 // si trouve, affectations
157 if (($trouve !== false)) {
158 if ($params) {
159 $params = $this->verif_params($identifiant, $params);
160 // dedans on merge
161 if ($lieu == 'dedans') {
162 return $tableau[$trouve] = array_merge($tableau[$trouve], $params);
163 }
164 // avant ou apres, on insere
165 elseif ($lieu == 'avant') {
166 array_splice($tableau, $trouve, 0, array($params));
167 return $params;
168 }
169 elseif ($lieu == 'apres') {
170 array_splice($tableau, $trouve+1, 0, array($params));
171 return $params;
172 }
173 }
174 return $tableau[$trouve];
175 }
176
177 // recursivons sinon !
178 foreach ($tableau as $i=>$v){
179 if (is_array($v)) {
180 foreach ($v as $m=>$n) {
181 if (is_array($n) AND ($r = $this->affecter($tableau[$i][$m], $identifiant, $params, $lieu)))
182 return $r;
183 }
184 }
185 }
186 return false;
187 }
188
189
190 /**
191 * Permet d'affecter des parametres toutes les elements de la barre
192 *
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)
197 */
198 function affecter_a_tous(&$tableau, $params=array(), $ids=array()){
199 if (!$params)
200 return false;
201
202 if ($tableau === null)
203 $tableau = &$this->markupSet;
204
205 $params = $this->verif_params('divers', $params);
206
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);
211 }
212 // recursion si sous-menu
213 if (isset($tableau[$i]['dropMenu'])) {
214 $this->affecter_a_tous($tableau[$i]['dropMenu'], $params, $ids);
215 }
216 }
217 return true;
218 }
219
220
221 /**
222 * Affecte les valeurs des parametres indiques au bouton demande
223 * et retourne l'ensemble des parametres du bouton (sinon false)
224 *
225 * @param string/array $identifiant : id du ou des boutons a afficher
226 * @param array $params : param->valeur
227 * @return mixed
228 */
229 function set($identifiant, $params=array()) {
230 // prudence tout de meme a pas tout modifier involontairement (si array)
231 if (!$identifiant) return false;
232
233 if (is_string($identifiant)) {
234 return $this->affecter($this->markupSet, $identifiant, $params);
235 }
236 elseif (is_array($identifiant)) {
237 return $this->affecter_a_tous($this->markupSet, $params, $identifiant);
238 }
239 return false;
240 }
241
242 /**
243 * Retourne les parametres du bouton demande
244 *
245 * @param string $identifiant : nom (de la classe du) bouton
246 * @return mixed
247 */
248 function get($identifiant) {
249 if ($a = $this->affecter($this->markupSet, $identifiant)) {
250 return $a;
251 }
252 return false;
253 }
254
255
256 /**
257 * Affiche le bouton demande
258 *
259 * @param string $identifiant : nom (de la classe du) bouton a afficher
260 * @return true/false
261 */
262 function afficher($identifiant){
263 return $this->set($identifiant,array('display'=>true));
264 }
265
266
267 /**
268 * Cache le bouton demande
269 *
270 * @param string $identifiant : nom (de la classe du) bouton a afficher
271 * @return true/false
272 */
273 function cacher($identifiant){
274 return $this->set($identifiant,array('display'=>false));
275 }
276
277
278 /**
279 * Affiche tous les boutons
280 *
281 * @param string $identifiant : nom (de la classe du) bouton a afficher
282 * @return true/false
283 */
284 function afficherTout(){
285 return $this->affecter_a_tous($this->markupSet, array('display'=>true));
286 }
287
288 /**
289 * Cache tous les boutons
290 *
291 * @param string $identifiant : nom (de la classe du) bouton a afficher
292 * @return true/false
293 */
294 function cacherTout(){
295 return $this->affecter_a_tous($this->markupSet, array('display'=>false));
296 }
297
298
299 /**
300 * ajouter un bouton ou quelque chose, avant un autre deja present
301 *
302 * @param string $identifiant : identifiant du bouton ou l'on doit se situer
303 * @param array $params : parametres de l'ajout
304 */
305 function ajouterAvant($identifiant, $params){
306 return $this->affecter($this->markupSet, $identifiant, $params, 'avant');
307 }
308
309 /**
310 * ajouter un bouton ou quelque chose, apres un autre deja present
311 *
312 * @param string $identifiant : identifiant du bouton ou l'on doit se situer
313 * @param array $params : parametres de l'ajout
314 */
315 function ajouterApres($identifiant, $params){
316 return $this->affecter($this->markupSet, $identifiant, $params, 'apres');
317
318 }
319
320 /**
321 * ajouter une fonction js pour etre utilises dans les boutons
322 *
323 * @param string $fonction : code de la fonction js
324 * @return null
325 */
326 function ajouterFonction($fonction){
327 if (false === strpos($this->functions, $fonction)){
328 $this->functions .= "\n" . $fonction . "\n";
329 }
330 }
331
332 /**
333 * Supprimer les elements non affiches (display:false)
334 *
335 * @param false/array $tableau : tableau a analyser (sert pour la recursion)
336 */
337 function enlever_elements_non_affiches(&$tableau){
338 if ($tableau === null)
339 $tableau = &$this->markupSet;
340
341 foreach ($tableau as $p=>$v){
342
343 if (isset($v['display']) AND !$v['display']) {
344 unset($tableau[$p]);
345 $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.
346 }
347 // sinon, on lance une recursion sur les sous-menus
348 else {
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']) {
353 unset($tableau[$p]);
354 $tableau = array_values($tableau);
355 }
356 }
357 }
358 }
359 }
360
361 /**
362 * Supprime les elements vides (uniquement a la racine de l'objet)
363 * et uniquement si chaine ou tableau.
364 *
365 * Supprime les parametres prives
366 * Supprime les parametres inutiles a markitup/json dans les parametres markupSet
367 * (id, display, icone)
368 */
369 function enlever_parametres_inutiles() {
370 foreach($this as $p=>$v){
371 if (!$v) {
372 if (is_array($v) or is_string($v)) {
373 unset($this->$p);
374 }
375 } elseif ($p == 'functions') {
376 unset($this->$p);
377 }
378 }
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]);
383 }
384 }
385 }
386 unset ($this->_liste_params_autorises);
387 }
388
389
390 /**
391 * Cree la sortie json pour le javascript des parametres de la barre
392 * et la retourne
393 *
394 * @return string : declaration json de la barre
395 */
396 function creer_json(){
397 $barre = $this;
398 $type = $barre->nameSpace;
399 $fonctions = $barre->functions;
400
401 $barre->enlever_elements_non_affiches($this->markupSet);
402 $barre->enlever_parametres_inutiles();
403
404 $json = Barre_outils::json_export($barre);
405
406 // on lance la transformation des &chose; en veritables caracteres
407 // sinon markitup restitue &laquo; 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";
412 }
413
414 /**
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
418 *
419 * @access private
420 * @param mixed the variable
421 * @return string js script | boolean false if error
422 */
423 function json_export($var) {
424 $asso = false;
425 switch (true) {
426 case is_null($var) :
427 return 'null';
428 case is_string($var) :
429 if (strtolower(substr(ltrim($var),0,8))=='function')
430 return $var;
431 return '"' . addcslashes($var, "\"\\\n\r") . '"';
432 case is_bool($var) :
433 return $var ? 'true' : 'false';
434 case is_scalar($var) :
435 return $var;
436 case is_object( $var) :
437 $var = get_object_vars($var);
438 $asso = true;
439 case is_array($var) :
440 $keys = array_keys($var);
441 $ikey = count($keys);
442 while (!$asso && $ikey--) {
443 $asso = $ikey !== $keys[$ikey];
444 }
445 $sep = '';
446 if ($asso) {
447 $ret = '{';
448 foreach ($var as $key => $elt) {
449 $ret .= $sep . '"' . $key . '":' . Barre_outils::json_export($elt);
450 $sep = ',';
451 }
452 return $ret ."}\n";
453 } else {
454 $ret = '[';
455 foreach ($var as $elt) {
456 $ret .= $sep . Barre_outils::json_export($elt);
457 $sep = ',';
458 }
459 return $ret ."]\n";
460 }
461 }
462 return false;
463 }
464
465 }
466
467
468
469 /**
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.
473 *
474 * elle cherche une fonction barre_outils_($barre)_icones pour chaque
475 * barre et l'appelle si existe.
476 *
477 * @return string : declaration css des icones
478 */
479 function barre_outils_css_icones(){
480 // recuperer la liste, extraire les icones
481 $css = "";
482
483 // liste des barres
484 if (!$barres = barre_outils_liste())
485 return null;
486
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);
494 }
495 }
496 }
497
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);
500
501 // passage en css
502 foreach ($classe2icone as $n=>$i) {
503 $pos="";
504 if (is_array($i)){
505 $pos = "background-position:".end($i);
506 $i = reset($i);
507 }
508 $css .= "\n.markItUp .$n a b {background-image:url(".url_absolue(find_in_path("icones_barre/$i")).");$pos}";
509 }
510
511 return $css;
512 }
513
514
515 /**
516 * Retourne une instance de Barre_outils
517 * cree a partir du type de barre demande
518 *
519 * @param string $set : type de barre
520 * @return object/false : objet de type barre_outil
521 */
522 function barre_outils_initialiser($set){
523 if ($f = charger_fonction($set, 'barre_outils')) {
524 // retourne une instance de l'objet Barre_outils
525 return $f();
526 }
527 return false;
528 }
529
530 /**
531 * Retourne la liste des barres d'outils connues
532 *
533 * @return array/false : tableau des noms de barres trouvees
534 */
535 function barre_outils_liste(){
536 static $sets = -1;
537 if ($sets !== -1)
538 return $sets;
539
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 !");
544 $sets = false;
545 return $sets;
546 }
547
548 foreach($sets as $fichier=>$adresse) {
549 $sets[$fichier] = substr($fichier,0,-4); // juste le nom
550 }
551 return $sets;
552 }
553
554 /**
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
560 *
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
565 */
566 function traitements_previsu($texte, $nom_champ='', $type_objet='', $connect=null) {
567 include_spip('public/interfaces'); // charger les traitements
568 safehtml($t);
569 global $table_des_traitements;
570 if(!strlen($nom_champ) || !isset($table_des_traitements[$nom_champ])) {
571 $texte = propre($texte, $connect);
572 }
573 else {
574 include_spip('base/abstract_sql');
575 $table = table_objet($type_objet);
576 $ps = $table_des_traitements[$nom_champ];
577 if(is_array($ps))
578 $ps = $ps[(strlen($table) && isset($ps[$table])) ? $table : 0];
579 if(!$ps)
580 $texte = propre($texte, $connect);
581 else
582 // remplacer le placeholder %s par le texte fourni
583 eval('$texte=' . str_replace('%s', '$texte', $ps) . ';');
584 }
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
588 // forcement de lui
589 return safehtml($texte);
590 }
591 ?>