In Liferay 7 la funzionalità di menzione (con la "e"... menzione... o se preferite gli inglesismi "mention") nell'editor di testo esiste nativamente, ma se ne ho bisogno in Liferay 6.2 come posso fare? Posso fare, per esempio, completamente a mano partendo da zero (o quasi).
A grandi linee gli step sono 2:
Step 1. Lato client: plugin di autocompletamento per CKEditor
Ho realizzato un plugin per CKEditor in cui ho implementato una funzionalità di autocompletamento che si attiva alla pressione di un tasto (nel mio caso "@") e mostra un elenco di parole suggerite in base a quello che si continua a scrivere successivamente. Cliccando, poi, uno dei risultati dell'elenco, verrà scritto, dopo il carattere "@", la parola appena selezionata. In sostanza un normale "autocomplete", ma dentro CKEditor.
Step 2: Lato server: parsing testo e azione legata alla mention
Al salvataggio del messaggio, parso il testo (in formato HTML) che arriva da CKEditor in cerca degli elementi di tipo mention che ha inserito il plugin, ottengo tutte le parole chiave e agisco di conseguenza. Nel mio caso l'autocompletamento era sugli utenti e invio a tutti i menzionati una email.
Cosa bisogna fare, in pratica, per lo step 1:
Creare un plugin per CKEditor
I plugin vanno salvati nella cartella plugins
della root del CKEditor (in Liferay 6.2 il percorso, a partire dalla cartella di tomcat, è webapps\ROOT\html\js\editor\ckeditor\plugins
) ognuno nella sua cartella. Nel mio caso l'alberatura è la seguente:
-
plugins/
-
mention/
-
plugin.js
In questo file c'è tutto il malloppone javascript -
mention.png
Questa è l'immagine che appare nella toolbar (appare solo se si configura CKEditor appositamente) -
mention.css
Questo è un css custom per stilare gli elementi che ho creato
Il plugin si inizializza e si associa all'editor nel file plugin.js
:
CKEDITOR.plugins.add('mention', {
icons: 'mention.png',
init: function(editor) {
// ...
console.log('ciao sono il plugin mention e sono stato inizializzato');
}
});
Nella init
ho, poi, aggiunto i listener che mi servivano... ma andiamo per gradi. A questo punto il plugin è definito, e va agganciato all'editor nella jsp in cui si vuole usare in questo modo:
CKEDITOR.editorConfig = function( config ){
config.extraPlugins = 'mention';
};
All'inizializzazione dell'editor verrà, ora, effettuata una stampa di log. Ma quello che dovrà fare, per la mention, sarà:
- intercettare i caratteri scritti dall'utente
- effettuare una chiamata ajax per ottenere i valori per l'autocompletamento
- mostrare i risultati in un popup e, al click, scrivere sull'editor il valore cliccato.
Attenzione: l'area editabile del CKEditor è un iframe, quindi bisogna fare attenzione quando si manipola il DOM. CKEditor mette a disposizione diverse API con cui è possibile accedere agli elementi dell'iframe in maniera quasi umana. Per esempio, nell'init del codice sopra si può usare l'oggetto editor
, che, come il nome suggerisce, rappresenta l'istanza dell'editor. Al suo interno si possono trovare le informazioni sullo stato dell'istanza, i plugin disponibili, ecc.; inoltre permette di accedere alla parte editabile dell'area.
Note Varie:
Il tasto premuto?
L'API per l'intercettazione del tasto premuto è la seguente:
editor.on('key', function (event) { ... });
Non ho potuto, però, utilizzarla perchè il keycode
che mi restituiva su android non era corretto, probabilmente perchè la tastiera del telefono non è fisica. Soluzione: ho intercettato l'ultimo carattere immesso nell'editor.
Posizionami in un punto del testo
Un'interessante funzionalità del CKEditor è quella che permette di posizionare il cursore all'interno dell'area di testo in riferimento, ad esempio, ad un elemento.
var range = editor.getSelection().getRanges()[0];
range.moveToPosition(element, CKEDITOR.POSITION_AFTER_END);
range.select();
Questo codice recupera il range di selezione corrente e lo sposta alla fine dell'elemento element
.
Parametrizzazione del plugin
Per la chiamata ajax ho avuto necessità di rendere il plugin parametrizzabile per il passaggio dell'URL per l'autocompletamento. Nell'init
del plugin.js
si possono definire dei valori di default, per poi sovrasciverli con quelli impostati dal chiamante, se presenti.
var defaultConfig = {
myParam : 'param default'
};
var config = CKEDITOR.tools.extend(defaultConfig, editor.config.mention || {}, true);
Nella jsp, si passa il parametro che serve così:
CKEDITOR.config.mention = {
myParam : 'param vero'
};
Popup con i risultati e chiamata ajax
Per gestire il corretto posizionamento della popup dei risultati e per intercettare l'editing della mention, nel momento in cui viene inserita nell'area di testo il carattere "@", posiziono il curosore all'interno di uno span
a cui associo un id
e una class
; questo mi consente, quindi, di riconoscere che il testo che sto scrivendo deve lanciare l'autocompletamento, e con l'id riesco a creare il popup coi risultati ad esso collegato. Ovviamente questa gestione è una delle tante possibili, ognuno può implementare l'autocompletamento come più preferisce, ma è probabile che la necessità di creare un elemento all'interno dell'area si abbia comunque, a questo punto.
var timestamp = new Date().getTime();
var newElement = new CKEDITOR.dom.element("span");
newElement.setAttributes({id: 'mention' + timestamp, class: 'mention'});
editor.insertElement(newElement);
E questi erano gli "hint" principali per la realizzazione dell'autocompletamento all'interno del CKEditor.
Per quanto riguarda lo step 2, ovvero la parte server, credo non ci sia nulla di particolare da dire; si dovrà parsare l'HTML risultante a seconda di come si salva il risultato dell'autocompletamento nell'editor, e poi agire di conseguenza con i risultati ottenuti.