a281f1d8c90a1884cec53531ffb52f03ea10d661
3 * Fonctions utiles pour le Porte Plume
5 * @plugin Porte Plume pour SPIP
7 * @package SPIP\PortePlume\BarreOutils
10 if (!defined("_ECRIRE_INC_VERSION")) return;
13 * Objet contenant les différents paramètres definissant une barre d'outils
14 * Markitup et permettant d'agir dessus
17 * $barre = new Barre_Outil($description);
19 * @package SPIP\PortePlume\BarreOutils
23 * Identifiant HTML de la barre
24 * @todo À supprimer car non utilisé !
29 * Nom de la barre d'outil
31 public $nameSpace = "";
35 * @todo À supprimer car non utilisé !
40 * Option de markitup : rafraîchir la prévisu ?
41 * @todo À supprimer car non utilisé !
43 public $previewAutoRefresh = false;
46 * Option de markitup : nom de la fonction de prévisu
47 * @todo À supprimer car on le redéfini dans l'appel javascript !
49 public $previewParserPath = "";
52 * Option de markitup : que faire sur l'appuie de Entrée ?
54 public $onEnter = array();
58 * Option de markitup : que faire sur l'appuie de Shift+Entrée ?
59 * @example array('keepDefault'=>false, 'replaceWith'=>"\n_ ")
61 public $onShiftEnter = array();
64 * Option de markitup : que faire sur l'appuie de Control+Entrée ?
66 public $onCtrlEnter = array();
69 * Option de markitup : que faire sur l'appuie d'une tabulation ?
71 public $onTab = array();
74 * Option de markitup : Code JS à exécuter avant une insertion
76 public $beforeInsert = "";
79 * Option de markitup : Code JS à exécuter après une insertion
81 public $afterInsert = "";
84 * Description des outils/boutons et leurs sous boutons éventuels
86 public $markupSet = array();
89 * Fonctions JS supplémentaires à écrire après la déclaration JSON
90 * des outils. Ces fonctions peuvent servir aux boutons.
92 public $functions = "";
95 * Liste des paramètres valides pour une description d'outils (markupSet)
97 private $_liste_params_autorises = array(
102 'openBlockWith', // sur multiline, avant les lignes selectionnees
103 'closeBlockWith', // sur multiline, apres les lignes selectionnees
104 'placeHolder', // remplace par ce texte lorsqu'il n'y a pas de selection
106 'beforeInsert', // avant l'insertion
107 'afterInsert', // apres l'insertion
111 'dropMenu', // appelle un sous menu
113 'name', // nom affiche au survol
114 'key', // raccourcis clavier
115 'className', // classe css utilisee
116 'lang', // langues dont le bouton doit apparaitre - array
117 'lang_not', // langues dont le bouton ne doit pas apparaitre - array
118 'selectionType', // '','word','line' : type de selection (normale, aux mots les plus proches, a la ligne la plus proche)
119 'multiline', // open/close sur chaque ligne (mais replace est applique sur l'ensemble de la selection)
120 'forceMultiline', // pour faire comme si on faisait systematiquement un control+shift (et replace est applique sur chaque ligne de la selection)
127 // cacher ou afficher facilement des boutons
129 // donner un identifiant unique au bouton (pour le php)
136 * Initialise la barre avec les paramètres transmis
137 * en n'adressant que les paramètres effectivement valides
140 * @param array $params Paramètres de la barre d'outil
143 function Barre_outils($params=array()){
144 foreach ($params as $p=>$v) {
145 if (isset($this->$p)) {
146 // si tableau, on verifie les entrees
148 $v = $this->verif_params($p,$v);
157 * Vérifie que les paramètres d'une clé existent
158 * et retourne un tableau des paramètres valides
161 * Clé à vérifier (ex: 'markupSet')
162 * @param array $params
163 * Paramètres de cette clé (description des boutons ou sous boutons)
165 * Paramètres, soustrait de ceux qui ne sont pas valides
167 function verif_params($nom, $params=array()) {
168 // si markupset, on boucle sur les items
169 if (stripos($nom, 'markupSet')!==false) {
170 foreach ($params as $i=>$v) {
171 $params[$i] = $this->verif_params($i, $v);
174 // sinon on teste la validite
176 foreach ($params as $p=>$v) {
177 if (!in_array($p, $this->_liste_params_autorises
)) {
186 * Permet d'affecter des paramètres à un élément de la barre
188 * La fonction retourne les paramètres, de sorte qu'on peut s'en servir
189 * pour simplement récupérer ceux-ci.
191 * Il est possible d'affecter des paramètres avant/après l'élément trouvé
192 * en definisant une valeur différente pour le $lieu : 'dedans','avant','apres'
193 * par defaut 'dedans' (modifie l'élément trouvé).
195 * Lorsqu'on demande d'insérer avant ou après, la fonction retourne
196 * les paramètres inserés
198 * @param array $tableau
199 * Tableau ou chercher les elements (sert pour la recursion)
200 * @param string $identifiant
201 * Identifiant du bouton a afficher
202 * @param array $params
203 * Paramètres à affecter à la trouvaille (ou avant ou après).
204 * Peut être un tableau clé/valeur ou un tableau de tableaux
205 * clé/valeur (sauf pour $lieu = dedans)
206 * @param string $lieu
207 * Lieu d'affectation des paramètres (dedans, avant, apres)
208 * @param bool $plusieurs
209 * Définit si $params est une forme simple (tableau cle/valeur)
210 * ou comporte plusieurs boutons (tableau de tableaux cle/valeur).
212 * Paramètres de l'élément modifié ou paramètres ajoutés
213 * False si l'identifiant cherché n'est pas trouvé
215 function affecter(&$tableau, $identifiant, $params=array(), $lieu='dedans', $plusieurs=false){
216 static $cle_de_recherche = 'id'; // ou className ?
218 if ($tableau === null) // utile ?
219 $tableau = &$this->markupSet
;
221 if (!in_array($lieu, array('dedans','avant','apres')))
224 // present en premiere ligne ?
226 foreach ($tableau as $i=>$v){
227 if (isset($v[$cle_de_recherche]) and ($v[$cle_de_recherche] == $identifiant)) {
232 // si trouve, affectations
233 if (($trouve !== false)) {
235 // verifier que les insertions sont correctes
236 $les_params = ($plusieurs ?
$params : array($params));
237 foreach ($les_params as $i=>$un_params) {
238 $les_params[$i] = $this->verif_params($identifiant, $un_params);
241 // dedans on merge ($params uniquement tableau cle/valeur)
242 if ($lieu == 'dedans' && !$plusieurs) {
243 return $tableau[$trouve] = array_merge($tableau[$trouve], $les_params[0]);
245 // avant ou apres, on insere ($params peut etre tableau cle/valeur ou tableau de tableaux cle/valeur)
246 elseif ($lieu == 'avant') {
247 array_splice($tableau, $trouve, 0, $les_params);
250 elseif ($lieu == 'apres') {
251 array_splice($tableau, $trouve+
1, 0, $les_params);
255 return $tableau[$trouve];
258 // recursivons sinon !
259 foreach ($tableau as $i=>$v){
261 foreach ($v as $m=>$n) {
262 if (is_array($n) AND ($r = $this->affecter($tableau[$i][$m], $identifiant, $params, $lieu, $plusieurs)))
272 * Permet d'affecter des paramètres à tous les éléments de la barre
273 * ou à une liste d'identifiants d'éléments indiqués.
275 * @param array $tableau
276 * Tableau où chercher les éléments
277 * @param array $params
278 * Paramètres à affecter aux éléments
280 * Tableau d'identifiants particuliers à qui on affecte les paramètres.
281 * Si vide, tous les identifiants seront modifiés
283 * false si aucun paramètre à affecter, true sinon.
285 function affecter_a_tous(&$tableau, $params=array(), $ids=array()){
289 if ($tableau === null)
290 $tableau = &$this->markupSet
;
292 $params = $this->verif_params('divers', $params);
294 // merge de premiere ligne
295 foreach ($tableau as $i=>$v){
296 if (!$ids OR in_array($v['id'], $ids)) {
297 $tableau[$i] = array_merge($tableau[$i], $params);
299 // recursion si sous-menu
300 if (isset($tableau[$i]['dropMenu'])) {
301 $this->affecter_a_tous($tableau[$i]['dropMenu'], $params, $ids);
309 * Affecte les valeurs des paramètres indiqués au bouton demandé
310 * et retourne l'ensemble des paramètres du bouton (sinon false)
313 * @param string|array $identifiant
314 * Identifiant du ou des boutons.
315 * @param array $params
316 * Paramètres de l'ajout (tableau paramètre=>valeur)
318 * false si l'identifiant n'a pas été trouvé
319 * true si plusieurs identifiants,
320 * array sinon : description de l'identifiant cherché.
322 function set($identifiant, $params=array()) {
323 // prudence tout de meme a pas tout modifier involontairement (si array)
324 if (!$identifiant) return false;
326 if (is_string($identifiant)) {
327 return $this->affecter($this->markupSet
, $identifiant, $params);
329 elseif (is_array($identifiant)) {
330 return $this->affecter_a_tous($this->markupSet
, $params, $identifiant);
336 * Retourne les parametres du bouton demande
339 * @param string|array $identifiant
340 * Identifiant du ou des boutons.
342 * false si l'identifiant n'est pas trouvé
343 * array sinon : Description de l'identifiant cherché.
345 function get($identifiant) {
346 if ($a = $this->affecter($this->markupSet
, $identifiant)) {
354 * Affiche le ou les boutons demandés
357 * @param string|array $identifiant
358 * Identifiant du ou des boutons
360 * false si l'identifiant n'a pas été trouvé
361 * true si plusieurs identifiants,
362 * array sinon : description de l'identifiant cherché.
364 function afficher($identifiant){
365 return $this->set($identifiant,array('display'=>true));
370 * Cache le ou les boutons demandés
373 * @param string|array $identifiant
374 * Identifiant du ou des boutons
376 * false si l'identifiant n'a pas été trouvé
377 * true si plusieurs identifiants,
378 * array sinon : description de l'identifiant cherché.
380 function cacher($identifiant){
381 return $this->set($identifiant, array('display'=>false));
386 * Affiche tous les boutons
390 * false si aucun paramètre à affecter, true sinon.
392 function afficherTout(){
393 return $this->affecter_a_tous($this->markupSet
, array('display'=>true));
397 * Cache tous les boutons
401 * false si aucun paramètre à affecter, true sinon.
403 function cacherTout(){
404 return $this->affecter_a_tous($this->markupSet
, array('display'=>false));
409 * Ajoute un bouton ou quelque chose, avant un autre déjà présent
412 * @param string $identifiant
413 * Identifiant du bouton où l'on doit se situer
414 * @param array $params
415 * Paramètres de l'ajout.
416 * Description d'un bouton (tableau clé/valeurs).
418 * Paramètres ajoutés avant
419 * False si l'identifiant cherché n'est pas trouvé
421 function ajouterAvant($identifiant, $params){
422 return $this->affecter($this->markupSet
, $identifiant, $params, 'avant');
426 * Ajoute plusieurs boutons, avant un autre déjà présent
429 * @param string $identifiant
430 * Identifiant du bouton où l'on doit se situer
431 * @param array $tableau_params
432 * Paramètres de l'ajout.
433 * Description de plusieurs boutons (tableau de tableaux clé/valeurs).
435 * Paramètres ajoutés avant
436 * False si l'identifiant cherché n'est pas trouvé
438 function ajouterPlusieursAvant($identifiant, $tableau_params){
439 return $this->affecter($this->markupSet
, $identifiant, $tableau_params, 'avant', true);
443 * Ajoute un bouton ou quelque chose, après un autre déjà présent
446 * @param string $identifiant
447 * Identifiant du bouton où l'on doit se situer
448 * @param array $params
449 * Paramètres de l'ajout.
450 * Description d'un bouton (tableau clé/valeurs).
452 * Paramètres ajoutés après
453 * False si l'identifiant cherché n'est pas trouvé
455 function ajouterApres($identifiant, $params){
456 return $this->affecter($this->markupSet
, $identifiant, $params, 'apres');
460 * Ajoute plusieurs boutons, après un autre déjà présent
463 * @param string $identifiant
464 * Identifiant du bouton où l'on doit se situer
465 * @param array $tableau_params
466 * Paramètres de l'ajout.
467 * Description de plusieurs boutons (tableau de tableaux clé/valeurs).
469 * Paramètres ajoutés après
470 * False si l'identifiant cherché n'est pas trouvé
472 function ajouterPlusieursApres($identifiant, $tableau_params){
473 return $this->affecter($this->markupSet
, $identifiant, $tableau_params, 'apres', true);
477 * Ajoute une fonction JS qui pourra être utilisée par les boutons
480 * @param string $fonction Code de la fonction JS
483 function ajouterFonction($fonction){
484 if (false === strpos($this->functions
, $fonction)){
485 $this->functions
.= "\n" . $fonction . "\n";
490 * Supprimer les éléments non affichés (display:false)
491 * Et les séparateurs (li vides) selon la configuration
493 * @param array $tableau Tableau de description des outils
496 function enlever_elements_non_affiches(&$tableau){
497 if ($tableau === null) // utile ?
498 $tableau = &$this->markupSet
;
500 foreach ($tableau as $p=>$v) {
502 if (isset($v['display']) AND !$v['display']) {
504 $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.
506 // sinon, on lance une recursion sur les sous-menus
508 if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {
509 $this->enlever_elements_non_affiches($tableau[$p]['dropMenu']);
510 // si le sous-menu est vide
511 // on enleve le sous menu.
512 // mais pas le parent ($tableau[$p]), qui peut effectuer une action.
513 if (!$tableau[$p]['dropMenu']) {
514 unset($tableau[$p]['dropMenu']);
522 * Enlève les séparateurs pour améliorer l'accessibilité
523 * au détriment du stylage possible de ces séparateurs.
525 * Le bouton précédent le séparateur reçoit une classe CSS 'separateur_avant'
526 * Celui apres 'separateur_apres'
528 * @param array Tableau de description des outils
531 function enlever_separateurs(&$tableau) {
532 if ($tableau === null) // utile ?
533 $tableau = &$this->markupSet
;
535 foreach ($tableau as $p=>$v) {
536 if (isset($v['separator']) and $v['separator']) {
537 if (isset($tableau[$p-1])) {
538 $tableau[$p-1]['className'] .= " separateur_avant";
540 if (isset($tableau[$p+
1])) {
541 $tableau[$p+
1]['className'] .= " separateur separateur_apres $v[id]";
544 $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.
546 // sinon, on lance une recursion sur les sous-menus
548 if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {
549 #$this->enlever_separateurs($tableau[$p]['dropMenu']);
556 * Supprime les éléments vides (uniquement à la racine de l'objet)
557 * et uniquement si chaîne ou tableau.
559 * Supprime les paramètres privés
560 * Supprime les paramètres inutiles a markitup/json dans les paramètres markupSet
561 * (id, display, icone)
563 function enlever_parametres_inutiles() {
564 foreach($this as $p=>$v){
566 if (is_array($v) or is_string($v)) {
569 } elseif ($p == 'functions') {
573 foreach($this->markupSet
as $p=>$v) {
574 foreach ($v as $n=>$m) {
575 if (in_array($n, array('id', 'display'))) {
576 unset($this->markupSet
[$p][$n]);
580 unset ($this->_liste_params_autorises
);
585 * Crée la sortie json pour le javascript des paramètres de la barre
587 * @return string Déclaration json de la barre
589 function creer_json(){
591 $type = $barre->nameSpace;
592 $fonctions = $barre->functions
;
594 $barre->enlever_elements_non_affiches($this->markupSet
);
595 $barre->enlever_separateurs($this->markupSet
);
596 $barre->enlever_parametres_inutiles();
598 $json = Barre_outils
::json_export($barre);
600 // on lance la transformation des &chose; en veritables caracteres
601 // sinon markitup restitue « au lieu de « directement
602 // lorsqu'on clique sur l'icone
603 include_spip('inc/charsets');
604 $json = unicode2charset(html2unicode($json));
605 return "\n\nbarre_outils_$type = ".$json . "\n\n $fonctions";
609 * Transforme une variable PHP dans un équivalent javascript (json)
611 * Copié depuis ecrire/inc/json, mais modifié pour que les fonctions
612 * JavaScript ne soient pas encapsulées dans une chaîne (string)
615 * @param mixed the variable
616 * @return string js script | boolean false if error
618 function json_export($var) {
623 case is_string($var) :
624 if (strtolower(substr(ltrim($var),0,8))=='function')
626 return '"' . addcslashes($var, "\"\\\n\r") . '"';
628 return $var ?
'true' : 'false';
629 case is_scalar($var) :
631 case is_object( $var) :
632 $var = get_object_vars($var);
634 case is_array($var) :
635 $keys = array_keys($var);
636 $ikey = count($keys);
637 while (!$asso && $ikey--) {
638 $asso = $ikey !== $keys[$ikey];
643 foreach ($var as $key => $elt) {
644 $ret .= $sep . '"' . $key . '":' . Barre_outils
::json_export($elt);
650 foreach ($var as $elt) {
651 $ret .= $sep . Barre_outils
::json_export($elt);
664 * Crée le code CSS pour les images des icones des barres d'outils
666 * S'appuie sur la description des jeux de barres disponibles et cherche
667 * une fonction barre_outils_($barre)_icones pour chaque barre et
668 * l'exécute si existe, attendant alors en retour un tableau de couples :
669 * nom de l'outil => nom de l'image
671 * @pipeline_appel porte_plume_lien_classe_vers_icone
673 * @return string Déclaration CSS des icones
675 function barre_outils_css_icones(){
676 // recuperer la liste, extraire les icones
680 if (!$barres = barre_outils_liste())
683 // liste des classes css et leur correspondance avec une icone
684 $classe2icone = array();
685 foreach ($barres as $barre) {
686 include_spip('barre_outils/' . $barre);
687 if ($f = charger_fonction($barre . '_icones', 'barre_outils', true)) {
688 if (is_array($icones = $f())) {
689 $classe2icone = array_merge($classe2icone, $icones);
695 * Permettre aux plugins d'étendre les icones connues du porte plume
697 * On passe la liste des icones connues au pipeline pour ceux qui
698 * ajoutent de simples icones à des barres existantes
700 * @pipeline_appel porte_plume_lien_classe_vers_icone
701 * @var array $classe2icone
702 * Couples identifiant de bouton => nom de l'image (ou tableau)
703 * Dans le cas d'un tableau, cela indique une sprite : (nom de l'image , position haut, position bas)
704 * Exemple : 'outil_header1' => array('spt-v1.png','-10px -226px')
706 $classe2icone = pipeline('porte_plume_lien_classe_vers_icone', $classe2icone);
709 foreach ($classe2icone as $n=>$i) {
712 $pos = "background-position:".end($i);
715 $css .= "\n.markItUp .$n>a>em {background-image:url(".protocole_implicite(url_absolue(find_in_path("icones_barre/$i"))).");$pos}";
723 * Retourne une instance de Barre_outils
724 * crée à partir du type de barre demandé
726 * Une fonction barre_outils_{type}_dist() retournant la barre doit
730 * Type de barre (ex: 'edition')
731 * @return Barre_Outils|bool
732 * La barre d'outil si la fonction a été trouvée, false sinon
734 function barre_outils_initialiser($set){
735 if ($f = charger_fonction($set, 'barre_outils')) {
736 // retourne une instance de l'objet Barre_outils
743 * Retourne la liste des barres d'outils connues
746 * Tableau des noms de barres d'outils trouvées
747 * False si on ne trouve aucune barre.
749 function barre_outils_liste(){
754 // on recupere l'ensemble des barres d'outils connues
755 if (!$sets = find_all_in_path('barre_outils/','.*[.]php')
756 or !is_array($sets)) {
757 spip_log("[Scandale] Porte Plume ne trouve pas de barre d'outils !");
762 foreach($sets as $fichier=>$adresse) {
763 $sets[$fichier] = substr($fichier,0,-4); // juste le nom
769 * Filtre appliquant les traitements SPIP d'un champ
771 * Applique les filtres prévus sur un champ (et eventuellement un type d'objet)
772 * sur un texte donné. Sécurise aussi le texte en appliquant safehtml().
774 * Ce mécanisme est à préférer au traditionnel #TEXTE*|propre
776 * traitements_previsu() consulte la globale $table_des_traitements et
777 * applique le traitement adequat. Si aucun traitement n'est trouvé,
778 * alors propre() est appliqué.
780 * @package SPIP\PortePlume\Fonctions
781 * @see champs_traitements() dans public/references.php
782 * @global table_des_traitements
784 * @param string $texte
786 * @param string $nom_champ
787 * Nom du champ (nom de la balise, en majuscules)
788 * @param string $type_objet
789 * L'objet a qui appartient le champ (en minuscules)
790 * @param string $connect
791 * Nom du connecteur de base de données
793 * Texte traité avec les filtres déclarés pour le champ.
795 function traitements_previsu($texte, $nom_champ='', $type_objet='', $connect=null) {
796 include_spip('public/interfaces'); // charger les traitements
798 global $table_des_traitements;
799 if (!strlen($nom_champ) ||
!isset($table_des_traitements[$nom_champ])) {
800 $texte = propre($texte, $connect);
803 include_spip('base/abstract_sql');
804 $table = table_objet($type_objet);
805 $ps = $table_des_traitements[$nom_champ];
807 $ps = $ps[(strlen($table) && isset($ps[$table])) ?
$table : 0];
810 $texte = propre($texte, $connect);
812 // [FIXME] Éviter une notice sur le eval suivant qui ne connait
813 // pas la Pile ici. C'est pas tres joli...
814 $Pile = array( 0 => array() );
815 // remplacer le placeholder %s par le texte fourni
816 eval('$texte=' . str_replace('%s', '$texte', $ps) . ';');
819 // il faut toujours securiser le texte prévisualisé car il peut contenir n'importe quoi
820 // et servir de support a une attaque xss ou vol de cookie admin
821 // on ne peut donc se fier au statut de l'auteur connecté car le contenu ne vient pas
823 return safehtml($texte);