Tu sei qui

Durante il mio ingresso nel mondo Drupal, arrivando dalla programmazione per WordPress, ho notato (e sentito) la mancanza degli shortcode.

Uno shortcode è letteralmente un "codice abbreviato", un segnaposto che mi permette di scrivere nell'editor di testo delle una semplice tag, ad esempio [autore][/autore], e vederla sostituita in fase di rendering della pagina con un codice più complesso che, per esempio, recupera i dati dell'autore e li formatta come li desidero. Bella semplificazione vero?

Il tempo impiegato ad imparare ed inserire uno shortcode risulta decisamente più basso rispetto all'inserimento di una porzione di codice HTML. Inoltre, nulla ci vieta di definire dei parametri per i nostri shortcode. Questo ci consente di personalizzare lo stesso shortcode in diversi punti della pagina ottenendo cosi comportamenti differenti.

Lo shortcode altro non è che una macro che permette di scrivere un semplice codice che richiama a sua volta un set di istruzioni più complesse

Durante lo sviluppo del nuovo sito di TourTools mi sono trovato a dover implementare alcune funzionalità che potevano essere più semplicemente gestite con l'utilizzo di shortcode. Quale momento e situazione migliore per testare come gestire gli shortcode in Drupal?

All'inizio si parlava di gestire la formattazione di righe e colonne per il tema resposive basato con Zurb Foundation, poi le funzionalità gestite con gli shortcode sono aumentate fino a coprire il rendering del blocco autore, i TweetMe, i testi in evidenza, i badge di Google+ e altre cose ancora.

Drupal 7

Cercando informazioni in merito agli shortcode e Drupal ho trovato tre moduli interessanti che meritano di essere citati:

  • innanzitutto il fondamentale Shortcode che implementa una gestione di base di alcuni shortcode (quote, immagini ed altro);
  • vi è inoltre il modulo Basic Shortcodes Library che si appoggia sul primo modulo arricchendolo con shortcode aggiuntivi;
  • ed infine il modulo Shortcode for Wysiwyg che, seppur in versione Development, permette di integrare nell'editor WYSIWYG un pulsante per facilitare l’inserimento degli shortcode nel testo.

Aver trovato quei tre moduli ha velocizzato notevolmente i tempi di sviluppo. Tralasciando Basic Shortcodes Library, che risulta inutile nel nostro caso, ho deciso di appoggiarmi al modulo Shortcode per sfruttare il core di inserimento ed a Shortcode for Wysiwyg per facilitare l'editing degli articoli.

A questo punto bisogna però vedere come gestire gli shortcode.

I code therefore I am

Photo by { circle } / CC BY-NC-SA 2.0

NB: in questo articolo vedremo il codice per i soli shortcode. Tutto quello che verrà spiegato prevede che abbiate già installato ed abilitato i moduli Shortcode e Shortcode for Wysiwyg sul vostro sito.

Ho deciso di organizzare il codice degli shortcode suddividendo le funzionalità per ambito e creando quindi un singolo sottomodulo per ogni ambito. Innanzitutto ho creato un modulo principale, chiamato tx_shortcode, che gestisce semplicemente hook_menu e la sua callback per drupal_get_form per aggiungere una pagina di configurazione globale di tutti moduli in un unica pagina del nostro pannello amministrativo. All’interno di ogni singolo sottomodulo ho poi implementato nuovamente hook_menu ed il suo drupal_get_form per renderizzare la specifica interfaccia di configurazione.

Un primo esempio: la didascalia

Siamo finalmente arrivati a mettere le mani in pasta. Il primo esempio è quello di uno shortcode semplice e senza parametri aggiuntivi utilizzato per stilizzare un blocco contenente del testo per una didascalia.

/**
 * Implementation of hook_shortcode_info().
 * This function use the same structure of hook_filter_info().
 * This function will add a shortcode to the available list.
 */
function tx_shortcode_element_shortcode_info() {
  $shortcodes = array();
  $shortcodes['tx_shortcode_dida'] = array(
    'title' => t('Didascalia'),
    'description' => t('Div contenuto con stile didascalia'),
    'tips callback' => 'tx_shortcode_element_dida_tips',
    'attributes callback' => 'tx_shortcode_element_dida_attributes',
    'process callback' => 'tx_shortcode_element_dida',
    'default settings' => array(),
  );
  return $shortcodes;
}

In questo primo hook (che si appoggia al modulo Shortcode) definiamo il nostro nuovo shortcode tx_shortcode_dida. Per ogni shortcode che andiamo a definire dobbiamo fornire un titolo, una descrizione, eventuali setting di default e tre funzioni:

  • la funzione definita in tips callback ci servità per mostrare il suggerimento di inserimento sotto all’editor;
  • la funzione definita in attributes callback ci permette di personalizzare il form con i setting che viene mostrato da Shortcode for Wysiwyg;
  • la funzione definita in process callback è il vero motore dell’elaborazione dello shortcode.

Andiamo quindi a vedere nel dettaglio il codice di queste tre funzioni

/**
 * Add custom tip to the editor
 */
function tx_shortcode_element_dida_tips($format, $long){
  return '[ tx_shortcode_dida ]your content here[ /tx_shortcode_dida ]';
}
/**
 * Create the form to help the insertion of the new shortcode
 */
function tx_shortcode_element_dida_attributes($form, $form_state){
  return $form;
}
/**
 * Build the code to extract
 */
function tx_shortcode_element_dida($attrs, $content = null){
  return '<div class="tx_dida"><div class="tx_dida_border"></div>' . $content . "</div>";
}

In realtà c’è ben poco da spiegare. La didascalia (tx_shortcode_element_dida_tips) mostra semplicemente la forma con cui inserire il nostro shortcode. Gli attributi dello shortcode (tx_shortcode_element_dida_attributes) in questo caso non esistono ed il motore dello shortcode (tt_shortcode_element_dida) non fa altro che inserire il contenuto nella struttura che andremo poi a stilizzare ad hoc tramite CSS.

Ma questo era solo un esempio iniziale, senz’altro utile ma senza grosse complicazioni.

Un secondo esempio, includere un post di Facebook

Esempio sicuramente più interessante è quello che ci permette di inserire un post di Facebook nel nostro contenuto. In questo caso vedremo come gestire i parametri dello shortcode e come il tutto viene sfruttato ed integrato. Mi soffermerò ovviamente solo sul codice nuovo e non aggiungerò nulla a quanto detto per l’esempio precedente.

/**
 * Form builder for tx_sc_emb_admin_form form.
 * @see system_settings_form()
 */
function tx_sc_emb_admin_form($form, &$form_state) {
  $form = array();

  //Shortcode for Facebook
  $form['facebook'] = array(
    '#type' => 'fieldset',
    '#title' => t('Facebook settings'),
    '#weight' => 6,
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['facebook']['txsc_emb_facebook_maxwidth'] = array(
    '#type' => 'textfield',
    '#title' => t('Default Facebook post width'),
    '#description' => t('The maximum width in pixels that the embed should be rendered at. This value is constrained to be between 350 and 750.'),
    '#default_value' => variable_get('txsc_emb_facebook_maxwidth', '500'),
  );
  $form['facebook']['txsc_emb_facebook_appid'] = array(
    '#type' => 'textfield',
    '#title' => t('Default Facebook App Id for sharing'),
    '#description' => t('You should create an App before use this plugin. Please insert here the App Id. Is not possible change this value in the shortcode.'),
    '#default_value' => variable_get('txsc_emb_facebook_appid', ''),
  );

  return system_settings_form($form);
}

In questo caso il nostro shortcode richiede alcuni parametri per la configurazione globale, che vengono quindi esposti nel pannello amministrativo.

Qui semplicemente aggiungiamo ad un form gli attributi per il post width e per il nostro AppId di Facebook.

Il tutto viene gestito automaticamente tramite le variable di Drupal.

/**
 * Implementation of hook_shortcode_info().
 * Using same formatting as hook_filter_info()
 */
function tx_shortcode_embedded_shortcode_info() {
  $shortcodes = array();

  $shortcodes['tx_shortcode_facebook'] = array(
    'title' => t('Facebook Post'),
    'description' => t('Add new Facebook embedded post.'),
    'tips callback' => 'tx_shortcode_embedded_facebook_tips',
    'attributes callback' => 'tx_shortcode_embedded_facebook_attributes',
    'process callback' => 'tx_shortcode_embedded_facebook',
    'default settings' => array(),
  );

  return $shortcodes;
}

Di nuovo, il codice di hook_shortcode_info definisce il nostro shortcode per l’editor.

/**
 * Add custom tip to the editor
 */
function tx_shortcode_embedded_facebook_tips($format, $long){
  $output = '[tx_shortcode_facebook maxwidth="300" posturl="tourtools/posts/10152584460538081"][/tx_shortcode_facebook]';
  return $output;
}

Il tip questa volta ci mostra uno shortcode lievemente più complesso in quanto riporta i parametri facoltativi che l’utente finale può impostare (in questo caso la larghezza e l’url del post da includere)

/**
 * Create the form to help the insertion of the new shortcode
 */
function tx_shortcode_embedded_facebook_attributes($form, $form_state){
  $form['maxwidth'] = array(
    '#type' => 'textfield',
    '#title' => t('Post width'),
    '#description' => t('The maximum width in pixels that the embed should be rendered at. This value is constrained to be between 350 and 750.'),
    '#default_value' => variable_get('txsc_emb_facebook_maxwidth', '500'),
    '#states' => array('visible' => array(':input[name="shortcode"]' => array('value' => 'tx_shortcode_facebook'))),
  );
  $form['posturl'] = array(
    '#type' => 'textfield',
    '#title' => t('Facebook URL'),
    '#description' => t('You must use the complete URL of the post (e.g. tourtools/posts/10152584460538081).'),
    '#default_value' => '',
    '#states' => array('visible' => array(':input[name="shortcode"]' => array('value' => 'tx_shortcode_facebook'))),
  );
 
  return $form;
}

Il codice in tx_shortcode_embedded_facebook_attributes definisce invece il form da mostrare da Shortcode for Wysiwyg per facilitare l’inserimento dello shortcode all'utente finale.

Prestiamo attenzione al parametro #states che ci permette di definire quando questo campo è visibile. In questo modo se vi trovate a dover gestire, come nel nostro caso, decine di shortcode eviterete lo sgradevole effetto di visualizzare contemporaneamente tutti i campi di configurazione di ogni singolo shortcode (si, Shortcode for Wysiwyg elabora e mostra tutti i campi insieme).

/**
 * Build the code to extract
 */
function tx_shortcode_embedded_facebook($attrs, $content = null){
  extract(shortcode_attrs(array(
    'maxwidth' => '',
    'posturl' => '',
  ), $attrs));

  //If the needed variables are empty i get the default value
  if($maxwidth == ''){
    $maxwidth = variable_get("txsc_emb_facebook_maxwidth", 500);
  }

  //AppId should be defined outside in a global way
  $appid = variable_get("txsc_emb_facebook_appid", '');

  $finalDiv = "";
 
  if($posturl != '' && $appid != ''){
    $finalDiv ='<div class="social_embed_facebook">
                  <div class="fb-post" data-href="https://www.facebook.com/'.$posturl.'" data-width="'.$maxwidth.'">
                    <div class="fb-xfbml-parse-ignore"></div>
                  </div>
                </div>';
  }

  return $finalDiv;
}

Ed eccoci al motore dell’elaborazione del nostro shortcode. Possiamo notare alcune cose interessanti.

Tramite extract andiamo a ricavare gli attributi passati dal form di configurazione (vi ricordate tx_shortcode_embedded_facebook_attributes, vero?) e ad assegnarli in automatico a delle variabili di comodo.

Il passo successivo ci permette di sovrascrivere la larghezza del post con il valore impostato di default laddove l'utente non abbia inserito un valore ad hoc per questo shortcode e nel leggere la variabile dell'AppId configurata sempre a livello globale.

A questo punto, se sono presenti l'url del post e l'AppId di Facebook, andiamo a comporre il codice standard di Facebook per incorporare il post all'interno del nostro contenuto.

Ma non è finita qui! Infatti questo shortcode richiede anche l'elaborazione di un codice Javascript per funzionare.

Per completezza dell'esercizio riporto il codice per generare ed incorporare nella pagina il Javascript necessario, ma non andrò ad approfondire ne spiegare il codice stesso in quanto viene semplicemente gestito il Javascript standard di Facebook.

Per quanto riguarda invece l'implementazione di hook_views_api e hook_library vi rimando, se dovesse essere necessario, all'interessante articolo A Better Way to Add Javascript and CSS with hook_library() di Eric C. Brown

Tiriamo le somme

Siamo giunti alla fine di questo (lunghissimo) articolo!

Abbiamo visto come sia possibile implementare la tecnica degli shortcode per facilitare l’inserimento all’utente di strutture e codice che potrebbe risultare assai complesso.

Questa tecnica ha ovviamente validità solo in caso di codice che può essere inserito in più punti del testo e, sicuramente, in più pagine del sito.

Spero di avervi convinto nell’utilità di questo strumento. E se non fossi riuscito nel mio intento, giocate a "trova la differenza" con quello che riporto qua sotto ed immedesimatevi nel vostro cliente finale... Cosa preferireste scrivere per gestire i contenuti sul vostro sito Drupal?

[tx_shortcode_dida]Testo visualizzato come didascalia[/tx_shortcode_dida]
<div class="tx_dida">
  <div class="tx_dida_border"></div>Testo visualizzato come didascalia
</div>
[tx_shortcode_facebook maxwidth="300" posturl="tourtools/posts/10152584460538081"][/tx_shortcode_facebook]
<div class="social_embed_facebook">
  <div class="fb-post" data-href="https://www.facebook.com/tourtools/posts/10152584460538081" data-width="300">
    <div class="fb-xfbml-parse-ignore"></div>
  </div>
</div>
<script type="text-javascript">
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/it_IT/sdk.js#xfbml=1&appId=MYAPIKEY&version=v2.0";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, \'script\', \'facebook-jssdk\'));
</script>