lunedì 28 gennaio 2008

Javascript: Oggetto prototype ed ereditarietà

Continuiamo il discorso sulla programmazione a oggetti in Javascript. Come al solito i sorgenti degli esempi che accompagnano l'articolo sono disponibili sul sito (Javascript-Oggetti).

Nell'articolo precedente (Javascript: Esempi di Programmazione a Oggetti) abbiamo utilizzato una funzione costruttore per creare oggetti auto. Supponiamo ora di voler far gestire alla nostra ipotetica applicazione Javascript un oggetto aereo. Ci accorgiamo che la funzione costruttore Veicolo va quasi bene nel senso che attributi quali passeggeri e velocità e un metodo carica() sono appropriati anche per un aereo. Ma cosa succederebbe se volessimo gestire un attributo altitudine ed un metodo decolla()?

Si potrebbe pensare semplicemente di aggiungerli al costruttore

function Veicolo(passeggeri) {
  this.velocita = 0;
  this.passeggeri = 0;
  this.altitudine = 0;
  this.decolla = function() {
    this.velocita = 100;
    this.altitudine = 10;
  }
  /* ... */
}

Non è una buona idea. Una volta modificato il costruttore in questo modo diventa possibile scrivere

var auto = new Veicolo(2);
auto.decolla();

Che chiaramente non ha senso, tranne forse per l'auto di 007.

L'attributo e il metodo aggiunti hanno reso il costruttore Veicolo troppo specifico. In questo modo può essere utilizzato per veicoli volanti, ma non più per veicoli terrestri.

D'altra parte se abbandonassimo l'oggetto veicolo per creare due oggetti nuovi (auto e aereo) con relative funzioni costruttore distinte e separate, dovremmo necessariamente duplicare tutto il codice necessario a gestire gli attributi e metodi comuni (passeggeri, velocità e carica()).

Abbiamo due oggetti con un certo numero di attributi e metodi in comune ed altri individuali e specifici.

Chi è pratico di altri linguaggi di programmmazione potrà riconoscere in questa situazione un caso tipico che può essere risolto sfruttando l'ereditarietà tra classi: creando cioè una classe base, dove vengono definiti gli attributi e metodi comuni ai due oggetti, e classi derivate che, oltre a definire attributi e metodi specifici, ereditano gli attributi e metodi della classe base.

L'oggetto prototype

Tornando a Javascript si è già visto che in questo linguaggio non esistono classi (vedi articolo precedente sopra citato) e gli oggetti sono creati a partire da funzioni costruttore. Per cui come possiamo risolvere la situazione se non è possibile definire classi base e classi derivate?

Ogni funzione in Javascript possiede un attributo prototype che si riferisce ad un oggetto prototype. All'oggetto prototype possiamo aggiungere attributi e metodi come potremmo fare con qualsiasi altro oggetto. Solo che l'oggetto prototype non è come tutti gli altri in quanto la sua funzione è fare da modello (prototipo appunto) per la creazione di altri oggetti.

La cosa in estrema sintesi funziona così. Ogni attributo e metodo aggiunto ad un oggetto prototype viene reso disponibile a tutti gli oggetti creati dalla funzione costruttore a cui l'oggetto prototype è attribuito.

Anche gli oggetti di sistema (come gli array) sono creati da funzioni costruttore che espongono un attributo prototype. Eccone un esempio di utilizzo.

Aggiungeremo all'oggetto Array un metodo sum() che ritorni la somma di tutti gli elementi dell'array ignorando eventuali elementi non numerici (esempio 1 codice dimostrativo).

var a = new Array(3, 2, 6);

Array.prototype.sum = function() {
  var r = 0;
  for(var i = 0; i < this.length; i++) {
    if(typeof(this[i]) == 'number') {
      r += this[i];
    }
  }
  return r;
}
document.write(a.sum()); //Risultato 11

Come si vede anche gli oggetti già creati acquisiscono il nuovo metodo.

Nell'esempio appena visto abbiamo aggiunto un singolo metodo ad un oggetto prototype, ma niente ci vieta di rimpiazzare completamente un oggetto prototype con un nuovo oggetto appositamente creato.

Questo è il risultato. Data una funzione F ( function F(){...} ) ed un oggetto p, se assegnamo p come nuovo prototipo di F (F.prototype=p), gli oggetti che saranno (o sono stati già) creati utilizzando la funzione F come costruttore avranno disponibili, e potranno utilizzare come propri, tutti gli attributi e metodi di p.

Ereditarietà tramite prototype

Ecco quindi come fare a costruire l'oggetto aereo senza riscrivere gli attributi e metodi già definiti in veicolo (esempio 2 codice dimostrativo).

Il costruttore Veicolo già visto nell'articolo precedente (con qualche piccola modifica per eliminare un paio di righe di codice duplicato) è questo

function Veicolo(passeggeri) {
  this.velocita = 0;
  this.passeggeri = 0;
  this.carica = function(passeggeri) {
    if(passeggeri > 0) {
      this.passeggeri += passeggeri;
    }
  }
  this.carica(passeggeri);
}

Creiamo una funzione costruttore con gli attributi e metodi specifici dell'oggetto aereo.

function Aereo(passeggeri) {
  this.carica(passeggeri);
  this.altitudine = 0;
  this.decolla = function() {
    this.velocita = 100;
    this.altitudine = 10;
  }
}

sostituiamo l'oggetto prototype di Aereo con un nuovo oggetto creato con la funzione Veicolo

Aereo.prototype = new Veicolo();

Fatto questo possiamo scrivere

var jumbo = new Aereo(250);
jumbo.decolla();
document.write(jumbo.velocita); // 100
document.write(jumbo.altitudine); // 10
document.write(jumbo.passeggeri); // 250

Si possono notare due cose

  • il metodo decolla() definito nella funzione costruttore Aereo può accedere e modificare l'attributo velocita definito nella funzione costruttore Veicolo.
  • il metodo carica() può essere invocato all'interno della funzione Aereo per inizializzare l'attributo passeggeri.

Quindi ogni oggetto creato da Aereo eredita tramite prototype attributi e metodi definiti nella funzione costruttore Veicolo. Che è quello che ci serviva.

Ripristino constructor nell'oggetto derivato

Ogni oggetto in Javascript possiede un attributo constructor che si riferisce alla funzione costruttore utilizzata per creare l'oggetto. Un effetto collaterale della sostituzione dell'oggetto prototype in una funzione è che in tutti gli oggetti creati utilizzando quella funzione come costruttore, l'attributo constructor si riferirà alla funzione utilizzata per creare l'oggetto che è divenuto il nuovo prototype della funzione.

Ecco perché dopo

Aereo.prototype = new Veicolo();

dobbiamo aggiungere

Aereo.prototype.constructor = Aereo;

altrimenti tutti gli oggetti creati da Aereo avranno l'attributo constructor che si riferisce a Veicolo. Vedi anche esempio 3 del codice dimostrativo.

Ricordatevi di fare questa cosa senza farvi (e soprattutto farmi!) troppe domande e vi troverete sempre bene. Scherzi a parte, mi limito a sottolineare che questa cosa accade, chi avesse la curiosità di sapere perché accade può trovare maggiori dettagli nei riferimenti al termine dell'articolo.

Variabili private

Anche se abbiamo predisposto un metodo carica() l'attributo passeggeri è accessibile pubblicamente per cui niente vieta di scrivere per esempio

jumbo.passeggeri = -7;

Per impedire che all'attributo siano assegnati valori incongrui possiamo modificare il costruttore Veicolo in questo modo (vedi esempio 4 codice dimostrativo).

function Veicolo(passeggeri) {
  this.velocita = 0;
  var p_passeggeri = 0;
  this.carica = function(passeggeri) {
    if(passeggeri > 0) {
      p_passeggeri += passeggeri;
    }
  }
  this.scarica = function(passeggeri) {
    if(passeggeri < 0) { return; }
    p_passeggeri -= passeggeri;
    if(p_passeggeri < 0) {
      p_passeggeri = 0;
    }
  }
  this.numeroPasseggeri = function() {
    return p_passeggeri;
  }
  this.carica(passeggeri);
}

L'attributo passeggeri è stato rimosso. Al suo posto si è dichiarata una variabile privata

var p_passeggeri = 0;

Solo i metodi all'interno di Veicolo, carica(), scarica() e numeroPasseggeri(), possono accedervi. L'unico controllo che viene fatto è che il contatore non diventi mai minore di zero. Potete divertirvi (a dire la verità mi vengono in mente diversi divertimenti più divertenti) a perfezionare i metodi in modo che sia impossibile scaricare o caricare frazioni di passeggero.

Miglioriamo l'applicazione di esempio

Arrivati a questo punto Veicolo contiene un contatore che indica il numero dei passeggeri caricati. Vogliamo rendere l'applicazione di esempio più sofisticata creando una nuova funzione costruttore Persona e modificando il metodo carica() in modo che accetti come argomento un oggetto persona. Il metodo scarica() servirà a far scendere un numero a piacere di passeggeri secondo la regola dell'ultimo a salire è il primo a scendere. Veicolo manterrà al suo interno non più un contatore, ma un array di oggetti persona (i passeggeri) ed avrà un nuovo metodo (listaPasseggeri()) con la funzione di stampare una lista di informazioni sui passeggeri.

Va da sè che il nuovo metodo, come tutti gli altri, sarà ereditato da tutti gli oggetti creati con funzioni derivate da Veicolo, nel nostro caso, per il momento, solo Aereo.

Per prima cosa creiamo la funzione costruttore Persona.

function Persona(nome) {
  this.nome = nome;
}
Modifichiamo Veicolo in questo modo
function Veicolo(passeggero) {
  this.velocita = 0;
  var p_passeggeri = [];
  this.carica = function(passeggero) {
    if(typeof(passeggero) == 'object') { 
      p_passeggeri.push(passeggero);
    }
  }
  this.scarica = function(n) {
    if(n <= 0) { n = 1; }
    if(n > p_passeggeri.length) {
      n = p_passeggeri.length;
    }
    for(var i=0; i < n; i++) {
      p_passeggeri.pop();
    }
  }
  this.numeroPasseggeri = function() {
    return p_passeggeri.length;
  }
  this.listaPasseggeri = function() {
    for(var x=0; x < p_passeggeri.length; x++) {
      document.write('#' + p_passeggeri[x].nome + '<br />');
    }
  }
  this.carica(passeggero);
}

e Aereo così

function Aereo(passeggero) {
  this.carica(passeggero);
  /* il resto non cambia */
}
Aereo.prototype = new Veicolo();
Aereo.prototype.constructor = Aereo;

Sembrerebbe tutto a posto.

In effetti se ci limitiamo a caricare e scaricare passeggeri da un singolo oggetto aereo tutto funziona. Se però creiamo un secondo oggetto ci aspetta una sorpresa.

Consideriamo il codice seguente

var tizio = new Persona('Tizio');
var caio = new Persona('Caio');
var dc9 = new Aereo();
var jumbo = new Aereo();

jumbo.carica(tizio);
dc9.carica(caio);

document.write(jumbo.numeroPasseggeri()); // 2
jumbo.listaPasseggeri(); // #Tizio #Caio
document.write(dc9.numeroPasseggeri()); // 2
dc9.listaPasseggeri(); // #Tizio #Caio

Si capisce chiaramente che un passeggero caricato su uno degli oggetti aereo appare nella lista passeggeri anche dell'altro! I due oggetti hanno l'array (p_passeggeri[]) in comune (vedi anche esempio 5).

Questo dipende dal meccanismo utilizzato per gestire l'ereditarietà in Javascript. Quando scriviamo

Aereo.prototype = new Veicolo();

una singola istanza di un oggetto Veicolo sarà condivisa (tramite prototype) da tutti gli oggetti creati da Aereo.

Per risolvere il problema bisogna modificare il costruttore Aereo in questo modo

function Aereo(passeggeri) {
  Veicolo.call(this,passeggeri);
  this.carica(passeggeri);
  /* il resto invariato */
}

call esegue la funzione costruttore Veicolo nello spazio di visibilità (scope) di this, cioè dell'oggetto aereo che stiamo creando. In questo modo p_passeggeri (e anche il resto) è ricreata nello spazio di visibilità di ogni singola istanza di Aereo (vedi anche esempio nella documentazione di call).

La vecchia chiamata this.carica(passeggeri) in Aereo non serve più perché a questo punto è l'omonimo metodo in Veicolo ad occuparsi dell'inizializzazione dell'array p_passeggeri come è giusto che sia visto che fin dall'inizio di questo lungo discorso l'attributo passeggeri è sempre stato di 'competenza' di Veicolo.

Il tutto può non essere tanto facile da capire, l'importante è essere consapevoli del problema e della soluzione. Confrontare il codice dell'esempio 5 (problema) ed esempio 6 (soluzione) potrà aiutare.

Nei riferimenti trovate anche approcci leggermente diversi al problema, io credo che l'uso di call sia tutto sommato la soluzione più lineare.

Esempio finale

L'esempio 7 del codice dimostrativo mostra un modo in parte diverso di creare gli oggetti derivati: secondo questa impostazione il codice all'interno delle funzioni costruttore viene ridotto al minimo mentre attributi e metodi sono aggiunti all'oggetto prototype del costruttore.

In questo modo, ad esempio, il costruttore Aereo viene scritto così

function Aereo(passeggeri) {
  Veicolo.call(this, passeggeri);
}
Aereo.prototype = new Veicolo();
Aereo.prototype.constructor = Aereo;
Aereo.prototype.altitudine = 0;
Aereo.prototype.decolla = function() {
  this.velocita = 100;
  this.altitudine = 10;
}

Si capisce facilmente che definendo metodi e attributi una volta per tutte nel prototype del costruttore si velocizza e ottimizza il processo di creazione delle singole istanze.

Non è sempre possibile seguire questa strada: i metodi carica(), scarica(), numeroPasseggeri(), listaPasseggeri() rimangono definiti internamente a Veicolo in quanto devono poter accedere alla variabile privata p_passeggeri.

L'esempio mostra inoltre come aggiungere un nuovo attributo al prototype del costruttore e come questo divenga disponibile anche alle istanze degli oggetti già creati. Rimando ai commenti nel codice per i dettagli.

Riferimenti

Siamo alla fine di questa panoramica sulla programmazione a oggetti in Javascript. L'ereditarietà realizzabile nei modi che abbiamo visto è comunemente definita ereditarietà prototipale per distinguerla da quella basata sulle classi propria di altri linguaggi.

Ho lasciato da parte molti dettagli, ma d'altra parte l'intenzione non era quella di fare una trattazione esaustiva di un argomento abbastanza complesso, quanto piuttosto mostrare qualche esempio pratico. Concludo però con alcuni riferimenti utili per chi avessa voglia di approfondire.

giovedì 17 gennaio 2008

Javascript: Esempi di Programmazione a Oggetti

Javascript mette a disposizione una serie di funzionalità e costrutti del linguaggio che consentono agli sviluppatori di progettare e creare applicazioni utilizzando una metodologia di programmazione orientata agli oggetti, in modo certo non identico, ma molto simile a quanto sarebbe possibile fare con linguaggi quali Java, C++ o PHP5.

Parlare qui approfonditamente degli aspetti teorici della programmazione orientata agli oggetti sarebbe lungo e non molto utile in quanto articoli sul tema ne trovate quanti volete (per esempio questo a carattere introduttivo su Wikipedia).

Credo però che sia utile dare degli esempi di codice che aiutino a capire i concetti fondamentali, non solo perché si tratta di un argomento interessante, ma anche perché può servire come introduzione ad altri argomenti di cui ho intenzione di parlare in futuro.

Codice dimostrativo di tutti gli argomenti trattati nell'articolo è disponibile sul sito (Javascript-Oggetti). Scaricato il pacchetto zip potete semplicemente decomprimerlo in una cartella di prova sul vostro sito web o webserver locale e aprire la pagina index.html.

Creare un oggetto in Javascript

Detto nel modo più semplice possibile (e rimandando per approfondimenti all'articolo sopra citato) un oggetto, dal punto di vista che ci interessa, è un'entità che possiede propri attributi o proprietà (dati) e può compiere una serie di azioni predefinite attraverso procedure (metodi) che operano sui suoi dati.

In un oggetto quindi i dati e le procedure che operano sugli stessi sono accorpati in un'entità unica.

Per chi è abituato alla programmazione ad oggetti in altri linguaggi (esempio Java o PHP) è normale considerare gli oggetti come istanze di una classe. In Javascript la situazione è un po' diversa.

Vediamo come creare un semplice oggetto di esempio, veicolo (vedi esempio 1 del codice dimostrativo)

function Veicolo(passeggeri) {
  this.velocita = 0;
  this.passeggeri = 0;
  if(passeggeri > 0) {
    this.passeggeri = passeggeri;
  }
}

var auto = new Veicolo(1);

La prima cosa interessante da notare è che per creare un oggetto siamo partiti dalla definizione di una funzione. Il compito che in altri linguaggi orientati agli oggetti è svolto dalle classi, definire cioè gli attributi e i metodi di un oggetto, in Javascript è svolto dalle funzioni.

Una funzione che viene utilizzata nel modo che abbiamo appena visto per creare un oggetto è detta costruttore.

this all'interno di un costruttore si riferisce sempre a quel particolare oggetto che stiamo creando. Quindi tutto il codice visto sopra crea un nuovo oggetto di nome auto (verrebbe da dire di classe veicolo, ma non sarebbe una terminologia corretta in Javascript) che possiede due attributi: velocità, con valore predefinito impostato a zero, e passeggeri, con valore predefinito impostato da un parametro passato alla funzione costruttore.

Modifica degli attributi di un oggetto

Ogni oggetto possiede una propria copia 'personale' dei valori degli attributi che lo caratterizzano. Nel costruttore, come si è visto, gli attributi possono essere impostati a valori predefiniti. Ma una volta creato l'oggetto i valori dei suoi attributi sono modificabili individualmente e possono assumere valori diversi da quelli di ciascuna copia (o istanza) degli altri oggetti creati dalla stessa funzione costruttore.

Questo concetto si esprime dicendo che gli oggetti, anche quando creati mediante la stessa funzione costruttore, hanno una propria identità.

Proseguendo con l'esempio, (vedi esempio 2 codice dimostrativo)

var auto2 = new Veicolo(1);
auto2.velocita = 2;

In questo modo creiamo un secondo oggetto e impostiamo il valore del suo attributo velocità.

La sintassi per accedere ad un attributo di un oggetto è, come si vede

nome_oggetto.nome_attributo

A questo punto dell'esecuzione lo stato degli oggetti è rappresentato dalla seguente tabella.

L'oggetto auto mantiene il valore degli attributi impostati nel costruttore, l'oggetto auto2 assume il nuovo valore (cioè 2) dell'attributo velocità.

Metodi di un oggetto

Oltre ad attributi un oggetto può possedere metodi. Un metodo è una funzione che opera sugli attributi di un oggetto determinandone il comportamento o le funzionalità.

Possiamo creare un metodo ed assegnarlo ad un oggetto in questo modo (vedi esempio 3 codice dimostrativo)

function carica(passeggeri) {
  if(passeggeri > 0) {
    this.passeggeri += passeggeri;
  }
}

function Veicolo(passeggeri) {
  this.velocita = 0;
  this.passeggeri = 0;
  if(passeggeri > 0) {
    this.passeggeri = passeggeri;
  }
  this.carica = carica;
}

Il metodo carica ha la funzione di imbarcare un certo numero di passaggeri sull'oggetto veicolo.

L'uso di this all'interno di un metodo è analogo a quello visto nella funzione costruttore: si riferisce cioè al particolare oggetto su cui il metodo è invocato.

Quindi la riga

this.passeggeri += passeggeri;

ha questo significato: somma al valore dell'attributo passeggeri dell'oggetto su cui questo metodo è invocato, il valore dell'argomento passeggeri passato al metodo.

La sintassi per invocare un metodo è la seguente

nome_oggetto.nome_metodo(argomenti_metodo);

Un metodo può non avere argomenti e in questo caso il nome del metodo è seguito da una coppia di parentesi vuote ()

var auto = new Veicolo(1);
auto.carica(2);

Dopo l'esecuzione del metodo il valore di auto.passeggeri sarà uguale a 3.

Anche se il codice sopra riportato funziona, non è questo il modo migliore di creare i metodi di un oggetto. Infatti abbiamo posto la funzione carica esternamente e quindi allo stesso livello del costruttore dell'oggetto Veicolo. Questo ci preclude la possibilità di creare un metodo carica per un oggetto diverso perché avremmo un conflitto tra nomi di funzione.

La soluzione consiste nell'incorporare la definizione del metodo all'interno della funzione costruttore in questo modo (vedi esempio 4 codice dimostrativo)

function Veicolo(passeggeri) {
  this.velocita = 0;
  this.passeggeri = 0;
  if(passeggeri > 0) {
    this.passeggeri = passeggeri;
  }
  this.carica = function(passeggeri) {
    if(passeggeri > 0) {
      this.passeggeri += passeggeri;
    }
  }
}

Così facendo possiamo utilizzare senza conflitti carica come nome di metodo per altri oggetti o nostre funzioni.

Questa è giusto una prima infarinatura sulla programmazione a oggetti in Javascript. Quanto detto può essere risultato banale o complicato a seconda del grado di familiarità che si ha con questi concetti. In caso di dubbi i commenti ci sono apposta. L'argomento comunque merita di essere approfondito e lo sarà molto presto.

sabato 5 gennaio 2008

Textpattern: Installare un Template

Continuiamo lo sviluppo del nostro mini-sito con Textpattern. Chi fosse interessato alle 'puntate' precedenti può cliccare l'etichetta Textpattern in fondo a questo post.

L'estetica del sito lascia parecchio a desiderare per cui installeremo un template.

Avvertenza preliminare. Se avete avuto la pazienza di costruirvi il vostro sito di prova pezzo per pezzo seguendo i due articoli precedenti, tenete presente che l'installazione del template sovrascriverà alcuni elementi (stile, pagina e modulo di default) a cui abbiamo già apportato modifiche. Rimetteremo le cose a posto alla fine, però, e questo vale soprattutto per chi avesse continuato lo sviluppo del sito per conto proprio, è bene salvare a parte una copia del sorgente dei vari elementi che vi sarà chiesto di modificare. Si tratta sempre di semplice testo per cui basta fare copia e incolla in un file e salvarlo localmente.

Come template ho scelto Azurio perché a mio parere è piuttosto gradevole pur essendo molto semplice nella struttura.

Lo trovate distribuito insieme a molti altri template sul sito Textgarden.org.

Per prima cosa scaricate il pacchetto azurio.zip e decomprimetelo in una cartella sul vostro PC.

Le istruzioni di installazione sono in un file readme.html. Seguiremo passo-passo queste istruzioni anche se non proprio alla lettera, ma con qualche semplificazione.

Caricare le immagini

Le immagini allegate al template devono essere caricate nel sito Textpattern. Si potrebbe fare questa operazione dall'area di amministrazione di Textpattern (Contenuti -> Immagini). Se si segue questa strada tutti i file saranno rinominati con un indice numerico, il che complica le successive fasi di installazione.

Molto più semplice utilizzare un programma FTP per caricare nella cartella images del vostro sito tutte le immagini in ...\images\ (dove i puntini stanno per il percorso alla cartella sul vostro PC dove avete decompresso il pacchetto zip di installazione del template).

Configurazione fogli di stile (css)

Il template include un foglio di stile (css) che deve essere reso visibile a Textpattern. Effettuate il login all'area di amministrazione e utilizzate le linguette per andare alla pagina

Aspetto -> Stile

1) Il foglio di stile corrente è default. Cancellate il contenuto presente nell'area di testo e inserite il contenuto del file (...\azurio_css.txt). Premete Salva.

Se avete installato Textpattern nella radice del vostro spazio web (cioè il vostro sito è visibile direttamente come www.example.com), per quanto riguarda i css avete finito. Se invece avete installato Textpattern in una cartella (cioè il vostro sito è visibile come, per esempio, www.example.com/textp/), sono necessari alcuni aggiustamenti al percorso delle immagini utilizzate nel foglio di stile. Selezionate lo stile default, trovate la riga

background:#FFFFFF url(/images/azurio-container-bg.gif) top

e modificatela così

background:#FFFFFF url(/textp/images/azurio-container-bg.gif) top

Dove textp deve essere sostituito dal nome della cartella dove avete installato Textpattern.

La stessa procedura va ripetuta per tutte le proprietà background presenti nel css.

Configurazione pagine

Andate alla pagina

Aspetto -> Pagine

1) Accertatevi che il template corrente sia default (la scritta sopra l'area di testo deve essere Stai modificando il template default).

2) Come detto all'inizio, conviene salvare il sorgente della pagina prima di proseguire.

3) Cancellate il contenuto dell'area di testo, aprite il file ...\page.txt, copiatene il contenuto negli Appunti ed incollatelo nell'area di testo. Premete il pulsante Salva.

Configurazione moduli

Il template include diversi moduli che devono essere installati. Rimanendo sempre all'interno dell'area di amministrazione di Textpattern andate alla pagina

Aspetto -> Moduli

Il modulo selezionato dovrebbe essere default.

Anche in questo caso è opportuno fare una copia del contenuto dell'area di testo. Eliminate poi il contenuto presente, copiate e incollate il contenuto del file ...\form_default.txt e premete Salva.

Allo stesso modo rimpiazzate il contenuto dei seguenti moduli (cliccando sul nome del modulo nella colonna di destra per selezionarlo)

  • comments_display con il file ...\form_comments_display.txt
  • comment_form con il file ...\form_comments_form.txt
  • plainlinks con il file ...\form_plain_links.txt

Non dimenticate di salvare ogni modifica.

Aggiustamenti finali

Arrivati a questo punto, chi ha lavorato su una installazione 'pulita' di Textpattern ha concluso il lavoro.

Chi è partito dal sito di esempio creato negli articoli precedenti deve fare qualche aggiustamento.

Avevamo in precedenza (vedi Textpattern: Liste Articoli e Pagine Statiche) seguito la strada di creare un template pagina separato per le cosiddette pagine statiche, nel nostro caso l'articolo della sezione Chi Siamo. L'installazione del template Azurio cambia le cose e ci conviene a questo punto seguire un'impostazione diversa.

Aspetto -> Sezioni

Nella sezione chi-siamo modificate il valore del campo Usa la pagina da static a default

Contenuti -> Articoli

Cliccare il titolo Chi Siamo e nella pagina successiva Opzioni avanzate. Modificate il valore del campo Forza il modulo selezionando dalla lista static_art.

Installando il template abbiamo sovrascritto il foglio di stile di default perdendo le proprietà che gestivano l'allineamento delle immagini allegate agli articoli. Rimettiamo le cose a posto.

Aspetto -> Stile

Alla fine del foglio di stile default aggiungete

.article-image {
  float:left;
  margin: 0 10px 10px 0;
}

Premete Salva.

Per concludere

Il risultato di tutto questo noioso lavoro è visibile nella versione aggiornata del nostro sito di esempio.

Mi sono dilungato in molti dettagli a beneficio degli utilizzatori meno esperti di Textpattern. Infatti le istruzioni che si trovano nei pacchetti di installazione dei template sono spesso scritte avendo in mente utenti che hanno una buona familiarità con questo cms.

Quanto detto può essere utilizzato come riferimento per installazioni di altri template in quanto la procedura è grosso modo sempre questa: si tratta di estrarre dal pacchetto di installazione un certo numero di file di testo con i sorgenti di tutti i template pagina, moduli, fogli di stile e, spesso, plugin necessari, per poi creare gli 'oggetti' corrispondenti uno per uno nell'area di amministrazione di Textpattern.

Le immagini si trasferiscono con FTP o importandole con la funzione di gestione immagini nell'area di amministrazione.

Va detto comunque che tutta la procedura risulta abbastanza macchinosa specialmente per chi è abituato a cms come Drupal o Joomla dove cambiare template è un'operazione che si compie con pochi click di mouse.

In Textpattern è anche piuttosto complicato (anche se certo non impossibile) far convivere più template insieme e passare dall'uno all'altro anche solo per scegliere quello che si preferisce. Questo perchè i template molto spesso creano proprie versioni della pagina, modulo e stile di default e quindi, se si tenta di installare più template sullo stesso sito, si rischia sempre di sovrascrivere un elemento modificato da un template precedente.

Non sono certo difficoltà insormontabili, ma creano qualche complicazione di troppo nella vita spesso già abbastanza incasinata di chi sviluppa siti web.