Gli argomenti trattati fanno riferimento alla versione 6.x di Drupal
I nodi contengono il contenuto primario del sito.
L'elenco di tutti i nodi inseriti nel sistema è contenuto nella tabella NODE.
Le informazioni sono lette e codificate dal sistema, mediante la funzione menu_execute_active_handler(), descritta in Decodifica URL. La funzione individua la page callback e, tramite essa, torna la stringa del contenuto codificato.
Per i nodi sono disponibili due page callback:
La prima pagina del sito mostra generalmente più di un nodo. Questi sono i nodi a cui è stato assegnato l'attributo di pubblicazione "promosso in prima pagina". Per selezionare tali nodi, la funzione node_page_default() esegue la seguente query sulla tabella NODE:
SELECT n.nid, n.sticky, n.created FROM node n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC
Dalla query si vede che le condizioni affinché un nodo venga selezionato sono:
L'ordine di visualizzazione dei nodi è fornito invece dal valore sticky:più è alto, più il nodo salirà nell'elenco. A parità di valore l'ordine è dato dalla data di creazione. I nodi più recenti , sono visti per prima.
Il valore di sticky normalmente vale zero. Volendo quindi mostrare un nodo sempre per primo, indipendentemente dalla data di creazione, basterà assegnare un valore >0 al nodo, ad esempio 100.
Individuati i nodi da visualizzare , la funzione node_page_default(), inizia un ciclo di lettura e visualizzazione di tutti i nodi. Questo viene eseguito all'interno del ciclo contenuto nella funzione node_page_default().
<?php
function node_page_default() {
......
$output = '';
$num_rows = FALSE;
while ($node = db_fetch_object($result)) {
$output .= node_view(node_load($node->nid), 1);
$num_rows = TRUE;
}
// Il valore 1 in node_view indica che si tratta del contenuto teaser
.....
?>All'interno del ciclo sono visibili le due funzioni di Drupal per il caricamento del nodo node_load() e per la visualizzazione del nodo node_view(). Al termine la stringa $output conterrà tutti i contenuti dei nodi da visualizzare in prima pagina.
Se la pagina richiesta non è la home page, ma un singolo contenuto, Drupal seleziona la page callback node_page_view(). Poichè la richiesta di visualizzazione è di un particolare nodo (ad esempio http://miosito.com/node/100), Drupal carica prima il nodo richiesto mediante la funzione node_load() e successivamente chiama la node_page_view() passando le informazioni sul nodo ($node) lette e un identificativo ($cid) che, se non nullo, indica che la pagina del nodo è già presente nella cache.
La funzione node_page_view() imposta il titolo della pagina (drupal_set_title(check_plain($node->title))) e chiama la funzione node_view($node, FALSE, TRUE). Il terzo parametro a TRUE indica che Drupal deve visualizzare l'intero contenuto del nodo.
In definitiva abbiamo visto che le due funzioni di base utilizzate da Drupal sia per la visualizzazione della home page sia per la visualizzazione di un singolo contenuto sono sempre node_load() e node_view().
Il funzionamento di queste due funzioni e quindi la logica del caricamento e della visualizzazione dei nodi è descritto nei successivi paragrafi: Caricamento, Visualizzazione.
node_page_default()
node_page_view()
node_load()
node_view()
node_invoke()
node_invoke_api()
La lettura del contenuto del nodo, è eseguita mediante la funzione node_load($nid) a cui è passato il valore dell'identificativo univoco del nodo <nid> da caricare. Al suo interno viene generata la query indicata, che usa tre tabelle:
Query di selezione
SELECT n.nid, n.type, n.language, n.uid, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, n.tnid, n.translate, r.vid, r.uid AS revision_uid, r.title, r.body, r.teaser, r.log, r.timestamp AS revision_timestamp, r.format, u.name, u.picture, u.data FROM node n INNER JOIN users u ON u.uid = n.uid INNER JOIN node_revisions r ON r.nid = n.nid AND r.vid = <n.nid> WHERE n.nid = <n.nid>
Dala funzione node_load() torna un oggetto ($node) contenente le informazioni lette mediante la query.
In particolare i valori $node->body e $node->teaser conterranno rispettivamente contenuto e sommario del nodo.
Oggetto $node generato dalla funzione.
(ND=da tabella NODE; NR da tabella NODE_REVISIONS; US da tabella USERS)
| $node->nid | ND | identificativo univoco del nodo |
| $node->language | ND | lingua del contenuto:'it' , 'en', 'fr', … |
| $node->type | ND | tipo del nodo. Tabella di codifica NODE_TYPE |
| $node->uid | ND | identificativo utente del proprietario del nodo |
| $node->status | ND | =0 non visibile; =1 visibile |
| $node->created | ND | timestamp della data di creazione del nodo |
| $node->changed | ND | timestamp della data di modifica del nodo |
| $node->comment | ND | identificativo utente dell'ultimo commento |
| $node->promote | ND | =1 il nodo è visualizzato in prima pagina |
| $node->moderate | ND | =1 il nodo deve essere controllato prima della pubblicazione |
| $node->sticky | ND | numero d'ordine del nodo nella prima pagina |
| $node->tnid | ND | set ID traduzione (da capire meglio) |
| $node->translate | ND | Booleano.Indica se la traduzione deve essere aggiornata |
| $node->sticky | ND | numero d'ordine del nodo nella prima pagina |
| $node->vid | NR | identificativo del numero di revisione |
| $node->uid | NR | identificativo utente del proprietario del nodo |
| $node->title | NR | titolo del nodo per la revisione corrente |
| $node->body | NR | contenuto del nodo per la revisione corrente |
| $node->teaser | NR | sommario del nodo per la revisione corrente |
| $node->log | NR | Messaggio di log contenente le modifiche eseguite |
| $node->revision_timestamp | NR | data di modifica del nodo |
| $node->format | NR | formato del nodo da tabella FILTERS_FORMAT |
| $node->name | US | nome utente |
| $node->picture | US | percorso all'immagine utente |
| $node->data | US | serializzazione di un array che rappresenta i campi dela form utente |
Oltre alla lettura delle informazioni contenute specificatamente nelle tabelle NODE e NODE_REVISIONS, Drupal consente di aggiungere ulteriori informazioni ai nodi e quindi all'oggetto $node.
Potremmo decidere di definire un nuovo tipo di nodo con informazioni aggiuntive salvate in una tabella separata.
Pensiamo ad esempio ad una rubrica telefonica. Il nominativo e l'identificativo univoco potrebbero essere rispettivamente il campo <title> e il campo <nid> della tabella NODE, mentre tutte le informazioni aggiuntive come l'indirizzo, il telefono, l'email etc, potrebbero essere memorizzate in una tabella separata.
Si pone quindi il problema di leggere queste ulteriori informazioni associate ai nodi.
Drupal consente di leggere queste extra informazioni implementando funzioni particolari che gestiscano queste situazioni.
Dobbiamo distinguere due casi:
Nella tabella NODE_TYPES sono elencati il tipo di nodi definiti nel sistema. Tra questi sono presenti i nodi principali, forniti con il sistema, quali: page, book, story etc.
Dalla stessa tabella vediamo che il campo <module> ci dice da quale modulo sono gestiti i nodi.
Ora supponiamo di aggiungere il nuovo tipo di nodo "rubrica" e che questo tipo di nodo sia gestito dall'omonimo modulo con tutte le sue funzioni implementate in rubrica.module. Ora quando Drupal leggerà un nominativo dal nodo rubrica, nominativo e identificativo saranno letti come indicato nel precedente paragrafo, mentre l'ulteriore contenuto sarà letto chiamando la funzione rubrica_load($node). La funzione deve tornare un array associativo costituito dalle coppie chiave=valore. Un esempio di semplice codice è il seguente:
<?php
function rubrica_load($node) {
// rid = identificativo nominativo ;
$r = db_fetch_object(db_query('SELECT telefono, indirizzo,citta FROM {rubrica} WHERE rid = %d ', $node->nid));
info = array();
info['telefono'] = $r->telefono;
info['indirizzo'] = $r->indirizzo;
info['citta'] = $r->citta;
return $info;
}
?>All'oggetto $node originale, Drupal aggiungerà le informazioni:
| $node->telefono |
| $node->indirizzo |
| $node->citta |
La funzione è chimata all'interno di node_load() mediante node_invoke($node,'load');
Attenzione:Nel caso il modulo di gestione sia quello di default node, il nome della funzione deve essere node_content_load() e non node_load() come dovrebbe.
In questo caso viene chiamata una funzione per avvisare altri moduli che si sta caricando un nodo.
Infatti la funzione non appartiene al modulo che gestisce il tipo di nodo come nel caso precedente , ma ad altri moduli attivi.
A questo punto la funzione chiamata potrebbe compiere due diverse operazioni:
In entrambi i casi possono dunque essere aggiunte o meno extra informazioni all'oggetto $node.
La funzione da implementare deve chiamarsi nomemodulo_nodeapi($node,$opz).
A questa funzione è passato l'oggetto $node e il valore $opz='load. che indica l'operazione che si sta eseguendo sul nodo .
Un esempio di semplice codice, nel caso in cui venga aggiunta informazione a $node, è il seguente:
<?php
function rubrica_nodeapi($node,$opz) {
switch (
$opz) {
case 'load':
info = array();
info['telefono'] = $r->telefono;
info['indirizzo'] = $r->indirizzo;
info['citta'] = $r->citta;
return $info;
break;
default:
break;
}
?>La funzione è chiamata all'interno di node_load() mediante node_invoke_nodeapi($node,'load');
La funzione node_load() per ogni nodo letto chiama tutte le funzioni implametate in questo modo.
Abbiamo visto che la funzione node_load() insieme ad altre funzioni di integrazione, genera l'oggetto $node con tutte le informazioni sul nodo e, tra queste, quelle sul corpo ($node->body) e sul sommario ($node->teaser). Drupal deve ora leggere queste informazioni,e prepararle alla visualizzazione finale. La fase di preparazione del contenuto usa quindi la funzione node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE). Il parametro $node è l'oggetto nodo; $teaser indica se visualizzare o meno il sommario; $page indica se visualizzare o meno l'intero contenuto del nodo; $links indica se visualizzare o meno i links associati al nodo.
Ma perchè è necessaria la fase di preparazione dei contenuti? Ci sono diversi motivi, ad esempio:
In questa fase Drupal genera l'array $node->content[<campo>] che contiene tutti i contenuti del nodo, sia del campo body sia di tutti i campi aggiunti dai moduli esterni a quella tipologia di nodo.
Vediamo quindi quali sono i passi che portano alla definizione del contenuto da visualizzare.
Per fare questo di seguito è riportato integralmente il codice della funzione node_view()
<?php
function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
$node = (object)$node;
// ############## FASE 1
$node = node_build_content($node, $teaser, $page);
// ############## FASE 2
if ($links) {
$node->links = module_invoke_all('link', 'node', $node, $teaser);
drupal_alter('link', $node->links, $node);
}
// Set the proper node part, then unset unused $node part so that a bad
// theme can not open a security hole.
// ############## FASE 3
$content = drupal_render($node->content);
if ($teaser) {
$node->teaser = $content;
unset($node->body);
}
else {
$node->body = $content;
unset($node->teaser);
}
// ############## FASE 4
// Allow modules to modify the fully-built node.
node_invoke_nodeapi($node, 'alter', $teaser, $page);
// ############## FASE 5
return theme('node', $node, $teaser, $page);
}
?>La funzione comprende cinque fasi:
1.Costruzione del contenuto
Per prima cosa la funzione node_build_content (costruzione del contenuto) genera l'array $node[content].
2. Costruzione dei links del contenuto
Se richiesti i links nel contenuto, Drupal chiama tutte le funzioni dei moduli esterni che implementano la funzione nomemodulo_link($node,$teaser) e successivamente consente di modificare i links costruiti chiamando la funzione, se esistente, nomemodulo_tiponodo_alter(). Ad esempio avendo un tipo nodo rubrica e gestito dal modulo node, la funzione si chiamerà node_rubrica_alter().
3. Rendering del contenuto
Drupal esegue il rendering dei campi definiti in $node->content. Il risultato è una stringa che viene copiata in $node->body o $node_teaser in funzione dei valori passati in $page e $teaser.
4. Modifica globale del contenuto
Qui Drupal dà l'ultima possibilità di modificare le informazioni del nodo, contenuto e links inclusi, prima di applicare il tema corrente e di visualizzare il nodo.i
5. Tematizzazione del contenuto
Al termine il contenuto costruito viene tematizzato per la definitiva visualizzazione. Per un nodo generalmente è usato il template node.tpl.php.
Avvia la costruzione del contenuto del nodo mediante la node_build_content($node, $teaser, $page);
Qui, la preparazione dell'array $node[content] può avvenire mediante due funzioni:
1. Una funzione personalizzata
2. La funzione standard node_prepare()
Drupal legge il tipo di nodo corrente ($node->type) e dalla tabella NODE_TYPE ricava il modulo che gestisce il tipo di nodo, che supponiamo abbia il nome nomemodulo.
Cerca quindi la funzione nomemodulo_view($node, $teaser, $page) e se esiste la chiama.
Nel caso il nodo sia gestito dal modulo "node" la funzione si dovrebbe chiamare node_view, ma poichè questa funzione è quella che stiamo descrivendo, solo per questo caso la costruzione del nome della funzione non segue la regola generale, ma si dovrà chiamare node_content_view() (che comunque non esiste).
La costruzione dell' array $node->content[] per tutti i campi del nodo è quindi totalmente affidata alla funzione personalizzata.
Se non è fornita la funzione personalizzata , Drupal chiama la sua funzione node_prepare()
Qui viene elaborato il contenuto del corpo (body) o del sommario (teaser) del nodo applicando i fitri impostati tramite la funzione check_markup(), di cui parleremo in altra parte. Inoltre imposta il flag $node->readmore= True/False in funzione del valore di $teaser/$page. Al termine è generato il primo pezzo dell'array node->content e precisamente:
node->content['body']['#value'] che contiene il valore del corpo o del sommario del nodo,
node->content['body']['#weight']=0 posizione del corpo del nodo in fase di visualizzazione.
Si pone ora il problema di generare il contenuto di tutti quei campi non standard del nodo, aggiunti eventualmente da altri moduli. Ad esempio se è stato generato un nuovo tipo di nodo usando il modulo CCK, il nodo conterrà campi aggiuntivi che devono essere gestiti dal modulo CCK. Per fare questo Drupal esegue tutte le funzione nomemodulo_nodeapi($node,'view', $teaser, $page).
Gli array node->content['nome campo'] che vengono creati possono avere strutture complesse e non omogenee.
Ad esempio per un campo CCK immagine la struttura che si ottiene è la seguente
$node[content][field_immagine] Array [6] field Array [11] #type content_field #title immagine #field_name field_immagine #access true #label_display above #node stdClass #teaser 1 #page false #context teaser #single true items Array [1] 0 Array [7] #item Array [12] fid 20 list 0 data Array [0] uid 3 filename foto.jpg filepath sites/files/foto.jpg filemime image/jpeg filesize 123453 status 1 timestamp 1265803634 nid 7 #delta 0 #weight 0 #theme filefield_formatter_default #field_name field_immagine #type_name nuovotipo #formatter default #node stdClass #weight 1 #post_render Array [1] 0 content_field_wrapper_post_render #field_name field_immagine #type_name nuovotipo #context teaser
All'oggetto $node sono associati i Links di navigazione del nodo che , generalmente, compaiono al termine del sommario di un nodo in prima pagina. Se presenti sono inseriti nell' array $node->links. Drupal consente di generare l'array $node->links chiamando due funzioni personalizzate:
- nomemodulo_link($type, $node, $teaser = FALSE)
Dove:
$type è il tipo di nodo (ad esempio 'node'),
$node l'oggetto in cui inserire l'array del link,
$teaser indica se il nodo è in prima pagina o meno.
La funzione è chiamata per tutti i moduli che la implementano. Ad esempio node_link(), comment_link() taxonomy_link() etc.
La funzione deve tornare un array contenente le componenti caratteristiche della funzione di costruzione del link: l().
Come esempio di implementazione riporto integralmente la funzione node_link().
<?php
function node_link($type, $node = NULL, $teaser = FALSE) {
$links = array();
if ($type == 'node') {
if ($teaser == 1 && $node->teaser && !empty($node->readmore)) {
$links['node_read_more'] = array(
'title' => t('Read more'),
'href' => "node/$node->nid",
// The title attribute gets escaped when the links are processed, so
// there is no need to escape here.
'attributes' => array('title' => t('Read the rest of !title.', array('!title' => $node->title)))
);
}
}
return $links;
}
?>Come si vede , Il valore di ritorno è l'array $links che sarà aggiunta agli array già definiti. L'oggetto nodo non deve quindi essere modificato all'interno della funzione.
- nomemodulo_link_alter(&$data)
Dopo che l'intero array dei link è stato costruito, Drupal da una ultima opportunità di modificarlo chiamando ancora per tutti i moduli le funzioni nomemodulo_link_alter() passando in &$data il riferimento il valore $node->links.
Il rendering del contenuto (generazione del codice HTML definitivo) avviene mediante la funzione drupal_render(&$elements) chiamata ricorsivamente su tutti i campi del nodo. Il parametro $elements corrisponde all'array precedentemente generata $node->content[]. La funzione torna la stringa $content contenente tutto il contenuto del nodo pronto per essere visualizzato. Il contenuto è copiato in $node->body o in $node->teaser a seconda dei valori $teaser e $page passati alla funzione node_view(). In $node , la variabile in cui non è copiato il contenuto è eliminata da Drupal.
Al''interno della funzione drupal_render(), Drupal ci consente di modificare il contenuto sia prima di eseguire il rendering sia dopo averlo eseguito. Vediamo come.
- Personalizzazione mediante $node->content['#pre_render']
Prima di eseguire il rendering del contenuto, Drupal chiama tutte le funzioni eventualmente definite nell'array $node->content['#pre_render']. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa tutta l'array $node->content precedentemente generata.
- Personalizzazione mediante $node->content[<campo>]['#pre_render']
Il valore [#pre_render] può essere assegnato ad ogni campo (Es.: $content[body][#pre_render]) per chiamare una o più funzioni da eseguire per il particolare elemento. In questo caso è passato alla funzione il solo elemento $content[<campo>].
- Personalizzazione mediante $node->content['#post_render']
Dopo aver eseguito il rendering del contenuto, Drupal chiama tutte le funzioni eventualmente definite nell'array $node->content['#post_render']. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa la stringa $content contenente il rendering del contenuto e l'array $node->content.
- Personalizzazione mediante $node->content[<campo>]['#post_render']
Il valore [#post_render] può essere assegnato ad ogni campo (Es.: $content[body][#post_render]) per chiamare una o più funzioni da eseguire per il particolare elemento. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa la stringa $content contenente il rendering del contenuto e l'array $node->content[<campo>].
- Personalizzazione mediante ['#prefix'] e ['suffix']
Il rendering finale di ogni singolo elemento del contenuto viene sempre racchiuso tra i valori #prefix e #suffix forniti per quell'elemento come vediamo dal codice stesso della funzione drupal_render(&$elements)
<?php
function drupal_render(&$elements) {
......
$prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
$suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
return $prefix . $content . $suffix;
...
}
?>In questa fase, l'oggetto $node contiene il rendering del contenut , l'array dei links se presenti e quanto modificato o aggiunto dai moduli che implementano la funzione nomemodulo_nodeapi($node,'view', $teaser, $page).
Qui il nodo può essere ulteriormente modificato prima che sia applicato il tema corrente, implementando nei moduli la funzione nomemodulo_nodeapi($node,'alter', $teaser, $page).
Al nodo è applicata la tematizzazione mediante la funzionetheme('node', $node, $teaser, $page) . Generalmente per tematizzare un nodo viene usato il file template node.tpl.php. Di questo ne parleremo in altra parte.
Drupal durante le operazioni di caricamento, inserimento, aggiornamento, cancellazione e visualizzazione dei nodi , consente allo sviluppatore di eseguire funzioni personalizzate per alterare la modalità operativa standard.
Indichiamo con:
Per ogni tipo di operazione, la sequenza con cui sono descritte le funzioni coincide con quella di esecuzione.
Di seguito riportiamo le funzioni che , se esistenti, sono chiamate da Drupal nei diversi contesti.
Implementata nel modulo che gestisce il tipo di nodo, consente di caricare dei campi aggiuntivi all'oggetto nodo.
Consente a ogni modulo che la implementa di aggiungere ulteriori campi all'oggetto nodo non definiti nella tabella NODE.
Implementata nel modulo che gestisce il tipo di nodo, se presente sosituisce la funzione node_prepare() che è responsabile della generazione dell'array $node->[content].
Consente a ogni modulo che la implementa di preparare alla visualizzazione i campi aggiunti al nodo.
$type è il tipo di nodo (ad esempio 'node'),
Consente a ogni modulo che la implementa di aggiungere links da visualizzare nel nodo corrente.
Consente a ogni modulo che la implementa di modificare l'intero array dei links da visualizzare nel nodo corrente.
Consente a ogni modulo che la implementa di modificare l'intero oggetto $node prima che il nodo sia passato alla funzione theme('node', $node).