Come mescolare i siti

Una guida su come utilizzare strumenti quali Diazo e Nginx per mescolare un sito in Plone con altri siti.

Introduzione

Partiamo da quale sia l'esigenza, o use case per i piu' smaliziati: si parte con due siti differenti in tutto, o quasi. L'idea e' che uno dei due debba mascherare l'altro senza modificarlo in alcuna maniera. Il mascheramento riguarda: url, css, intestazione e footer.

Riassumiamo il software utilizzato:

  • Plone: ospita il sito che dovra' mascherare il secondo, utilizza internamente Diazo per lo skinning;
  • Diazo: egg python utile per manipolare dei file html mediante xslt
  • Paster: server python utilizzato da proxy
  • Nginx: webserver

Riguardo al secondo sito, l'unica ipotesi e' che sia accessibile tramite http.

La Skin

Prima di procedere con il resto occorre avere una skin da poter utilizzare con Diazo, per dare uniformita' al tutto.

Server Plone

Utilizzare una skin realizzata per Diazo in plone e' semplice: si possono utilizzare sia dei file zip da caricare mediante l'apposita form, che degli egg fatti apposta da includere nel buildout. Non essendo questo il focus del post, vi rimando alla documentazione in rete. Nel resto della guida, si assume che il server Plone sia in esecuzione ed accessibile.

Server Secondario

Come anticipato, non poniamo vincoli sul tipo di piattaforma su cui e' in esecuzione il secondo sito, non e' infatti nostro interesse cambiarlo in alcuna maniera. Deve solo essere accessibile in modo da poter accedere alle varie pagine richieste. Da tenere presente che qualsiasi sistema di autenticazione utilizzata da questo sito non ha alcuna relazione con quella di Plone. Questa soluzione produce solo una integrazione grafica e non funzionale: non e' quindi un modo per fare SingleSignOn.

Proxy Server con Paster/Diazo

Il primo passo per procedere all'integrazione e' mascherare il secondo sito: cio' puo' essere fatto agilmente con un buildout basato su Paster.

[buildout]
versions=versions
parts =
    lxml
    diazo

[diazo]
recipe = zc.recipe.egg
eggs =
    diazo [wsgi]
    PasteScript

[lxml]
recipe = z3c.recipe.staticlxml
egg = lxml

[versions]

Una volta fatto girare il buildout ed ottenuto un ambiente consono, occorrono due cose: un cartella che contenga il tema ed un file proxy.ini da dare in pasto a Paster. La cartella puo' essere ovunque, basta che il server Paster possa accederci in lettura. Personalmente preferisco metterla all'interno del buildout in modo da avere tutto insieme utilizzando git submodule.

Relativamente al file proxy.ini, non poniamoci troppi dubbi e mettiamolo direttamente nella stessa cartella del file buildout.cfg. Il contenuto del file dovrebbe essere qualcosa del tipo:

[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5000

[composite:main]
use = egg:Paste#urlmap
/static = static
/ = default

[app:static]
use = egg:Paste#static
document_root = %(here)s/miotema

[pipeline:default]
pipeline = theme
           content

[filter:theme]
use = egg:diazo
rules = %(here)/miotema/rules.xml
prefix = /static

[app:content]
use = egg:Proxy#proxy
address = http://urlsecondosito
suppress_http_headers = accept-encoding

 

Da notare i placeholder miotema e urlsecondosito, che vanno naturalmente rimpiazzati con le coordinate opportune.

Se tutto fino qui e' andato bene, lanciando Paster dovremmo ottenere un server in ascolto sulla porta 5000. Il comando e'

$ bin/paster serve --reload proxy.ini

Come prima controprova, aprite il browser e digitate come url localhost:5000. In base alle regole specificate dentro rules.xml potreste vedere cose molto differenti. Rimando ai post competenti le indicazioni su come modificare il file per mascherare il secondo sito. Da tenere presente che la versione di rules.xml che utilizzate per il secondo sito sara' molto probabilmente differente rispetto a quella utilizzata all'interno di Plone, quindi occhio a non confondere le due cose. Se state mettendo in pratica quanto viene esposto, il mio suggerimento e' di ridurre il file ai minimi termini, ovvero lasciare solo i tag rules senza specificare nulla. In questo modo, il proxy non modifichera' alcun contenuto e si vedra' il sito esattamente uguale all'originale.

Nginx

Nginx e' il webserver che si occupera' di deviare le richieste tra il server Plone ed il secondo server. L'idea di fondo e' di filtrare i contenuti in base all'url, per cui un certo sottoalbero degli url verra' rediretto su server secondario, mentre tutto il resto verra' rigirato sul serverl plone. Fin qui tutto semplice.

Vorrei poter dire di utilizzare il server Nginx che preferite, ma purtroppo il modulo xslt non fa il proprio dovere. In accordo con quanto presente sulla documentazione di Diazo, potete optare per una branch di Nginx che trovate all'url

http://html-xslt.googlecode.com

Oppure potete utilizzare quella che trovate qui:

https://bitbucket.org/lrowe/nginx-xslt-html-parser

Il primo url si riferisce ad un progetto che non e' piu' supportato, mentre il secondo url fa riferimento ad un branch di Nginx a cui sono state applicate le stesse patch del progetto precedente. A questo url trovate un thread in merito:

http://forum.nginx.org/read.php?29,223663

Il modo migliore per installare quest'ultima versione e' aggiungerlo al buildout di Diazo, ovvero:

[buildout]
parts = ....
nginx

...
[nginx]
recipe = zc.recipe.cmmi
url = https://bitbucket.org/lrowe/nginx-xslt-html-parser/get/1a21700079c9.tar.gz
extra_options =

extra_options =

--conf-path=${buildout:directory}/etc/Nginx.conf

--sbin-path=${buildout:directory}/bin

--error-log-path=${buildout:directory}/var/log/Nginx-error.log

--http-log-path=${buildout:directory}/var/log/Nginx-access.log

--pid-path=${buildout:directory}/var/Nginx.pid

--lock-path=${buildout:directory}/var/Nginx.lock

--with-http_stub_status_module

--with-http_xslt_module

 

Ovviamente va fatto girare nuovamente il buildout.

Se tutto va come deve (download, compilazione, installazione), dentro la cartella bin ci dovrebbe essere anche il binario nginx, mentre dentro etc si trova la configurazione di nginx. Il file Nginx.conf e' quello che ci serve. In base alle proprie conoscenze si puo' decidere di fare pulizia. La sezione a cui si fara' riferimento da qui in poi e' server, punto in cui qui dovete abilitare l'opzione ssi:

...
server { 
listen ...;
server_name ...;
ssi on;
...
}
...

Questa opzione abilita Nginx ad analizzare i file in transito e fare il replace delle direttive SSI. Cio' e' necessario in quanto il proxy Paster non puo' sostituire le parti delle pagine prendendole da altri siti, ma puo' solo impaginare un sito mediante un tema, quindi il lavoro di sostituire parti del sito in Plone dentro le pagine del secondo sito deve essere fatto da Nginx.

Sempre nella sezione server del file Nginx.conf occorre modificare la direttiva location / per far redirigere ogni url su Plone, quindi:

...
location / {
    proxy_pass http://localhost:8080/...../VirtualHostRoot/;
}
...

Vanno poi aggiunti gli url che redirigono su Paster:

...
location /static {
    rewrite /static/(.*)$ /$1 break;
    proxy_pass http://localhost:5000/; 
}

location /secondosito {
    rewrite ^/secondosito(.*)$ /$1 break;
    proxy_pass http://localhost:5000/ ;
}
...

Infine occorre gestire gli url presenti nelle direttive SSI. In particolare, per come vengono utilizzati in questo particolare caso, gli url saranno del tipo:

 http://localhost/unapagina?;filter_xpath=unaespressionexpath

In casi come questo, Nginx deve prendere in carico la gestione del parametro filter_xpath, fornendo come risultato solo la porzione di pagina identificata dall'xpath. La documentazion di Diazo propone la seguente soluzione:

...
#sempre dentro server {
if ($args ~ "^(.*);filter_xpath=(.*)$") {
    set $newargs $1;
    set $filter_xpath $2;
    rewrite    ^(.*)$    /_include$1?$newargs?;
}

location ^~ /_include {
    # Restrict _include (but not ?;filter_xpath=) to subrequests
    internal;
    rewrite    ^/_include(.*)$   /VirtualHostBase/.../VirtualHostRoot/$1    break;
    proxy_pass http://127.0.0.1:8080/;
    # Protect against infinite loops
    proxy_set_header X-Loop 1$http_X_Loop; # unary count
    proxy_set_header Accept-Encoding "";
    error_page 500 = @include500;
    if ($http_X_Loop ~ "11111") {
        return 500;
    }
    xslt_html_parser on;
    xslt_stylesheet /daqualcheparte/filter_html.xsl
        xpath=$filter_xpath;
    xslt_types text/html;
}
...

Da notare il condizionale che si occupa di fare una rewrite solo nel caso in cui venga richiesta una risorsa da filtrare mediante xpath, redirigendo la richiesta su un secondo url. Questo tipo di soluzione serve a scindere i casi in cui applicare un filtro mediante xsl dai casi in cui si puo' restituire la pagina intera.

Una breve nota va fatta per il file filter_html.xsl: non e' necessario scriverlo ex novo, in quanto e' presente dentro gli egg di Diazo, e quindi puo' essere preso li.

Se provate ad utilizzare questa configurazione con Nginx preso dalla repository ufficiale o dalla distribuzione, vedrete che il parametro xslt_html_parser non viene gestito dal modulo, pre cui produce un errore.

A questo punto non resta che avviare Nginx e vedere l'effetto che fa. Le prove da fare sono due:

  • accedere a http://localhost per vedere se plone risponde correttamente;
  • accedere a http://localhost/secondosito per vedere se il secondo sito e' visibile correttamente.

Se tutto e' andato bene, potete procedere con il setup delle rules per il secondo sito.

Skinning

Torniamo a Diazo, quello configurato insieme a Paster, ed al suo file rules.xml. Qui potete sbizzarrirvi come preferite a montare la grafica del tema sul secondo sito. Capita che alcune parti dipendano pero' dal sito in Plone, ad esempio l'header o il footer. In questo caso la soluzione e' utilizzare SSI nel seguente modo:

   
...
<replace theme="//*[@id='portal-header']"
         content="//*[@id='portal-header']"
         href="/apageonplone"
         method="ssiwait"
     />
...

Come si puo' intuire dall'xpath, questa regola rimpiazza il tag portal-header del tema con lo snippet html preso non dal secondo sito, ma da apageonplone che si trova in Plone. La direttiva href gioca il ruolo di depistare le operazioni dalla pagina corrente, redirigendo il tutto all'esterno. La direttiva method e' necessaria ed utili allo scopo: qui si sta dicendo a Diazo che la apageonplone sara' trovata e manipolata da qualcun'altro, nel nostro caso Nginx.

Conclusione

Se tutto e' stato eseguito a dovere, dovreste trovarvi nella condizione di avere sotto un unico domino due siti che di base potrebbero essere molto diversi. Nel nostro esempio abbiamo mostrato come replicare l'header delle pagine preso dal sito in produzione, ma tale operazione puo' essere replicata per ogni sezione della pagina, a patto che ci sia un xpath in grado di identificarla.

Commenti finali

Prima di concludere vorrei parlare ancora di Nginx. Durante i test, ho riscontrato che anche la distribuzione standard funziona, a patto di utilizzare alcuni xpath. In pratica nessun xpath basato sul nome del tag (es /html) funziona. I problemi si hanno quando si prova a prendere tutto il tag head, che non ha altro modo di indirizzamento se non /html/head o //head. In entrambi i casi il filtro non trova nulla.

Un altro commento si potrebbe fare in merito alle regole di Nginx per gestire xpath, che danno l'idea di giocare un po' troppo con le rewrite. Sarebbe quindi intereassante vedere se ci sono possibili migliorie.

Share this on

Share |

On same topics

Commenti

comments powered by Disqus