<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://blog.redturtle.it/tutorial/RSS">
  <title>Tutorial</title>
  <link>http://blog.redturtle.it</link>

  <description>
    
      
    
  </description>

  

  
            <syn:updatePeriod>daily</syn:updatePeriod>
            <syn:updateFrequency>1</syn:updateFrequency>
            <syn:updateBase>2012-05-08T16:02:35Z</syn:updateBase>
        

  

  <image rdf:resource="http://blog.redturtle.it/logo.png"/>

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://blog.redturtle.it/how-to-automatically-refresh-your-database-in-silent-mode"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/pudb-ovvero-come-ho-imparato-velocizzare-mio-debug"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/quando-la-sicurezza-in-plone-e-importante-reindexobjectsecurity"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/e-uscito-il-buildout-2.0-posso-rilanciare-il-mio-buildout"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/merging-odf-files-using-xdocreport-and-xpages"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/creazione-plugin-gestire-gruppi-virtuali-plone"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/migrating-dexterity-items-to-dexterity-containers"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/migrazione-plone-4-tips-and-tricks"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/come-gestire-e-distribuire-i-vostri-prodotti-plone"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/caution-2013-meteor-will-hit-the-world-wide-web"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/buildout-plone-vs-forefront-tmg-using-cntlm"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/how-to-write-custom-plugin-for-nagios-and-check_mk"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/lotus-domino-twitter-way-for-mobile-approach"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/internazionalizzazione-javascript-plone"/>
      
      
        <rdf:li rdf:resource="http://blog.redturtle.it/content-type-products-contentmigration"/>
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://blog.redturtle.it/how-to-automatically-refresh-your-database-in-silent-mode">
    <title>How to automatically refresh your database in "silent mode"</title>
    <link>http://blog.redturtle.it/how-to-automatically-refresh-your-database-in-silent-mode</link>
    <description>Break away from dependence on the Lotus Notes client to update your database: an easy tutorial to get applications with the latest update every morning</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><strong>Scenario</strong>: a development server, a release server and many production customers' servers: nothing more classic.</p>
<p class="breakText" id="_mcePaste">Every time a change is deployed on the release server, the best thing would be to "push" it automatically on production server, or as soon as possible (at least).</p>
<p> </p>
<p> </p>
<p></p>
<a name="anchor-breaktext"></a><div class="breakText">We know that Lotus Domino provides the "Refresh design tool", but it requires a Lotus client installed and open, and a manual operation.</div>
<p class="breakText">Maybe you can work with replicas of the template, but, in any case, the "optimum" would be:</p>
<div id="_mcePaste"></div>
<div id="_mcePaste">
<ul>
<li>break away from the dependence of the Lotus Notes client</li>
<li>schedule the operation in automatic mode.</li>
</ul>
</div>
<p>So what?</p>
<h2>Many solutions found over the Web, but...</h2>
<p>As reported in this <a class="external-link" href="http://www-10.lotus.com/ldd/nd6forum.nsf/55c38d716d632d9b8525689b005ba1c0/6128e935dae691d580256df9003fb5a4?OpenDocument">thread</a>, automatic refresh design is something desired, perhaps to have something scheduled (agent), but even if low level Notes API are provided, it always requires an opened client and, in this case, a Windows client (API uses <i>dll</i>)!</p>
<blockquote class="pullquote"><i>Typically </i><i>'load convert' command is used for mail database</i></blockquote>
<p>I remembered something about notes mail database administration, and a very cool stuff to upgrade mail templates, with this command:</p>
<pre>load convert -n maildb.nsf * template.ntf</pre>
<p>So, why not apply the same method to <strong>not-mail</strong> database?</p>
<p>I've investigated a lot, I've also opened a discussion on <a class="external-link" href="http://stackoverflow.com/questions/14998328/mail-conversion-utility-of-a-non-mail-database-using-remote-server">Stackoverflow</a> and many authoritative people (<a class="external-link" href="http://www.poweroftheschwartz.com/">Richard H. Schwartz</a>, <a class="external-link" href="http://per.lausten.dk/blog/">Per Henrik Lausten</a>) answered me about different approaches, but I think that "load convert way" could be a good solution.</p>
<p>Be careful! If you try this, you can run into some problems: below I'll try to answer at the most commons I've experienced, and steps to resolve them.</p>
<h3>Q:</h3>
<p>What about if templates are on remote server?</p>
<h3>A:</h3>
<p>It's possible to directly refer the remote template, using this syntax:</p>
<pre>load convert -n mydatabase.nsf * remoteserver!!template.ntf</pre>
<p>where <i><strong>remoteserver </strong></i>is abbreviated name (e.g. <i>removeServerName/OrgUnit/Org</i>).</p>
<p>NOTICE: database and remote template could also be located in subfolder, so you can use:</p>
<pre>load convert -n dir1/dir2/mydatabase.nsf * remoteserver!!dir3/dir4/template.ntf</pre>
<p> </p>
<h3>Q:</h3>
<p>When I send the command, I obtain this error: <i>'Mail Upgrade Failed: Unable to open design template file ......Unable to find path to server'</i>"</p>
<h3>A:</h3>
<p>On production server, you need to create a connection to the release server, as shown in pictures:</p>
<p><img alt="connections" class="image-inline" src="http://blog.redturtle.it/uploads/copy_of_connections.JPG" title="connections" /></p>
<p> </p>
<h3>Q:</h3>
<p>After the connection is done, I obtain this error: 'The Address Book does not contain a cross certificate capable of validating the public key'</p>
<h3>A:</h3>
<p>A cross-certification between organizations is needed, in both directions (release server to production server and vice-versa), using <i>server.id </i>and <i>cert.id.</i></p>
<h2>Ready to go!</h2>
<p>All the prerequisites for a correct communication are made.</p>
<p>Now is possible to schedule the refresh command whenever you want, in your preferred way, for example:</p>
<ul>
<li>making a <i>program</i> (see <a class="external-link" href="http://www-01.ibm.com/support/docview.wss?uid=swg21088932">here</a>)</li>
<li>with a <i>scheduled agent</i> that send commands to server</li>
</ul>
<p>Next lines show how to do this in a Java agent:</p>
<pre>String command = "load convert -n mydatabase.nsf * remoteserver!!template.ntf";
System.out.println(session.sendConsoleCommand("", command));</pre>
<p>That's all! Feel free to let me know what do you think about this approach. Any comment/review will be appreciated!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Baglioni</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>Lotus</dc:subject>
    
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>Domino</dc:subject>
    
    <dc:date>2013-04-12T13:30:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/pudb-ovvero-come-ho-imparato-velocizzare-mio-debug">
    <title>PuDB, ovvero: come ho imparato a velocizzare il mio debug</title>
    <link>http://blog.redturtle.it/pudb-ovvero-come-ho-imparato-velocizzare-mio-debug</link>
    <description>Quando si fa debug di codice poco "docile" è importante avere gli strumenti giusti per non impazzire sprecando tempo prezioso nel trovare il problema</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p style="float:right; "><img class="image-inline" src="uploads/wtf.png/@@images/7c8b7077-5383-4f56-a234-73a3e78d2709.png" /></p>
<p>Nella vita di ogni sviluppatore arriva il giorno in cui si incrocia la propria tastiera con codice incomprensibile, dai risultati inspiegabili e che, ovviamente, bisogna correggere. Avere gli <strong>strumenti adatti per il debugging</strong> è fondamentale.</p>
<p>Come programmatore python ho avuto modo di utilizzare diversi tipi di debugger, dal semplice pdb a una versione più completa: <a class="external-link" href="https://pypi.python.org/pypi/pdbpp/"><strong>pdb++</strong></a>. Esistono debugger integrati nei vari IDE di sviluppo, di cui però non ho mai approfondito troppo l'uso dal momento che li ho sempre trovato ambienti un po' pesanti. Da amante della shell, divido il mio tempo di sviluppo fra vim e un editor di testo molto semplice come <a class="external-link" href="http://www.sublimetext.com/"><strong>sublime</strong></a>.</p>
<p></p>
<a name="anchor-breaktext"></a><div class="breakText">Ed è da amante della shell che sono rimasto piacevolmente colpito da un debugger di cui non conoscevo l'esistenza, e di cui mi ha raccontato qualche giorno fa un uccellino azzurro: <a class="external-link" href="https://pypi.python.org/pypi/pudb"><strong>puDB</strong></a>!</div>
<p>PuDB è un debugger visuale basato su <strong><a class="external-link" href="https://pypi.python.org/pypi/urwid">Urwid</a></strong>. Quando ho visto la sua prima immagine ho pensato che fosse qualcosa per nostalgici: sono riaffiorati parecchi ricordi delle schermate che mi hanno accompagnato quando muovevo i primi passi con TurboPascal.</p>
<p>Ma questo effetto <a class="external-link" href="http://it.wikipedia.org/wiki/Stile_retr%C3%B2"><strong>retrò</strong></a> è passato rapidamente in secondo piano: mi sono reso conto di avere sotto mano <strong>tutto quello che mi serviva per debuggare il problema su cui stavo lavorando</strong>.</p>
<h2>Installazione e uso</h2>
<p>L'installazione è semplice: è necessario utilizzare un interprete che vada almeno dalla versione 2.4 in poi, dopodiché, che si usi buildout, easy_install o pip per l'installazione è indifferente e in un attimo si è pronti all'uso del pacchetto; nel codice che si vuole debuggare basta inserire:</p>
<p class="callout">import <strong>pudb</strong>;<strong>pudb</strong>.set_trace()</p>
<p>e al momento dell'esecuzione, in shell ci si trova nel debugger:</p>
<p><img class="image-inline" src="uploads/pudb.png/@@images/c36746db-0a5c-4e45-b213-802cb5bad61a.png" /></p>
<p>Una sola immagine è già in grado di mostrare che si ha molto per le mani: un'intera pagina di codice sulla sinistra e 3 box sulla destra - il primo con le variabili dello scope, il secondo in cui viene mostrato lo stack e il terzo in cui si trova la lista dei breakpoint.</p>
<h2>Caratteristiche principali</h2>
<p>Il funzionamento base è come quello del pdb: <strong>'n'</strong> per proseguire nel debug, <strong>'s'</strong> per entrare in un metodo e così via. Ma vediamo quali sono le caratteristiche principali di questo debugger e perché, secondo me, rende più veloce il debug.</p>
<blockquote class="pullquote">Navigazione da tastiera tramite le frecce o Shift+[V/S/B] per passare ai box di variabili, stack e breakpoint</blockquote>
<p>Prima di tutto, abbiamo un'ottima navigazione da tastiera: per chi è abituato a vim, si tratta di ricordare qualche combinazione o tasto in più, ma una volta memorizzati i comandi principali si va veramente veloci. Per una lista di tutti i comandi, si può fare riferimento alla guida inserita nel debugger stesso, attivabile premendo <strong>'SHIFT+?'</strong>.</p>
<p>In secondo luogo, si vede una porzione molto ampia di codice; non è una regola, ma cerco di scrivere metodi non troppo lunghi (dove possibile) e con puDB diventa facile visualizzare l'intero metodo di cui si sta facendo debug.</p>
<blockquote class="pullquote">Visualizzazione differenziata per le variabili (tipo, repr, str) ed espandibile nel caso di variabili complesse</blockquote>
<p>Altra caratteristica che reputo importantissima (assieme alla visione completa del codice) è la presenza nei box di destra di tutte le variabili che vengono definite nello scope e aggiornate durante l'esecuzione. Quello che preferisco è il fatto che si possa agire in diversi modi sulle variabili: in caso di variabili complesse, queste si possano espandere come vediamo nell'immagine sotto (premendo <strong>'\'</strong>):</p>
<p style="text-align: center; "><img src="http://blog.redturtle.it/uploads/variables.png" style="text-align: center; " title="" class="image-inline" alt="" /></p>
<p>Inoltre si può scegliere il tipo di visualizzazione per ogni singola variabile, mostrandola per tipo (<strong>'t'</strong>), mostrandone la __repr__ (<strong>'r'</strong>), la __str__ (<strong>'s'</strong>) oppure utilizzando una rappresentazione personalizzata (<strong>'c'</strong>).</p>
<blockquote class="pullquote">Stack delle chiamate navigabile</blockquote>
<p>Per quello che riguarda lo stack, la possibilità di scorrerlo e di navigare nei metodi elencati al suo interno è molto utile, dal momento che si è in grado (senza dover aprire altre finestre o senza riavviare la sessione di debug) di visualizzare il codice chiamante dei metodi che stiamo analizzando. Una volta entrati in un altro metodo dello stack, cambierà lo scope e anche le variabili visualizzate saranno aggiornate; l'esecuzione del debug riprenderà da quel nuovo punto.</p>
<p>Ovviamente, nel caso fosse necessario utilizzare la linea di comando, la possibilità c'è: è sufficiente premere <strong>'!'</strong> per fare switch su una shell python contestualizzata nello scope corrente ('<strong>Ctrl-d'</strong> per uscirne).</p>
<p>Non c'è molto altro da aggiungere; la cosa migliore da fare è mettersi alla tastiera e iniziare a utilizzare questo debugger!</p>
<p><span class="discreet">Immagine nel body: <a href="http://www.flickr.com/photos/smitty/2245445147/">http://www.flickr.com/photos/smitty/2245445147/</a></span></p>
<p><span class="discreet">Immagine in testata: </span><a href="http://www.flickr.com/photos/28208534@N07/4047355843">http://www.flickr.com/photos/28208534@N07/4047355843</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Luca Bellenghi</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>tutorials</dc:subject>
    
    <dc:date>2013-04-03T08:50:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/quando-la-sicurezza-in-plone-e-importante-reindexobjectsecurity">
    <title>Quando la sicurezza in Plone è importante: reindexObjectSecurity</title>
    <link>http://blog.redturtle.it/quando-la-sicurezza-in-plone-e-importante-reindexobjectsecurity</link>
    <description>Quando gli indici introdotti da prodotti aggiuntivi hanno a che fare con la sicurezza del sito, è meglio prendere alcune precauzioni per evitare problemi</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Per chiunque sviluppi con Plone, diventa presto chiara l'importanza del catalogo e, contemporaneamente, la necessità di <strong>mantenere il catalogo del sito aggiornato</strong>.</p>
<p>L'API principale a cui si fa riferimento per aggiornare lo stato di un contenuto nel catalogo Plone è la chiamata a <strong>reindexObject</strong>:</p>
<pre>&gt;&gt;&gt; context.setTitle('Nuovo titolo')<br />&gt;&gt;&gt; context.reindexObject()<br />&gt;&gt;&gt; context.title()<br />'Nuovo titolo'</pre>
<p>Fin qui, nulla di nuovo.</p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<p>In alcuni casi è possibile limitare gli indici aggiornati a un sottoinsieme di quelli esistenti.</p>
<pre>&gt;&gt;&gt; context.setTitle('Nuovo titolo')<br />&gt;&gt;&gt; context.reindexObject(idxs['Title', 'sortable_title', 'SearchableText'])<br />&gt;&gt;&gt; context.title()<br />'Nuovo titolo'</pre>
<p>In base all'attributo modificato, vale la pena aggiornare tutti gli indici che lo riguardano.</p>
<h2>Quando la sicurezza incontra la ricerca</h2>
<p><img src="http://blog.redturtle.it/topic_images/MorePaperPeople.jpg/@@images/118a359e-7d62-465d-9cec-9f5f418c1cf4.jpeg" alt="Meeting" class="image-right" title="Meeting" />Un fatto meno noto: <strong>il catalogo di Plone è usato anche nella gestione della sicurezza</strong>.<br />In particolare esiste un indice dal nome <strong>allowedRolesAndUsers</strong> esplicitamente usato per una verifica di sicurezza: capire se un utente ha accesso a un contenuto (in pratica viene memorizzato nel catalogo se gli utenti hanno il permesso di "<strong>View</strong>" sul contenuto).</p>
<p>Perché tutto questo? L'alternativa sarebbe disastrosa in termini di prestazioni: caricare tutti i documenti risultanti dalla ricerca e verificare il controllo di sicurezza direttamente su questi.<br />Tale operazione può diventare molto lenta e dispendiosa, se pensiamo che una semplice ricerca testuale può portare a migliaia di risultati in un sito mediamente popolato.</p>
<p>E' quindi importante che <strong>questo indice sia sempre aggiornato</strong>. Per questo motivo, ogni volta che viene effettuata un'operazione che comporta un cambio di sicurezza di un contenuto (principalmente modifiche nella pagina di condivisione), l'indice viene aggiornato automaticamente.</p>
<p>Dato che una semplice chiamata al metodo <i>reindexObject</i> può essere relativamente dispendiosa (tenete sempre presente che Plone potrebbe indicizzare anche i file allegati e che l'operazione di re-indicizzazione di un file può essere piuttosto lenta), esiste un altro metodo che viene utilizzata al suo posto: <strong>reindexObjectSecurity</strong>.<br />Nel funzionamento base di Plone, questa chiamata si occupa di aggiornare solo l'indice <i>allowedRolesAndUsers</i> introdotto sopra.</p>
<h2>Sicurezze alternative</h2>
<p><img src="http://blog.redturtle.it/topic_images/5894290438_493dd4802c_o.jpg/@@images/81948fc8-a18a-43f3-8235-cc9a7468adf6.jpeg" alt="Security cams" class="image-right" title="Security cams" />Ma è vero che l'unico indice legato alla sicurezza sia allowedRolesAndUsers?<br />Nell'uso quotidiano di Plone sì, ma esistono alternative.</p>
<p>Vi porto due esempi.</p>
<h3>collective.portlet.truereview</h3>
<p>Sto parlando di un prodotto poco noto e di qualche anno fa: <a class="external-link" href="https://pypi.python.org/pypi/collective.portlet.truereview/">collective.portlet.truereview</a>.</p>
<p>Questo prodotto applica lo stesso principio utilizzato col permesso "<i>View</i>" dall'indice <i>allowedRolesAndUsers</i> per un nuovo permesso legato all'attività di revisione dei contenuti: "<i>Review portal content</i>".<br />Per fare questo viene creato un nuovo indice: <i>reviewerRolesAndUsers</i>.</p>
<p>Lo scopo del prodotto è fornire una portlet di revisione alternativa per Plone che, a differenza di quella ufficiale, non richieda il caricamento del contenuto.<br /><i>NB</i>: il prodotto è fermo dal 2009, non sono certo che funzioni ancora ma lo trovo piuttosto didattico.</p>
<h3>collective.localrolesdatatables</h3>
<p>Il secondo esempio è legato al più recente <a class="external-link" href="http://plone.org/products/collective.localrolesdatatables/">collective.localrolesdatatables</a>.</p>
<p>Questo prodotto vuole fornire una vista che permetta di ispezionare lo stato della sicurezza e delle condivisioni in un sito Plone.<br />Per fare questo è necessario capire su quali contenuti ci siano effettivamente delle personalizzazioni locali della sicurezza, ovviamente senza caricare i contenuti per non ricadere nel solito problema di prestazioni (ormai ci siamo capiti, vero?!).<br />L'approccio usato è quello di creare un nuovo indice "<strong>hasLocalRoles</strong>", un valore booleano che indica se un contenuto ha ruoli locali impostati o meno.</p>
<h2>Espandere il funzionamento di reindexObjectSecurity</h2>
<p><img src="http://blog.redturtle.it/topic_images/bingbang.jpg/@@images/35839ef6-0211-4940-b28f-61007cbbed77.jpeg" alt="Universe expanding" class="image-left" title="Universe expanding" />E' stato proprio utilizzando di recente <i>collective.localrolesdatatables</i> che mi sono imbattuto in un bug (di cui sono certo soffra anche <i>collective.portlet.truereview</i>):<br />il prodotto funziona a dovere, ma se la sicurezza di un contenuto viene aggiornata, l'indice "hasLocalRoles" non viene modificato a sua volta, almeno non fin quando l'intero documento verrà modificato (e quindi completamente reindicizzato).</p>
<p>Qual è il problema? Il metodo <strong><i>reindexObjectSecurity</i> "non sa" che anche questo indice fa parte della sicurezza del sito</strong>.</p>
<p>Come risolvere?</p>
<p>Se andiamo ad analizzare l'implementazione di <i>reindexObjectSecurity</i>, troviamo questa riga di codice:</p>
<pre>catalog.reindexObject(ob, idxs=self._cmf_security_indexes,<br />                      update_metadata=0, uid=brain_path)</pre>
<p>Viene quindi chiesto di reindicizzare nel catalogo il documento (<i>ob</i>) e di limitarsi agli indici definiti nella tupla <i>self._cmf_security_indexes</i> (quindi non tutti gli indici).<strong> </strong></p>
<p><strong>_cmf_security_indexes</strong> è un attributo di classe definito per <strong>CatalogAware</strong>, nel pacchetto <i>Products.CMFCore</i>. La sua definizione è la seguente:</p>
<pre>_cmf_security_indexes = ('allowedRolesAndUsers',)</pre>
<p>La soluzione è molto semplice: estendere la lista di indici registrati in questo attributo (<a class="external-link" href="https://github.com/collective/collective.localrolesdatatables/blob/8319deb4aef3d8db1803f1823071052130b28ce4/collective/localrolesdatatables/__init__.py">qui</a> come ho modificato <i>collective.localrolesdatatables</i>):</p>
<pre>from Products.CMFCore.CMFCatalogAware import CatalogAware<br /><br />CatalogAware._cmf_security_indexes += ('mioNuovoSecurityIndex',)</pre>
<h3>Problemi?</h3>
<p>Questo tipo di intervento ha un effetto collaterale: modifica l'attributo <strong>a livello di classe</strong> e si rispecchia quindi su <i>tutti</i> i siti Plone presenti nell'installazione. Potreste anche trovarvi nella situazione in cui il prodotto che necessita di questa modifica sia installato su un solo sito Plone, quindi il nuovo indice non sia presente in altri, eppure la modifica sia comunque applicata.</p>
<p>E' un problema? Per fortuna no!<br />Il catalogo Plone è molto tollerante: se viene chiesta la reindicizzazione di un indice non esistente, questo fallisce l'operazione senza sollevare eccezioni.</p>
<p><span class="discreet">L'immagine di testata è di <a class="external-link" href="http://www.flickr.com/photos/adulau/">Alexandre Dulaunoy</a>.</span></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Luca Fabbri</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>products</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    
      <dc:subject>Archetypes</dc:subject>
    
    
      <dc:subject>plone.it</dc:subject>
    
    <dc:date>2013-03-12T08:00:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/e-uscito-il-buildout-2.0-posso-rilanciare-il-mio-buildout">
    <title>E' uscito il Buildout 2.0! Posso rilanciare il mio buildout?</title>
    <link>http://blog.redturtle.it/e-uscito-il-buildout-2.0-posso-rilanciare-il-mio-buildout</link>
    <description>Viene rilasciata una nuova versione di un pacchetto, e per molti Plonisiti è il caos. Vediamo come usare (o non usare) il Buildout 2.0</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Non è molto che è uscita la versione <strong>2.0</strong> di <a class="external-link" href="https://pypi.python.org/pypi/zc.buildout/2.0.1">zc.buildout</a> e, come spesso accade, i buildout, soprattutto quelli un po’ più vecchi, non prendono di buon grado l’aggiornamento.<br />Nel caso di questo pacchetto, le cause sono alcuni import che sono cambiati o sono stati spostati.</p>
<p>Il <strong>Buildout 2.0</strong> fa un taglio netto con il passato che, con le versioni 1.6 e 1.7, aveva come obiettivo principale quello di isolare il più possibile il buildout dalla componente <strong>Python</strong>. Ma il compito si è rivelato troppo difficile da implementare, e quindi si è scelto di abbandonare questa strada e lasciare all’utilizzatore di decidere tramite l'utilizzo di <i>virtualenv</i>.</p>
<p>Non è però in questo articolo che voglio analizzare le modifiche apportate a questo componente (che potete comunque trovare <a class="external-link" href="https://pypi.python.org/pypi/zc.buildout/2.0.1#id4">qui</a>). Oggi vediamo cosa fare per far funzionare i <i>nostri</i> bulidout.</p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<h2>Rimanere ancorati al passato.</h2>
<blockquote class="pullquote">Tutto dipende dal bootstrap.py presente nel vostro buildout.</blockquote>
<p>Se vi interessa rimanere sulle versioni <strong>1.x</strong><strong>,</strong><strong> </strong>allora potete usare questo <strong>bootstrap.py</strong>: contiene una restrizione sulla versione di <i>zc.buildout</i> che deve essere minore di 2.0 - <a class="external-link" href="http://downloads.buildout.org/1/bootstrap.py">http://downloads.buildout.org/1/bootstrap.py</a>.</p>
<p>Ma se non vi sentite troppo sicuri nel sostituire il <i>bootstrap.py</i> del vostro buildout, c’è una seconda soluzione, suggerita in risposta a <a class="external-link" href="http://stackoverflow.com/questions/14801416/zc-buildout-stopped-working-importerror-no-module-named-apport-fileutils">questo bug report</a> su stack overflow. <br />Dovrete semplicemente rilanciare il bootstrap del vostro buildout  forzando la versione di <i>zc.buildout</i> in questo modo:</p>
<pre><br />     python bootstrap.py -v 1.7.1<br /> </pre>
<p>Tuttavia, con versioni molto vecchie del <i>bootstrap.py</i>, potreste ritrovarvi nel caso in cui non sia possibile forzare la versione; allora dovrete per forza aggiornare il file con la versione di cui sopra, per poi eseguire il comando forzando <i>zc.buildout</i> all’ultima versione precedente alla 2.0; al momento, questa: <a class="external-link" href="https://pypi.python.org/pypi/zc.buildout/1.7.1">https://pypi.python.org/pypi/zc.buildout/1.7.1</a>.</p>
<h2>Il nuovo che avanza.</h2>
<p>Per usare la versione <strong>2.0</strong> di <i>zc.buildout</i> vi consiglio di utilizzare questa versione del bootstrap: <a class="external-link" href="http://downloads.buildout.org/2/bootstrap.py">http://downloads.buildout.org/2/bootstrap.py</a>.</p>
<p>In questo caso, però, vi potreste trovare nella situazione inversa, ovvero: dopo aver lanciato il solito comando "<i>./bin/buildout -N"</i>, non trovate la nuova versione di <i>zc.buildout</i>.</p>
<p>La causa è da attribuire alla presenza di <strong>buildout.dumppickedversions</strong> che ha fatto il pin di alcune versioni e, quindi, lanciando il buildout con il <i>“-N”</i> (pratica più che corretta!), la nuova versione per <i>zc.buildout</i> non è stata presa in considerazione. <br />Magari avete specificato il <i>dumppickedversions</i> nelle <i>extensions</i> nel vostro <i>buildout.cfg</i> e state pensando di rimuoverlo; ma nelle ultime versioni del buildout è automaticamente incluso. <br />La soluzione, consigliata da Reinout van Rees nel suo <a class="external-link" href="http://reinout.vanrees.org/weblog/2013/02/12/prevent-buildout-problems.html">post</a>, è molto semplice: fate il pin di questi due prodotti nel<i> [versions]</i> del vostro file di configurazione e avrete il vostro pacchetto aggiornato :)</p>
<pre><br />    [versions]
    zc.buildout = 2.0.1
    zc.recipe.egg = 2.0.0a3<br /> </pre>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Mirco Angelini</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>Plone 4</dc:subject>
    
    
      <dc:subject>buildout</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    <dc:date>2013-03-11T10:55:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/merging-odf-files-using-xdocreport-and-xpages">
    <title>Merging odf files using XDocReport and XPages</title>
    <link>http://blog.redturtle.it/merging-odf-files-using-xdocreport-and-xpages</link>
    <description>Often there is the need to create documents from templates, and the need to fill these templates with data available from other sources: let's take a look at this brief solution </description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>In this scenario, we have a <a class="external-link" href="https://www.google.it/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=1&amp;cad=rja&amp;ved=0CDIQFjAA&amp;url=http%3A%2F%2Fit.wikipedia.org%2Fwiki%2FOpenDocument&amp;ei=1fwhUej8D4fesgaG_YHgDA&amp;usg=AFQjCNFsaJsTx0KGBA-7HBgvr74IKbUtZw&amp;bvm=bv.42553238,d.Yms">odf template</a> to merge with data.</p>
<p><a class="external-link" href="https://www.google.it/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=1&amp;cad=rja&amp;ved=0CDIQFjAA&amp;url=http%3A%2F%2Fit.wikipedia.org%2Fwiki%2FOpenDocument&amp;ei=1fwhUej8D4fesgaG_YHgDA&amp;usg=AFQjCNFsaJsTx0KGBA-7HBgvr74IKbUtZw&amp;bvm=bv.42553238,d.Yms"></a></p>
<p>Working in the Lotus Domino environment, one way to achieve this goal could be a server-side component invoked by an <strong>XPagdies</strong>, to make the casting process: first, I need something in Java that could do the job for me, and after some search I came across <a class="external-link" href="http://code.google.com/p/xdocreport/">XDocReport</a>.</p>
<blockquote class="pullquote">XDocReport means XML Document reporting</blockquote>
<p class="breakText"><i><strong>XDocReport </strong>provides Java API to merge XML document created with MS Office (docx, pptx) or OpenOffice (odt), LibreOffice (odt) with a Java model to generate report and convert it in another format (PDF, XHTML...).</i></p>
<p class="breakText">Basicly, to make the template, main steps are:</p>
<ol style="padding-left: 25px; ">
<li>create a <strong>template document</strong> with MS Word (docx) or OpenOffice (odt, ods)</li>
<li>compose the body of the document itself with <strong>variables </strong>to fill in</li>
<li>use <strong><a href="http://velocity.apache.org/" rel="nofollow">Velocity</a> </strong>or<strong> <a href="http://freemarker.sourceforge.net/" rel="nofollow">Freemarker</a> syntax</strong> to set variables to replace.</li>
</ol>
<p><br />In this case, I opted to use Velocity inside an ODF file.</p>
<p>Now let's start by explaining how to set up the whole project.</p>
<h3>Installing server libraries</h3>
<p>External libraries (.jar) that serve the purpose have been installed in the installation directory of the Domino server (<i><strong>jvm/lib/ext</strong> </i>path).</p>
<p>These are the files:</p>
<pre>commons-collections-3.2.1.jar
commons-lang-2.4.jar
fr.opensagres.xdocreport.converter-1.0.0.jar
fr.opensagres.xdocreport.converter.odt.odfdom-1.0.0.jar
fr.opensagres.xdocreport.core-1.0.0.jar
fr.opensagres.xdocreport.document-1.0.0.jar
fr.opensagres.xdocreport.document.odt-1.0.0.jar
fr.opensagres.xdocreport.itext.extension-1.0.0.jar
fr.opensagres.xdocreport.template-1.0.0.jar
fr.opensagres.xdocreport.template.velocity-1.0.0.jar
itext-2.1.7.jar
odfdom-java-0.8.7.jar
org.odftoolkit.odfdom.converter-0.9.0.jar
org.odftoolkit.odfdom.converter.core-1.0.0.jar
org.odftoolkit.odfdom.converter.pdf-1.0.0.jar
org.odftoolkit.odfdom.converter.xhtml-1.0.0.jar
oro-2.0.8.jar
velocity-1.7.jar
</pre>
<p>Dont' forget an important security setting in <i>java.policy</i> file:</p>
<div id="_mcePaste"><i>grant {</i></div>
<div id="_mcePaste"><i><span> </span> permission java.lang.RuntimePermission "getClassLoader";</i></div>
<div id="_mcePaste"><i><span> </span> permission java.lang.RuntimePermission "setContextClassLoader";</i></div>
<div id="_mcePaste"><i>}</i></div>
<div><i><br /></i></div>
<div></div>
<div>After that, restart HTTP server.</div>
<div></div>
<h3>Server-side tools</h3>
<p>To manage the whole process I used an HTML page with a button that calls (via Ajax) an XPages, that deals with the fusion of the data (HTML string) in the template. At the end, I create the PDF file using <a class="external-link" href="http://itextpdf.com/">iText </a>libraries.</p>
<p>Related to XPages, it only call the servlet in the <i>afterPageLoad</i> event:</p>
<pre>com.redturtle.XDocReport.mergeAndPDF(
	facesContext.getExternalContext().getRequest(), 
	facesContext.getExternalContext().getResponse()
);
</pre>
<p>This is how it looks the package, which includes the servlet:</p>
<p><img alt="package" class="image-inline" src="http://blog.redturtle.it/uploads/copy_of_package.GIF" title="package" /></p>
<p>Finally, this is the servlet code:</p>
<pre>package com.redturtle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.odftoolkit.odfdom.converter.pdf.PdfConverter;
import org.odftoolkit.odfdom.converter.pdf.PdfOptions;
import org.odftoolkit.odfdom.doc.OdfTextDocument;

import fr.opensagres.xdocreport.core.document.SyntaxKind;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;

import lotus.domino.NotesException;
import lotus.domino.Session;

public class XDocReport {

	private static String VERSION = "XDocReport Version 1.0.0";
	static PrintWriter outClass = null;
	private static Session session = null;

	public static void mergeAndPDF (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InterruptedException, NotesException {

                // NO DIIOP
		session=DominoAccess.getCurrentSession();
		try {			
			// 1) Load ODT file by filling Velocity template engine
			InputStream in = new FileInputStream("/tmp/template.odt");

			IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in,TemplateEngineKind.Velocity);

			FieldsMetadata metadata = report.createFieldsMetadata();
			metadata.addFieldAsTextStyling("comments",SyntaxKind.Html,false);

			// 2) Create context Java model
			IContext context = report.createContext();
			context.put("comments", "&lt;b&gt;<strong>hello world&lt;/b&gt;</strong>");
			
			// 3) Generate report by merging Java model with the ODT
			OutputStream out = new FileOutputStream(new File("/tmp/ODTProjectWithVelocity_Out.odt"));
			report.process(context, out);

			// 1) Load ODT into ODFDOM OdfTextDocument 
			in= new FileInputStream(new File("/tmp/ODTProjectWithVelocity_Out.odt"));
			OdfTextDocument document = OdfTextDocument.loadDocument(in);

			// 2) Prepare Pdf options
			PdfOptions options = PdfOptions.create();
			
			// 3) Convert OdfTextDocument to PDF via IText
			out = new FileOutputStream(new  File("/tmp/ODTProjectWithVelocityList_Out.pdf"));
			PdfConverter.getInstance().convert(document, out, options);
 
                        // Only manage response to Ajax call
		        response.setContentType("text/plain");
		        response.setCharacterEncoding("UTF-8");
			outClass = new PrintWriter(response.getWriter());
			printAgent("OK");
			return;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	protected static void printAgent(String string) {
		outClass.println(string);
	}
}
</pre>
<p>Pay attention to these points:</p>
<ol>
<li><i>comments<strong> </strong>-<strong> </strong></i> variable name inside ODF template</li>
<li><i>metadata.addFieldAsTextStyling("comments",SyntaxKind.Html,false); - </i>fill variable with something in HTML syntax</li>
<li><i>&lt;b&gt;hello world&lt;/b&gt;</i> - HTML string to merge</li>
</ol>
<p><strong>NOTE:</strong><i> DominoAccess </i>and <i>JSFUtil</i> are two very helpful classes written by Karsten Lehmann: see the <a class="external-link" href="http://www.mindoo.com/web/blog.nsf/dx/18.07.2009191738KLENAL.htm?opendocument&amp;comments">full code</a>. Thanks to them, DIIOP process for communication is not required.</p>
<h3>Pro &amp; Cons.</h3>
<p>I think that it's a very powerful approach; the only limitation I've found is the restricted set of styles supported by the merge process (see <a class="external-link" href="http://code.google.com/p/xdocreport/wiki/ODTReportingJavaMainHTMLTextStyling#Supported_styles">there</a> for details). Feel free to make some test using this approach, and let me know what you think :-)</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Baglioni</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>Domino</dc:subject>
    
    
      <dc:subject>java</dc:subject>
    
    
      <dc:subject>xPages</dc:subject>
    
    <dc:date>2013-03-08T08:55:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/creazione-plugin-gestire-gruppi-virtuali-plone">
    <title>Creazione di plugin per gestire gruppi virtuali in Plone</title>
    <link>http://blog.redturtle.it/creazione-plugin-gestire-gruppi-virtuali-plone</link>
    <description>Una funzionalità Plone che sfrutto pochissimo è il meccanismo dei gruppi virtuali, quello che sta dietro al gruppo "Authenticated User". Analizziamolo!</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Poche sono state le volte in cui ho avuto bisogno di sfruttare il gruppo che in Plone è chiamato <strong>"Authenticated Users"</strong>. Si tratta di un gruppo virtuale creato in modo tale per cui ogni utente autenticato risulta appartenere, fra gli altri, a questo gruppo in modo automatico.</p>
<a name="anchor-breaktext"></a><div class="breakText">E' utile sapere come creare questi gruppi per poterli sfruttare quando si presentano casi d'uso in cui una buona profilazione degli utenti passa per un determinato raggruppamento, soprattutto perché possiamo fare in modo che questo raggruppamento sia automatizzato.</div>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<h3>Il caso</h3>
<blockquote class="pullquote">Come fa Plone a gestire<br /> i gruppi virtuali??</blockquote>
<p>Partiamo da un caso concreto, al quale sto lavorando in questi giorni. Ci sono degli user che, tramite un sistema di autenticazione federato, si collegano al sito. Quando questi si collegano per la prima volta, viene creato un utente vero e proprio nel sito Plone a cui viene impostata una proprietà (supponiamo sia "caratteristica_x=True"). Devo fare in modo di raggrupparli automaticamente.</p>
<p>In Plone tutto questo si fa con <a class="external-link" href="https://pypi.python.org/pypi/Products.PluggableAuthService/"><strong>PAS</strong></a> e i suoi plugin in <a class="external-link" href="https://pypi.python.org/pypi/Products.PlonePAS/"><strong>PlonePAS</strong></a> (si può vedere della documentazione <a class="external-link" href="http://plone.org/documentation/manual/developer-manual/users-and-security/pluggable-authentication-service"><strong>qui</strong></a>). E proprio in quest'ultimo troviamo il <a class="external-link" href="https://github.com/plone/Products.PlonePAS/tree/master/Products/PlonePAS"><strong>codice</strong></a> che ci mostra come creare e gestire il gruppo degli "Authenticated User"; codice che si trova nei seguenti file:</p>
<ul>
<li>plugins/autogroup.py</li>
<li>__init__.py</li>
<li>Extensions/Install.py</li>
</ul>
<p>Negli ultimi due moduli c'è il codice per registrare il plugin e installarlo. Si tratta di operazioni banali, e ci tornerò sopra più avanti quando mostrerò come creare un nuovo plugin. Nel primo modulo, invece, c'è tutta la logica che riguarda il funzionamento del plugin, ed è questo che mi interessa analizzare.</p>
<h3>Analisi del plugin</h3>
<p>Il plugin è generato tramite la classe <strong>AutoGroup</strong>. I meccanismi di PAS si basano sull'implementazione di interfacce tramite cui la classe si impegna a fornire un set di metodi; nella classe in analisi troviamo:</p>
<blockquote class="pullquote">Tramite l'implementazione di interfacce, un plugin fornisce dei metodi e "promette" dei comportamenti</blockquote>
<ul>
<li><strong>IGroupEnumerationPlugin</strong>: fornisce metodi per l'enumerazione e la ricerca di gruppi</li>
<li><strong>IGroupsPlugin</strong>: fornisce un metodo tramite il quale, dato un principal (utente o gruppo) si ritornano tutti i gruppi a cui questo principal appartiene</li>
<li><strong>IGroupIntrospection</strong>: fornisce metodi per ottenere da un plugin gruppi e utenti dei gruppi</li>
<li><strong>IPropertiesPlugin</strong>: fornisce un metodo per ottenere le proprietà (titolo e descrizione) di un gruppo.</li>
</ul>
<p>Per il nostro use case, è tutto corretto e l'unico comportamento che ci interessa modificare è quella fornito da <strong>IGroupsPlugin</strong>, tramite cui decidiamo se uno user è nel gruppo oppure no.</p>
<h3>Creiamo un nuovo plugin</h3>
<p>Per farlo, percorriamo i passi già citati sopra:</p>
<ul>
<li>si crea un plugin</li>
<li>si registra</li>
<li>lo si installa nel sistema.</li>
</ul>
<p>Come detto, modifichiamo una sola funzionalita, data dal metodo <strong>getGroupsForPrincipal</strong>:</p>
<pre><br />from App.class_init import InitializeClass
from Products.PlonePAS.plugins.autogroup import AutoGroup<br />from Products.PageTemplates.PageTemplateFile import PageTemplateFile

manage_addCustomGroupForm = PageTemplateFile("./zmi/CustomUsersGroupForm",
                                           globals())<br /><br />
def manage_addCustomGroup(self,
                          id,
                          title='',
                          group='',
                          description='',
                          RESPONSE=None):
    """Add an Auto Group plugin."""

    plugin = CustomUsersGroup(id, title, group, description)
    self._setObject(id, plugin)

    red_to = "%s/manage_workspace?manage_tabs_message=Custom+Users+Group+plugin+added"
    if RESPONSE is not None:
        return RESPONSE.redirect(red_to % self.absolute_url())<br /><br />
class CustomUsersGroup(AutoGroup):
    meta_type = "Custom users group"

    # IGroupsPlugin implementation.
    def getGroupsForPrincipal(self, principal, request=None):
        if principal.getProperty('caratteristica_x'):
            return (self.group,)<br />        return ()

InitializeClass(CustomUsersGroup)<br /> </pre>
<p>Gli altri metodi nel codice sopra, <strong>manage_addCustomGroup</strong> e <strong>manage_addCustomGroupForm</strong>, sono utilizzati per l'aggiunta e la gestione del plugin in <strong>ZMI</strong> dentro la <strong>acl_users</strong> del sito: dobbiamo infatti creare un'istanza del plugin per avere il nuovo gruppo a disposizione.</p>
<p>Una volta scritto il codice del plugin lo dobbiamo registrare nell'inizializzazione del pacchetto:</p>
<pre><br />from Products.PluggableAuthService import registerMultiPlugin<br /><br />try:
    registerMultiPlugin(CustomUsersGroup.meta_type)
except RuntimeError:
    # Don't explode upon re-registering the plugin:
    pass

def initialize(context):
    context.registerClass(CustomUsersGroup,
                           permission=add_user_folders,
                           constructors=(manage_addCustomGroupForm,
                                         manage_addCustomGroup),
                           visibility=None
                           )

</pre>
<p>Fatto questo, possiamo considerare l'attività di sviluppo terminata. Se vogliamo istanziare il plugin con l'installazione del pacchetto, dobbiamo creare un <strong>import step</strong> dentro il quale fare qualcosa di questo tipo:</p>
<pre> 
    ....
    portal = context.getSite()
    acl_users = getToolByName(portal, 'acl_users')
    plugins = acl_users.plugins
    if not 'custom_users_group' in acl_users.objectIds():
        manage_addCustomGroup(acl_users,
                            "custom_users_group",
                            "Custom users (Virtual Group)",
                            "CustomUsersGroup",
                            "Automatic Custom Group Provider")
        for interface in [IGroupsPlugin,
                          IPropertiesPlugin,
                          IGroupEnumerationPlugin,
                          IGroupIntrospection]:
            plugins.activatePlugin(interface, "custom_users_group"<br />    ....<br />
</pre>
<p>In questo modo si aggiunge il plugin alla acl_users del sito e si attivano le interfacce per le funzionalità che il plugin dovrà gestire.</p>
<h3>Conclusioni</h3>
<p>Finito! Come possiamo vedere a questo punto nella gestione gruppi del sito, abbiamo un gruppo in più, che si comporta esattamente come il gruppo "Authenticated Users", tranne che per la condizione di appartenenza al gruppo; tale gruppo è disponibile anche nello sharing.</p>
<p>Per testarne il funzionamento, possiamo accedere al sito con un utente e verificare che non abbia permessi di nessun tipo. Dalla gestione gruppi possiamo assegnare il ruolo di Manager al gruppo appena creato e tornare a verificare con l'utente di cui sopra, che ora ha permessi da Manager nel portale.</p>
<p><span class="discreet">L'immagine in testata è di <a class="external-link" href="http://www.flickr.com/photos/denisemccooeyphoto/">evildan2</a></span></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Luca Bellenghi</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>PAS</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    
      <dc:subject>plone.it</dc:subject>
    
    <dc:date>2013-02-26T09:55:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/migrating-dexterity-items-to-dexterity-containers">
    <title>How to transform a dexterity Item in to a dexterity Container</title>
    <link>http://blog.redturtle.it/migrating-dexterity-items-to-dexterity-containers</link>
    <description>The use case of adding container capabilities to content types is common in the Plone world.
I show a nifty solution that applies to dexterity content types
</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<blockquote class="pullquote"><br />This is a rather technical post, full of code. Faint of hearth you are advised!</blockquote>
<h3>The story</h3>
<p>Not all the content types are designed from the beginning with a container behaviour, a feature that can become a requirement in consequence of specification changes.</p>
<p>It happened also to me and in my case I had to deal with dexterity based content types. I started googling around and found this <a class="external-link" href="https://groups.google.com/forum/?fromgroups=#!topic/dexterity-development/vI4lzGFhEig">unresolved question</a> on the dexterity mailing list.</p>
<p>Starting from the suggestions in the thread, I managed to find out a nifty solution that reveals some interesting aspects of the software we work with.</p>
<a name="anchor-breaktext"></a><div class="breakText">I will show you how to reproduce the problem and explain the solution I propose.</div>
<p class="breakText"> </p>
<h3>Creating the dexterity type</h3>
<p>First of all let's create through the web the <strong>Example content</strong> dexterity type:</p>
<p><img alt="Add Example content" class="image-inline" src="http://blog.redturtle.it/uploads/adddexteritycontent.png/@@images/4d2a064e-150c-41cc-8312-e2775d955f60.png" title="Add Example content" /></p>
<p>Then let's create the "<strong>Example 1</strong>" object:</p>
<p><img alt="Example 1 object" class="image-inline" src="http://blog.redturtle.it/uploads/example1view1.png/@@images/a6963282-ad2b-42db-a53a-2c1fcc611e30.png" title="Example 1 object" /></p>
<h3>Screwing it all up</h3>
<p>Going to the portal_type edit form we can make "<strong>Example content</strong>" become a folderish object by modifying it's content type class, just replace "Item" with "Container".</p>
<p><img class="image-inline" src="http://blog.redturtle.it/uploads/contenttypeclass.png" /></p>
<p>To make something interesting it is  probably advisable to play with the "<i>Filter content types?</i>" and "<i>Allowed content types</i>" properties, allowing the creation of objects inside this container.</p>
<p>Once this is done the newly created objects show a folderish behaviour and view:</p>
<p><img alt="Example 2 object" class="image-inline" src="http://blog.redturtle.it/uploads/example2view1.png/@@images/90dc5422-9ad8-4809-a654-eb8e10ee0799.png" title="Example 2 object" /></p>
<p>But the old one is completely <strong>unchanged</strong> and has to be migrated!</p>
<p>The objects have clearly a different nature, because they are instances of different classes. Going into a pdb we can clearly see it:</p>
<pre>(pdb) portal['example-1'].portal_type<br />'example_content'<br />(pdb) portal['example-2'].portal_type<br />'example_content'<br />(pdb) portal['example-1']<br />&lt;Item at /Plone/example-1&gt;<br />(pdb) portal['example-2']<br />&lt;Container at /Plone/example-2&gt;</pre>
<p>The portal type is the same but:</p>
<ol>
<li><strong>"Example 1"</strong> is an instance of <strong>plone.dexterity.content.Item</strong></li>
<li><strong>"Example 2"</strong> is an instance of <strong>plone.dexterity.content.Container.</strong></li>
</ol>
<h3>Building the box</h3>
<p>It is time to fold the paper sheet in to a box!</p>
<p><a class="external-link" href="https://groups.google.com/forum/#!msg/dexterity-development/vI4lzGFhEig/xj81brZ6MGAJ">Aspeli's suggestion</a> of replacing the __class__ attribute is a good starting point, but I found out that <strong>restarting the instance swallowed my changes</strong>.</p>
<p>I believe the answer can be found between the lines of <a class="external-link" href="http://tech.groups.yahoo.com/group/zope/message/158662">this post</a> from Dieter:</p>
<p class="callout"><i> The main problem [about __class__ switching] is that the <strong>class is usually coded</strong> (for efficiency reasons) <strong>in the persistent references to an object</strong>. As soon as the container is loaded from the ZODB, <strong>a ghost is created for its persistent reference using the class mentioned there</strong>.</i><br /> <i><br /> This means: it is <strong>not safe to change the class</strong> of a persistent object unless you although modify all its containers (otherwise, some persistent references remain with the old class. This potentially leads to non-deterministic behaviour).</i></p>
<p>A workaround that seems working to me is to detach the object from the ZODB, switch the class and put the object back in place, like shown in here:</p>
<pre>(pdb) obj = portal['example-1']<br />(pdb) del portal['example-1']<br />(pdb) obj<br />&lt;Item at /Plone/example-1&gt;<br />(pdb) from plone.dexterity.content import Container<br />(pdb) obj.__class__ = Container<br />(pdb) portal['example-1'] = obj</pre>
<p>This is unorthodox for sure, but for the time being it proved to be a working solution without side effects.</p>
<h3>Let's finish the job</h3>
<p>Ok, now the object is a <strong>Container instance</strong>!</p>
<pre>(pdb) obj<br />&lt;Container at /Plone/example-1&gt;</pre>
<p>But this is instance is not yet ready. As David Glick <a class="external-link" href="https://groups.google.com/d/msg/dexterity-development/vI4lzGFhEig/Ve2lKT6w8pcJ">suggests</a>:</p>
<p class="callout"><i><span style="text-align: start; float: none; "><span> </span>I doubt that [switching with the __class__] will be sufficient, since containers have internal data<span> </span></span><span style="text-align: start; float: none; ">structures that would <strong>need to get set up</strong> in order for the object to<span> </span></span><span style="text-align: start; float: none; ">function as a container.</span><span style="text-align: start; float: none; "> </span></i></p>
<p>Infact visiting "<strong>Example 1</strong>" returns an error:</p>
<pre>2013-02-20 16:03:23 ERROR Zope.SiteErrorLog 1361372603.770.0118314104451 http://localhost:8080/Plone/example-1<br />Traceback (innermost last):<br /> Module ZPublisher.Publish, line 115, in publish<br /> Module ZPublisher.BaseRequest, line 437, in traverse<br /> Module Products.CMFCore.DynamicType, line 147, in __before_publishing_traverse__<br /> Module Products.CMFDynamicViewFTI.fti, line 215, in queryMethodID<br /> Module Products.CMFDynamicViewFTI.fti, line 182, in defaultView<br /> Module Products.CMFPlone.PloneTool, line 847, in browserDefault<br /> Module Products.CMFPlone.PloneTool, line 715, in getDefaultPage<br /> Module Products.CMFPlone.utils, line 90, in getDefaultPage<br /> Module plone.app.layout.navigation.defaultpage, line 32, in getDefaultPage<br /> Module plone.app.layout.navigation.defaultpage, line 75, in getDefaultPage<br /> Module plone.folder.ordered, line 202, in __contains__<br />TypeError: argument of type 'NoneType' is not iterable</pre>
<p>Checking the code, it turns out that the <strong>_tree</strong> attribute of the instance is <strong>None</strong> as it can be verified with some analysis on the object:</p>
<pre>(pdb) obj._tree is None<br />True</pre>
<p>This is because we miss some initialization, so it's time to dive into Python and check which parent class __init__ function we have to call.</p>
<p>To understand this I analyzed the method resolution order of the two classes and tried to filtered out the parent classes that were not interesting for my sake, either because their __init__ is identical to object.__init__ or because they are in common with the Item class, and so their init method were already called during "<strong>Example 1</strong>" initialization.</p>
<p>A first attempt returns this:</p>
<pre>(pdb) container_mro = Container.__mro__<br />(pdb) item_mro = Item.__mro__<br />(pdb) container_only_klasses = [klass for klass in container_mro if not klass in item_mro and not klass.__init__ is object.__init__]<br />(pdb) pp container_only_klasses<br />[&lt;class 'plone.dexterity.content.Container'&gt;,<br /> &lt;class 'plone.folder.ordered.CMFOrderedBTreeFolderBase'&gt;,<br /> &lt;class 'plone.folder.ordered.OrderedBTreeFolderBase'&gt;,<br /> &lt;class 'Products.BTreeFolder2.BTreeFolder2.BTreeFolder2Base'&gt;,<br /> &lt;class 'Products.CMFCore.PortalFolder.PortalFolderBase'&gt;,<br /> &lt;class 'OFS.Folder.Folder'&gt;]</pre>
<p>This considerably lowers the __init__ that we want to check. But we can go further, by noting that at the end it is enough to call the CMFOrderedBTreeFolderBase __init__method:</p>
<pre>(pdb) from plone.folder.ordered import CMFOrderedBTreeFolderBase<br />(pdb) [klass for klass in container_only_klasses if not klass in CMFOrderedBTreeFolderBase.__mro__]<br />[&lt;class 'plone.dexterity.content.Container'&gt;]</pre>
<p>Asking help on this class __init__ reveals:</p>
<pre>(pdb) !help(CMFOrderedBTreeFolderBase.__init__)<br />Help on method __init__ in module plone.folder.ordered:<br /><br />__init__(self, id, title='') unbound plone.folder.ordered.CMFOrderedBTreeFolderBase method</pre>
<p>So to properly set up the missing blocks we just want to call this class __init__ passing as arguments the object itself, its id and its title:</p>
<pre>(pdb) CMFOrderedBTreeFolderBase.__init__(obj, obj.getId(), obj.title)</pre>
<p>Great, a new tree has born!</p>
<pre>(pdb) obj._tree._tree
&lt;BTrees.OOBTree.OOBTree object at 0xb4a85f5c&gt;</pre>
<p>To finalize our work the object has to be reindexed and the transaction committed:</p>
<pre>(pdb) obj.reindexObject()<br />(pdb) from transaction import commit<br />(pdb) commit()</pre>
<h3>Final remarks</h3>
<p>Of course code like this is intended for an upgrade step!</p>
<p>The techniques described in this post are for sure interesting and powerful, but I would not rely on such low level tricks for routine operations, i.e. they are useful for doing migration or management scripts but <strong>definitely not suited for a browser view</strong>!</p>
<p>My use case was slightly different and more complicated: I was working with classes derived from the dexterity Item and Content classes, but I simplified it for making an already complex topic clearer.</p>
<p>If you have a more complicated case, <strong>further actions</strong> may be needed to complete properly this migration. In my case, for example, I also had to change the portal_type and remap some attributes.</p>
<div>I do not see any reason why the same methodology should fail with other kind of objects, Archetypes for examples.</div>
<p> </p>
<p>Many times I read about the suggestion of <strong>creating content types that are folderish even if not requested by the specification</strong>. As you can see this choice allows more flexibility and can save you from the need of writing migration scripts or upgrade steps at a later time.</p>
<h3>Credits</h3>
<p><span><a href="http://teach.alimomeni.net/2012fall2b/wp-content/uploads/2012/10/origami-box.jpg">Original image </a></span>adapted by <a class="external-link" href="https://twitter.com/@petraplatz">@petraplatz</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Alessandro Pisa</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>Dexterity</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    
      <dc:subject>tutorials</dc:subject>
    
    <dc:date>2013-02-25T13:05:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/migrazione-plone-4-tips-and-tricks">
    <title>Migrazione a Plone 4: alcuni utili tips and tricks</title>
    <link>http://blog.redturtle.it/migrazione-plone-4-tips-and-tricks</link>
    <description>Devi migrare un sito a Plone 4? Hai paura di non riuscirci? Ecco una breve guida che può aiutarti a uscire (quasi) indenne da questa avventura</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Con l’uscita di versioni aggiornate di un software (<strong>Plone</strong> nel nostro caso), ci si ritrova spesso a dover aggiornare le vecchie installazioni per tenerle al passo con le ultime versioni rilasciate e godere delle migliorie apportate e delle nuove funzionalità.</p>
<p>Negli ultimi mesi, il mio lavoro è stato principalmente quello di “<i>aggiornare</i>” dei vecchi siti e migrarli da Plone 3 a Plone 4 (lo so, in ritardo di un paio d'anni).</p>
<p>Esistono sostanzialmente due modalità per migrare un sito Plone:</p>
<ul>
<li>esportazione dei soli contenuti dal vecchio sito (per esempio con strumenti come transmogrifier) e importazione di questi in un nuovo ambiente immacolato.</li>
<li>migrazione del portale così com'è mediante il tool interno fornito da Plone stesso.</li>
</ul>
<p>La migrazione con transmogrifier, di cui abbiamo già <a class="external-link" href="http://blog.redturtle.it/2012/05/04/la-nobile-arte-della-migrazione-transmogrifier">parlato</a> precedentemente, in linea di massima è consigliata in quei casi in cui il vecchio portale potrebbe avere diverso “<i>sporco</i>” al suo interno, dovuto a svariati motivi (errori di gioventù dei programmatori, prodotti installati e mai utilizzati o mal rimossi, ecc.), oppure se si decide che parte dei contenuti attuali non servono più e si vuole portare dietro solo alcune sezioni.</p>
<p>Nel nostro caso avevamo degli ambienti abbastanza controllati, dove conoscevamo bene i prodotti installati (in parte sviluppati da noi e in parte trovati su pypi ma utilizzati da tempo) e il livello di sporcizia era minimo, ma soprattutto i portali dovevano essere migrati per intero.</p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<p class="breakText">Per questo motivo abbiamo deciso di utilizzare il tool di migrazione nativo di Plone.<br />L’utilizzo del tool è molto semplice, basta un click e fa tutto da solo, però il difficile è stato “<i>preparare</i>” i vari portali alla migrazione.<br />Il nostro problema era che dovevamo migrare una ventina di portali in sequenza e il rischio era di rimanere impantanati nelle varie procedure.<br />Dopo diverse prove e migrazioni fallite, abbiamo trovato una serie di linee guida che ci hanno aiutato a sopravvivere e che sono tornate utili anche in successive migrazioni.</p>
<h3 class="mceContentBody documentContent"><img src="http://blog.redturtle.it/topic_images/sherlock.jpg/@@images/d23a81c6-df41-48b7-9a33-56c56aa27959.jpeg" alt="sherlock" class="image-right" title="sherlock" />Analisi della situazione</h3>
<p>Come prima cosa, bisogna fare un’analisi approfondita dell’utilizzo del portale, dei prodotti installati e dei contenuti creati.<br />In questo modo si può avere una panoramica su quanti e quali contenuti sono stati effettivamente creati, quali prodotti sono veramente utilizzati e bisogna portarsi dietro, e quali sono inutilizzati e si possono rimuovere per ottimizzare ulteriormente il portale.</p>
<h3>Compatibilità dei prodotti su Plone 4</h3>
<p>Dopo l’analisi preliminare, è utile testare il corretto funzionamento dei prodotti da mantenere, su un buildout Plone 4.<br />Per quelli di terze parti scaricati da pypi, di solito non c’è bisogno di fare molto: basta una veloce ricerca su <a class="external-link" href="http://pypi.python.org/pypi">pypi</a> o su <a class="external-link" href="http://plone.org/products/">plone.org</a> e il gioco è fatto. Se poi non sono compatibili, vuol dire che o sono stati superati da altri prodotti più evoluti (e forse è meglio informarsi meglio sull'eventualità di sostituirli), oppure possiamo sempre aggiornarli noi, in modo da dare un utile contributo alla comunità.<br />I prodotti sviluppati appositamente per i clienti, invece, vanno per forza sistemati. In generale, se non si deve fare un refactoring pesante ma solo un aggiornamento, un buon inizio è quello di controllare se presi e messi su un Plone 4 girano correttamente, ed eventualmente correggere a mano a mano gli errori che si riscontrano.<br />Molto probabilmente diversi template o importazioni si romperanno, perché alcune cose in Plone 4 sono cambiate; nessun problema, visto che sul sito di plone.org c'è una guida dedicata: <a class="external-link" href="http://plone.org/documentation/manual/upgrade-guide">http://plone.org/documentation/manual/upgrade-guide</a> - in alternativa, Google o le varie mailing-list tornano sempre utili.</p>
<h3>Rimozione dei prodotti inutilizzati</h3>
<p>Il passo successivo consiste nella rimozione dei prodotti vecchi e/o inutilizzati.<br />E’ un passo importante, poiché non è consigliato portarsi dietro dei prodotti che non vengono utilizzati, o che già sappiamo non essere compatibili su Plone 4. Per questo vanno rimossi prima di effettuare una migrazione.</p>
<blockquote class="pullquote">Promemoria: fare sempre i profili di disinstallazione!</blockquote>
<p>Se un prodotto non è dotato di un profilo di disinstallazione fatto correttamente (e per esperienza posso dire che purtroppo molti sono così), potrebbe lasciare dei “<i>residui</i>” indesiderati nel nostro Data.fs.<br />Questi possono generare dei semplici warning  nel nuovo sito, come per esempio dei CSS o Javascript registrati e non più disponibili, ma nella maggior parte dei casi il problema riguarda adapter o persistent utilities che non vengono rimossi correttamente e rischiano di vanificare la migrazione.<br />Come per l’aggiornamento dei prodotti, se questi sono stati scaricati da pypi, una buona prassi è quella di sistemare i profili di disinstallazione dando un contributo alla comunità.<br /><img src="http://blog.redturtle.it/topic_images/cleanuptheworld.jpg/@@images/df582867-7747-42ba-8d31-f9bf63d5b63c.jpeg" alt="clean" class="image-right" title="clean" />Di solito le risorse che sono registrate e non vengono disinstallate correttamente sono quelle gestite con i profili di GenericSetup, come per esempio dei CSS o javascript, ma anche controlpanel e skin layers. Per avere degli esempi di come eseguire una corretta disinstallazione, basta cercare diversi prodotti nella collective (ad esempio <a class="external-link" href="https://github.com/RedTurtle/redturtle.smartlink.git">redturtle.smartlink</a>).<br />Se malauguratamente il prodotto non mette a disposizione il repository con i sorgenti (e può capitare), o sistemare il profilo di disinstallazione risulta troppo complicato, si riescono a rimuovere parecchie risorse anche manualmente.<br />Tutte quelle relative a GenericSetup si possono rimuovere da ZMI nei vari tool, ma i nostri nemici più agguerriti sono state le <strong>persistent-utilities</strong>, che sono persistenti ma non si riescono a visualizzare da ZMI.<br />Nessun problema, ci sono venute in aiuto due preziose risorse:</p>
<ul>
<li><a class="external-link" href="http://pypi.python.org/pypi/wildcard.fixpersistentutilities">wildacard.fixpersistentutilities</a>, un prodotto che aiuta a riconoscere e rimuovere da interfaccia utente eventuali persistent-utilities presenti nel sito</li>
<li>un'utilissima <a class="external-link" href="http://plone.org/documentation/kb/manually-removing-local-persistent-utilities/">guida</a> di Nathan Van Gheem che spiega come rimuovere manualmente adapters, utility e subscribers.</li>
</ul>
<h3>Blob Blob Blob</h3>
<p>Su Plone 4 è diventato di default il supporto ai blob, per ovvi motivi di miglioramento delle prestazioni e di separazione di file e immagini dal Data.fs vero e proprio.<br />Per questo motivo, la migrazione di Plone 4 include uno step apposito che provvede in autonomia a migrare i contenuti di tipo file e immagine presenti nel sito ai blob.<br />Se i vostri prodotti prevedono contenuti che contengono immagini o file, è buona norma dotarli del supporto ai blob (procedura spiegata in un paragrafo della <a class="external-link" href="http://plone.org/documentation/manual/upgrade-guide/version/upgrading-plone-3-x-to-4.0/updating-add-on-products-for-plone-4.0/use-plone.app.blob-based-blob-storage">guida</a> alla migrazione precedente) e di una procedura che permetta di migrare a blob i contenuti già presenti nel sito (ovviamente post-migrazione a Plone 4). Un buon esempio è fornito da <a class="external-link" href="https://github.com/RedTurtle/redturtle.smartlink/blob/master/redturtle/smartlink/browser/migrate.py">redturtle.smartlink</a>.</p>
<h3>E per finire... la migrazione</h3>
<p>Arrivati a questo punto, dopo aver verificato che tutti i prodotti siano compatibili con Plone 4 e aver ripulito il nostro Data.fs, possiamo procedere alla migrazione vera e propria.<br />Non serve altro che copiare i nostri dati nel nuovo buildout, farlo partire e andare nella ZMI del sito da migrare: un testo avvertirà che il sito è obsoleto e va aggiornato. Seguendo il link si avrà la possibilità di eseguire la migrazione in due modi:</p>
<ul>
<li>dry-run, cioè eseguendo tutti i passi della migrazione, ma non committando le modifiche alla fine: serve se si vogliono prima fare delle prove</li>
<li>migrazione vera e propria: esegue tutti i passi e, una volta terminati, committa le modifiche e le scrive nel db.</li>
</ul>
<p><img src="http://blog.redturtle.it/uploads/laptop7.jpg/@@images/7c73daa5-2fe1-4faa-a041-181bbe6cd788.jpeg" alt="laptop" class="image-right" title="laptop" />Ovviamente il tempo impiegato dipende da molti fattori, come per esempio la potenza della macchina, la dimensione del Data.fs e il fatto di avere già i file sui blob.<br />Non sempre la migrazione andrà a buon fine al <strong>primo colpo</strong>. Fortunatamente quasi sempre il traceback dei fallimenti ci dà buone informazioni su cosa è andato storto, in modo da poterlo sistemare e ritentare. Nella mia esperienza, spesso i problemi erano proprio con utility mal rimosse o classi non più trovate.</p>
<p>Alla fine di tutto il procedimento, quando avremo un sito aggiornato e funzionante, prima di effettuare i vari test è buona norma fare un giro nel pannello di gestione dei prodotti aggiuntivi, in modo da vedere se ci sono degli upgrade-step da lanciare per sistemare alcuni prodotti. Altra operazione importante da fare è il pack del db, perché consente di guadagnare diverso spazio, visto lo spostamento all’esterno di file e immagini.</p>
<h3>Conclusioni</h3>
<p>In conclusione, se si prepara per bene il db da migrare, rimuovendo correttamente tutti i componenti inutilizzati, il tool di migrazione<strong> funziona discretamente bene</strong>.</p>
<p>Abbiamo solo avuto alcuni problemi durante la migrazione dei blob di un sito, come riportato anche da Ross Patterson in un suo <a class="external-link" href="http://rpatterson.net/blog/poskeyerror-during-commit">post</a>. Dopo una lunga analisi, abbiamo notato che il poskey Error veniva lanciato durante il commit finale, ma non siamo riusciti (come l'autore) a risalire all'origine del problema. La soluzione è stata quella di effettuare dei commit intermedi alla fine della migrazione dei file e alla fine delle immagini, anziché alla fine di tutta la procedura.</p>
<p>Seguendo questa sorta di check-list, siamo riusciti a migrare abbastanza agilmente una ventina di siti, e l'abbiamo riutilizzata anche per successivi lavori.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Cecchi</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>Plone 4</dc:subject>
    
    
      <dc:subject>migration</dc:subject>
    
    <dc:date>2013-02-12T10:00:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/come-gestire-e-distribuire-i-vostri-prodotti-plone">
    <title>Come gestire e distribuire i vostri prodotti Plone?</title>
    <link>http://blog.redturtle.it/come-gestire-e-distribuire-i-vostri-prodotti-plone</link>
    <description>Un'esplorazione di tutti i (bizzarri) metodi con cui ho visto gestire il codice Plone (ma non solo), alla ricerca di una soluzione per ogni problema
</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Come gestire e distribuire i vostri prodotti Plone?</p>
<p>Sarà perché nella mia esperienza ho affrontato vari corsi di formazione per sviluppatori Plone, sarà perché non tutto il codice è sempre pronto per essere rilasciato, una cosa è certa: il rapporto tra il neofita e il codice da lui sviluppato è piuttosto combattuto.</p>
<p>Partiamo con una carrellata di quello che potete fare (ma in gran parte non dovete) fino ad arrivare alla soluzione dei casi più delicati.</p>
<p></p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<h2>Sviluppo old-style</h2>
<p>Nell'uso quotidiano di Plone, il nostro amico buildout ci ha abituati per anni all'uso della directory di <strong>develop</strong> (di solito, la directory <i>src</i>):</p>
<pre>[buildout]<br />...<br />develop =<br />    src/my.package<br />    ...</pre>
<p><img alt="Papyrus" class="image-right" src="http://blog.redturtle.it/topic_images/papyrus.jpg/@@images/5bc9c800-3a14-47cd-99e0-d0c0c6299621.jpeg" title="Papyrus" />Sebbene questo metodo oggi sia largamente superato (vedi <a class="external-link" href="http://pypi.python.org/pypi/mr.developer">mr.developer</a>), la comunità Plone lo ha utilizzato per anni e c'è ancora tanta documentazione in giro che ne parla.</p>
<p>Per molti utenti che non hanno dimestichezza con l'installazione di mr.developer (prima che questo diventasse parte dei template di buildout Plone rilasciati) questo modo è quello "più semplice".</p>
<p class="callout">Semplicemente: funziona!</p>
<h2>Versiona-che?</h2>
<p>Ammettiamo che vi troviate nella situazione di cui sopra: qual è la cosa peggiore che potreste trovare in un buildout?<br />Semplice! L'<strong>assenza del versionamento</strong> del codice.</p>
<p>Mi è capitato diverse volte di trovarmi in questa situazione: il <i>Cliente X</i> chiama per mettere mano a un buildout in cui erano presenti prodotti legacy proprio all'interno della cartella <i>src</i>.<br />Nei casi peggiori, si tratta di codice non versionato. Eppure ci viene chiesto di mettergli mano e di apportare un qualche tipo di modifica!</p>
<p>A volte può capitare di accorgersi che nemmeno la configurazione del buildout sia sotto controllo di versione...</p>
<p>Spesso il cliente si sente sufficientemente al sicuro sapendo che ha "<i>una copia da qualche parte</i>" (quando va bene), eppure è difficile fargli capire che la soluzione "backup" non è tutto.</p>
<p>Probabilmente siamo passati tutti per questa fase (detta anche fase del "<i>ero giovane, avevo bisogno di lavorare</i>"), ma spero per voi l'abbiate abbandonata abbastanza in fretta.</p>
<h2>Externals SVN</h2>
<p>Prima dell'esplosione di popolarità di <a class="external-link" href="http://git-scm.com/">Git</a>/<a class="external-link" href="https://github.com/">Github</a>, il software di versionamento del codice più in voga tra gli sviluppatori Plone è stato <a class="external-link" href="http://subversion.apache.org/">SVN</a>.<br />Ne è riprova il fatto che, ancora oggi, la <a class="external-link" href="http://svn.plone.org/svn/collective/">collective SVN</a> è un repository piuttosto utilizzato, anche se la <a class="external-link" href="https://github.com/collective/">nuova collective</a>, ospitata su GitHub, sta rapidamente prendendo piede.</p>
<p>Una delle caratteristiche di SVN era la possibilità di definire il contenuto di una directory sotto controllo di versione tramite la definizione di externals (<a class="external-link" href="http://svnbook.red-bean.com/en/1.0/ch07s03.html">svn:externals</a>), ossia permettere di caricare nella directory una serie di altri sorgenti provenienti (volendo) anche da altri repository.</p>
<p>Una volta compresa l'importanza del versionamento, il <i>Cliente X</i> potrebbe davvero apprezzare il fatto di sapere che il suo buildout è stato versionato, e che il contenuto della directory <i>src</i> contenente il suo codice è controllato da <i>svn:externals</i>, così che il sorgente di ogni prodotto venga scaricato dal repository stesso del prodotto.</p>
<p>Un esempio del formato che questa proprietà dovrebbe avere per due ipotetici prodotti Plone:</p>
<pre>my.package https://my.repository/svn/my.package/trunk<br />your.package https://your.repository/svn/your.package/trunk</pre>
<p>Il tutto ora è piuttosto robusto. L'unico problema riguarda l'utilizzo del <strong>trunk</strong> dei prodotti.<br />Questo tipo di configurazione può andare bene per un ambiente di sviluppo, ma come possiamo configurare l'ambiente di produzione?</p>
<p>Il <i>trunk</i> è per definizione una versione instabile o comunque sempre aggiornata di un prodotto; la possibilità che, aggiornando l'installazione o effettuandone una replica su un nuovo server (magari in seguito ad un disaster-recovery del sistema), il codice possa cambiare o aggiornarsi in qualche modo, è piuttosto pericolosa.</p>
<p>Per mantenere le cose stabili, si dovrebbe quindi riferirsi ai tag dei vari prodotti:</p>
<pre>my.package https://my.repository/svn/my.package/tags/1.2<br />your.package https://your.repository/svn/your.package/tags/1.4.1</pre>
<p>Il problema ora diventa degli sviluppatori, che sarebbero obbligati a gestire manualmente il cambio di repository da tag a trunk.</p>
<p>Come soluzione, per un certo periodo, abbiamo sperimentato l'uso di un doppia directory dei sorgenti, una per i tag di produzione e una per lo sviluppo.</p>
<p>Diciamo quindi di avere un buildout con un <i>production.cfg</i> in questa forma:</p>
<pre>[buildout]<br />...<br />develop =<br />    prod-src/my.package<br />    prod-src/your.package<br />    ...</pre>
<p>...e un development.cfg così:</p>
<pre>[buildout]<br /><br />extends = production.cfg<br />...<br />develop +=<br />    src/my.package<br />    src/your.package<br />    ...</pre>
<p>Le due directory <strong>prod-src</strong> e <strong>src</strong> venivano quindi configurate con due <i>svn:externals</i> differenti: uno che puntava ai tag (versioni stabili e di produzione) e l'altro alle versioni di sviluppo.</p>
<p>La situazione è solida, ma in presenza di molti prodotti personalizzati (10, 20, ...) questo sistema risente di lentezza in fase di aggiornamento.</p>
<p>Ci sono modi migliori: andiamo avanti.</p>
<h2>infrae.subversion</h2>
<p>L'approccio dell'uso di <i>svn:externals</i> aveva anche un altro aspetto negativo: i puristi (diciamo pure: i maniaci) di buildout amano controllare <i>tutto</i> nei propri file <i>.cfg</i>; nell'esempio al caso precedente viene lasciata un'importante parte della configurazione (quali sorgenti scaricare) a SVN.<br />In pratica, aprendo il file di configurazione buildout.cfg non siamo in grado di vedere dove e quali versioni dei prodotti stiamo utilizzando.</p>
<p>La recipe <a class="external-link" href="http://pypi.python.org/pypi/infrae.subversion">infrae.subversion</a> faceva esattamente questo e per un breve periodo ha risentito di una certa popolarità: che cosa scaricare usando SVN veniva configurato in un file .cfg!</p>
<pre>[svneggs]<br />recipe = infrae.subversion<br />location = src<br />urls =<br />    https://my.repository/svn/my.package/tags/1.2 my.repository<br />    ...</pre>
<p>La cosa non è andata avanti per molto, per vari aspetti negativi dell'approccio tra cui la lentezza (maggiore che usando <i>svn:externals</i>) e la fragilità (spesso gli aggiornamenti non andavano a buon fine).</p>
<p>Dimentichiamoci quindi anche di questo approccio.</p>
<h2>Habemus egg</h2>
<p>Torniamo al principio:<br />L'uso della sezione <i>develop</i> del nostro buildout è stata prevista per lo sviluppo.</p>
<p>Rendiamoci conto che tutti questi problemi si hanno solo in presenza di codice non pubblicamente rilasciato e rilasciabile: un qualunque prodotto <i>pubblico</i> viene scaricato automaticamente dal <a class="external-link" href="http://pypi.python.org/">Python Package Index</a> e ci dobbiamo preoccupare del suo sorgente solo se intendiamo mettergli mano per svilupparlo.</p>
<p>La soluzione migliore, da tempo documentata, è:</p>
<ul>
<li>poter utilizzare un server pypi privato (aziendale o del cliente)</li>
<li>rilasciare i prodotti nel formato standard python (gli egg per l'appunto).</li>
</ul>
<p>Il primo caso è in realtà piuttosto semplice: un <i>pypi server</i> non è altro che una directory di file accessibile via Web/HTTP.</p>
<p>Ammettendo di avere una directory su un server privato e che questa directory contenga tutti i nostri egg, questo semplice comando Python lanciato sul server permetterebbe di avere un repository di egg compatibile con buildout:</p>
<pre>$ cd pypi-archive<br />$ python -m SimpleHTTPServer 9000</pre>
<p>A questo punto va indicato al buildout che esiste questa possibilità:</p>
<pre>[buildout]<br />...<br />find-links =<br />    http://dist.plone.org/release/4.2.4<br />    http://dist.plone.org/thirdparty<br />    http://the.server:9000/</pre>
<p>Ovviamente un modo più robusto sarebbe quello di configurare un vero web server, come Apache.</p>
<p>Il problema in questo caso è che l'egg, una volta generato, andrebbe copiato manualmente in questa directory, mentre l'uso del Python Package Index ci ha abituati a un automatismo a cui è difficile rinunciare.<br />Presto detto: se volete ottenere un <strong>server in stile pypi perfettamente funzionante</strong>, potete usare Plone stesso e il prodotto <a class="external-link" href="http://plone.org/products/plonesoftwarecenter">PloneSoftwareCenter</a> (che supporta le <strong>Python Package Index API</strong>).</p>
<p><img alt="Egg" class="image-left" src="http://blog.redturtle.it/topic_images/egg.jpg/@@images/76541d7b-236c-45c6-9c87-41e965133fc6.jpeg" title="Egg" />Ora il secondo problema: com'è fatto un egg? Come posso generarlo e rilasciarlo?</p>
<p>Se per anni il rilascio di egg (pubblico o privato che fosse) necessitava di conoscere almeno i rudimenti di <a class="external-link" href="http://docs.python.org/2/distutils/index.html#distutils-index">distutils</a>, oggi ci sono fantastiche utility che nascondono tutta la complessità. In questo stesso blog potete leggere una <a class="internal-link" href="http://blog.redturtle.it/come-rilasciare-un-pacchetto-senza-piu-pensieri-zest.releaser">dettagliata descrizione del migliore di questi: zest.releaser</a>.</p>
<p>Come potete leggere dall'articolo, <strong>zest.releaser</strong> si occupa di tutte quelle operazioni di versionamento del codice necessarie al momento di una nuova release (aggiornamento, tag della versione, ...) e lo fa per varie tecnologie di versionamento (SVN, Git, Mercurial, ...).</p>
<p>Sembrerebbe quindi che abbiamo trovato la soluzione migliore per i nostri prodotti proprietari:</p>
<ul>
<li>codice versionato</li>
<li>buildout che usa mr.developer</li>
<li>server pypi riservato</li>
<li>uso di zest.releaser.</li>
</ul>
<h2>Ambienti poco amichevoli</h2>
<p><img alt="Endor" class="image-left" src="http://blog.redturtle.it/topic_images/endor.jpg/@@images/183fb037-52ba-43d8-92ff-5f554cd1bc23.jpeg" title="Endor" />Non abbiamo però definito che cosa si intende per "<i>server pypi riservato</i>". Questo può essere:</p>
<ul>
<li>un servizio pubblicamente accessibile ma protetto da autenticazione</li>
<li>un servizio senza autenticazione, ma posto su rete interna del cliente o dell'azienda.</li>
</ul>
<p>Nel primo caso, l'autenticazione può essere un problema: configurare buildout/Python perché richieda autenticazione non è esattamente triviale e può dare vari grattacapi.</p>
<p>Nel secondo caso tutto torna ad essere molto semplice, ma cosa succede se il servizio è nella rete interna del cliente e siete voi il fornitore che deve mettere mano al codice per poi rilasciare nuove versioni?<br />A meno di non possedere una VPN, la cosa non sta in piedi.</p>
<p>In più: ci sono situazioni in cui l'accesso alla rete dall'esterno (e a volte anche verso l'esterno) è fortemente limitato da policy di sicurezza e regole di firewall che farebbero impallidire lo scudo sulla luna boscosa di Endor.</p>
<p class="callout">La difficoltà di poter avere un repository di egg condiviso tra voi e il cliente può non essere banale.</p>
<p>Ci sono però alcuni prerequisiti su cui non si transige: i sistemisti avranno dovuto accettare per forza di cose che "<i>Plone non viene distribuito con un CD ma viene installato accedendo alla Rete</i>" e che "<i>Il buildout usa un sistema di versionamento a cui gli sviluppatori devono poter accedere</i>".</p>
<p>In questi casi limite, noi in RedTurtle abbiamo iniziato a usare un metodo forse non troppo pulito, ma semplice e senza controindicazioni.</p>
<h2>Pypi-local!</h2>
<p>L'idea è semplice:</p>
<ul>
<li>generare gli egg senza rilasciarli (ci piace rilasciare codice, ma non sempre si può)</li>
<li>inserire gli egg nel buildout, in una directory</li>
<li>versionare anche i sorgenti dell'egg.</li>
</ul>
<p>"<i>pypi-local</i>" è stato il primo nome dato alla directory atta ad ospitare gli "egg locali", e da allora non è più cambiato (se in RedTurtle qualcuno dice "<i>...è nella pypi-local</i>" tutti capiscono!).</p>
<p>Tenendo fede a questo nome, ecco come configurare il buildout:</p>
<pre>[buildout]<br />...<br />find-links =<br />    ./pypi-local<br />    http://dist.plone.org/release/4.2.4<br />    http://dist.plone.org/thirdparty</pre>
<p>In pratica, nella radice del buildout deve esistere questa directory.</p>
<p>Prima di impallidire per la poca eleganza di questo approccio, sappiate che questo ci ha aiutato in molte situazioni, non ultimo il caso seguente: dare un prodotto (sempre non rilasciato pubblicamente, a volte per mere questioni di "<i>non saprei se va bene per pypi</i>") a un cliente con un livello tecnico minimo, limitato solo al "<i>so lanciare un buildout</i>".<br />Come? Si manda l'egg per e-mail, con due righe di istruzioni e il gioco è fatto.</p>
<h2>rt.zestreleaser.pypilocal</h2>
<p>Fermo restando che nessuno vuole abbandonare l'uso di <i>zest.releaser</i>, l'uso della directory locale richiede la perdita di un automatismo e una copia manuale dell'egg generato nella directory:</p>
<pre>...<br />Register and upload to pypi (y/N)? <br />Register and upload to plone.org (Y/n)? n<br />Register and upload to redturtle (Y/n)? n<br />...<br />INFO: Finished full release.<br />INFO: Reminder: tag checkout is in /private/var/folders/Ka/Ka7qqP8VFb8dZJO8sohbrE+++TI/-Tmp-/...</pre>
<p><i>zest.releaser</i> esegue una serie di operazioni automatiche per rilasciare gli egg su tutti i server configurati, ma alla fine ci dice dove possiamo trovare la copia temporanea usata per queste operazioni ("<i>Reminder: tag checkout is in...</i>").</p>
<p>Ci basta andare cercare il nostro egg dentro a quella directory (nella sottodirectory <i>dist</i>).</p>
<p>Semplice? Sì, ma rimangono due considerazioni:</p>
<ul>
<li>la programmazione fa l'uomo pigro</li>
<li><i>zest.releaser</i> è maledettamente ben fatto, ed è <strong>pluggabile.</strong></li>
</ul>
<p>Per questo motivo (sì, esatto, solo per non dover copiare manualmente l'egg) abbiamo reso disponibile un <strong>plugin per zest.releaser</strong> che permette di copiare automaticamente l'egg nella directory "pypi-local" (o in qualunque directory vogliate): <a class="external-link" href="http://pypi.python.org/pypi/rt.zestreleaser.pypilocal/">rt.zestreleaser.pypilocal</a>.</p>
<p>La sua configurazione è molto semplice: vi basta modificare il vostro file <i>.pypirc</i> nel seguente modo:</p>
<pre>[distutils]<br />index-servers =<br /> pypi<br /> plone.org<br /> ...<br /><br />[rt.zestreleaser.pypilocal]<br />pypi-local = ../../pypi-local<br />global = /opt/global-pypi</pre>
<p>Nella sezione "<i>rt.zestreleaser.pypilocal</i>" dovete andare a specificare la directory (volendo anche più di una) che <i>zest.releaser</i> cercherà prima di chiedervi se intendete copiare lì i file.</p>
<p>L'esempio "<strong>../../pypi-local</strong>" non è casuale: se state usando un struttura di directory standard nel vostro buildout, quando lancerete i comandi di zest.releaser vi troverete dentro alla directory del vostro prodotto (solitamente qualcosa nella forma <i>buildout-root/src/my.package</i>). Il percorso relativo fornito rispecchia esattamente l'idea che la directory <i>pypi-local</i> sia nella radice del buildout.</p>
<p>L'output di <i>zest.releaser</i> cambierà in questo modo:</p>
<pre>...<br />Register and upload to pypi (y/N)? <br />Register and upload to plone.org (Y/n)? n<br />Register and upload to redturtle (Y/n)? n<br />Copy egg to folder ../../pypi-local (Y/n)? <br />Copy egg to folder /opt/global-pypi (Y/n)? n<br />...<br />INFO: Finished full release.<br />INFO: Reminder: tag checkout is in /private/var/folders/Ka/Ka7qqP8VFb8dZJO8sohbrE+++TI/-Tmp-/...</pre>
<h2>Conclusione</h2>
<p>Tra tutti i metodi descritti, spero ne abbiate trovato almeno uno che si adatti alle vostre esigenze.</p>
<p>Gli strumenti per fare le cose nel modo giusto e per evitare il caos nella gestione dei vostri add-on Plone ci sono tutti, e sono semplici da usare!</p>
<p><span class="discreet">L'immagine in testata è di <a class="external-link" href="http://www.flickr.com/photos/fifthconspiracy/">fifthconspiracy</a>.</span></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Luca Fabbri</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>buildout</dc:subject>
    
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>riuso</dc:subject>
    
    
      <dc:subject>plone.it</dc:subject>
    
    
      <dc:subject>tutorials</dc:subject>
    
    <dc:date>2013-02-08T10:55:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/caution-2013-meteor-will-hit-the-world-wide-web">
    <title>Caution: in 2013 a "Meteor" will impact the World (Wide Web)</title>
    <link>http://blog.redturtle.it/caution-2013-meteor-will-hit-the-world-wide-web</link>
    <description>Meteor is a very powerful Javascript framework: this short article demonstrate how to install, configure and create your first application using "Meteor"</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Nothing to do with the Mayan prophecies, quiet!</p>
<p>Some time ago I came across a tweet that talked about <a class="external-link" href="http://meteor.com/">Meteor</a> and, as usual, I tried to investigate the simple reading.</p>
<div></div>
<div id="_mcePaste">I knew of the potential offered by Node.JS but I never tried something, so I took the opportunity to understand more. Also, these types of frameworks are increasingly directing a new way to write web applications (interesting this <a class="external-link" href="http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/">article</a>). Furthermore, <a class="external-link" href="http://madewith.meteor.com/">here</a> you can find a lot of examples of application made with Meteor.</div>
<blockquote class="pullquote">Meteor runs on Node.js. Has it's own server</blockquote>
<div></div>
<div></div>
<div id="_mcePaste"></div>
<div id="_mcePaste">Without spending a lot of time, I will try to reproduce steps how to create from scratch the environment and a simple application.</div>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<p> </p>
<p>First, install Meteor downloading the <a class="external-link" href="http://win.meteor.com/">installer</a> (in my case, on an old Windows XP machine - yes, there is also for Windows!).</p>
<p>Completed the first step, go to the installation folder of Meteor and create the application.</p>
<p><img src="http://blog.redturtle.it/uploads/copy_of_1.GIF" alt="setup&amp;config" class="image-inline" title="setup&amp;config" /></p>
<p>Now, let's create pages, css and javascript files. Basically, file are structured in three folders:</p>
<ul>
<li>client</li>
<li>server</li>
<li>public.</li>
</ul>
<blockquote class="pullquote">Meteor uses MongoDB, through Javascript API called <a class="external-link" href="http://docs.meteor.com/#meteor_collection">"collection"</a></blockquote>
<p>All files in client directory are served only to clients (server doesn't know about them), files in public are resources most of the time, i.e. images. Server directory serves data to server. Everything outside those folders is shared between clients and server (it would be a good place to put scripts or templates to run on both, server and client, more about that other time). In our simple program we are going to use only client directory.</p>
<p>helloworld.css</p>
<pre>html {margin:0;background-color: #eee;}

body {
width: 640px;
margin:auto;
text-align: center;
font-family: 'Helvetica';
color: #aaa;
text-shadow:1px 1px #fff;
}

p {font-style: italic}
</pre>
<p>helloworld.html</p>
<pre>&lt;head&gt;
 &lt;title&gt;Hello World&lt;/title&gt;
&lt;/head&gt;
 
&lt;body&gt;
&lt;h1&gt;Hello&lt;/h1&gt;
&lt;p&gt;My very first application&lt;/p&gt;
&lt;div style="float:left;width:49%;text-align:left"&gt;{{&gt; hello}}&lt;/div&gt;
&lt;div style="float:right;width:50%;text-align:left"&gt; 
{{&gt; form}}&lt;br&gt;&lt;br&gt;
{{&gt; anotherform}}
&lt;/div&gt;
&lt;/body&gt;
 
&lt;template name="hello"&gt;
 {{somebody}}
&lt;/template&gt;
 
&lt;template name="form"&gt;
&lt;input id='newName' type='text'/&gt;&lt;input type='submit' value='Set!'/&gt;
&lt;/template&gt;

&lt;template name="anotherform"&gt;
 &lt;input id='newNameAppend' type='text'/&gt;&lt;input type='submit' value='Append!'/&gt;
&lt;/template&gt;

</pre>
<p>helloworld.js</p>
<pre>Session.set( 'somebody' , '' );
 
Template.hello.somebody = function(){
    return Session.get('somebody');
}
 
Template.form.events({
     'click input[type=submit]': function(){
     Session.set( 'somebody' , $('#newName').val() );
}
 
});

Template.anotherform.events({
     'click input[type=submit]': function(){
     Session.set( 'somebody' , Session.get( 'somebody') +'\n'+ $('#newNameAppend').val() );
}
 
});
</pre>
<p><br /> css file is trivial, the most important things are about html and js file.</p>
<p>Let's get straight to the .html, that could be put there whatever you would like to render. Just without links to CSS and/or other scripts. If needed to include something, just copy file inside file structure (client, server, public or root of your project) and Meteor will minify it and include along other files.</p>
<p>Furthermore, html is written in <a class="external-link" href="http://handlebarsjs.com/">Handlebars</a> syntax, a powerful templates for javascript.</p>
<p>Inside curly brackets you put names of variables (<strong>somebody</strong>) or templates (<strong>&gt; hello</strong>). "&gt;" sign means that you are including a template. In our case we are including <strong>hello, form</strong> and <strong>anotherform</strong> templates which definitions you can find between template tags.</p>
<p>Let's talk about .js: In <strong>helloworld.js</strong> we set up a default value (void) for our session variable</p>
<pre>Session.set( 'somebody' , '' );</pre>
<p>Now that we have a variable inside a session, our template can use it to display</p>
<pre>Template.hello.somebody = function(){
return Session.get('somebody');
}</pre>
<p>All templates you setup are available in a namespace <strong>Template</strong>. In this case we have <i>Template.hello, Template.form</i> and <i>Template.anotherform</i> already defined for us by Meteor. To use {{somebody}} in the template, we must define that variable to get a proper display. We make it a simple function that returns our session value with <strong>Session.get('somebody')</strong>.</p>
<p>Finally, let's talk about <i>onclick events</i>:</p>
<pre>Template.form.events({
 'click input[type=submit]': function(){
 Session.set( 'somebody' , $('#newName').val() );
 }
});</pre>
<pre>Template.anotherform.events({
     'click input[type=submit]': function(){
     Session.set( 'somebody' , Session.get( 'somebody') +'\n'+ $('#newNameAppend').val() );
}
});
</pre>
<p>Using Template namespace we are reaching <strong>form </strong>and <strong>anotherform </strong>template. The first sets and displays the value submitted in text field, the second appends to present values just submitted. To handle events we put an object with definition inside an event function.</p>
<pre>Template.form.events( {} );
</pre>
<p><br /> That's it! Notice that each time we edit a file, no reload of our page is needed: it's automatically updated "on-the-fly".</p>
<p>This is a screenshot of the whole application, very simple but, IMHO, very clear. Stay tuned for something more complex about Meteor application in next posts.</p>
<p><img src="http://blog.redturtle.it/uploads/copy_of_example.GIF" alt="example" class="image-inline" title="example" /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Baglioni</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>Node.js</dc:subject>
    
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>javascript</dc:subject>
    
    
      <dc:subject>web</dc:subject>
    
    <dc:date>2013-01-30T09:15:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/buildout-plone-vs-forefront-tmg-using-cntlm">
    <title>Break free from forefront TMG proxy</title>
    <link>http://blog.redturtle.it/buildout-plone-vs-forefront-tmg-using-cntlm</link>
    <description>I was asked to install and configure a debian server to run a Plone site behind Microsoft Forefront TMG. Mission impossible? No... just a story to tell!</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<blockquote class="pullquote">It was a dark and stormy night, me and the server, Loca, left all alone in a chilly room.</blockquote>
<p>To let a linux server connect with <strong>Forefront</strong> I wanted to set up and run <a class="external-link" href="http://cntlm.sourceforge.net/">cntlm</a>, which is a local proxy that authenticates to a Forefront gateway to  allow internet traffic.</p>
<a name="anchor-breaktext"></a><div class="breakText">I had to install it manually on a debian server after that a minimal installation with the <a class="external-link" href="http://www.debian.org/CD/netinst/">netinst cd</a> was aborted.</div>
<p>The server was offline, I had to think about an <strong>offline method</strong> to install cntlm. I downloaded the deb from <a class="external-link" href="http://ftp.debian.org/pool/main/c/cntlm/cntlm_0.35.1-5_i386.deb">http://ftp.debian.org/pool/main/c/cntlm/cntlm_0.35.1-5_i386.deb</a> and saved it on a USB pen drive, then installed the deb with dpkg:</p>
<pre>dpkg - i cntlm_0.35.1-5_i386.deb
</pre>
<p>Once I got cntlm installed I tested my Forefront connection parameters using the <i>"magic"</i> <strong>-M</strong> flag:</p>
<pre>cntlm -fv -l $PORT -u $USER@$DOMAIN -p $PASSWORD \
      -w $NETBIOS_NAME -M $URL $TGMIP $TGMPORT
</pre>
<p>This will gave me hints to edit /etc/cntlm.conf file properly in order to run cntlm in daemon mode.</p>
<blockquote class="pullquote">I had just a debian network install cd with me and I asked Loca if she wanted some.</blockquote>
<p>A short explanation of the arguments in the command line follows:</p>
<ul>
<span class="discreet"> </span>
<li><span class="discreet"><strong> -fv</strong> run cntlm in foreground and verbose mode</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>-l $PORT</strong> listen to the specified port (default 3128)</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>-p $PASSWORD</strong> you should know it...</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>-u $USER@$DOMAIN</strong> the user that authenticates to Forefront (e.g. alert@redturtle)</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>-w $NETBIOS_NAME</strong> </span><span class="discreet">the w</span>orkstation NetBIOS name<span class="discreet"> (</span><span class="discreet">e.g. REDTURTLE)</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>-M $URL</strong> probe a test url to make "magic detection" (e.g. http://www.example.org)</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>$TGMIP</strong> the Forefront gateway ip address (e.g. 10.0.0.10)</span></li>
<span class="discreet"> </span>
<li><span class="discreet"> <strong>$TGMPORT</strong> the port Forefront gateway is listening to (e.g. 8080)</span></li>
</ul>
<p>More information on the cntlm man page (check out the <a class="external-link" href="http://man.devl.cz/man/1/cntlm">online version</a>).</p>
<h3>Environment variables</h3>
<p>With cntlm up and running daemonized by the init scripts, to use the proxy I had to define the proper environment variables:</p>
<pre>export http_proxy=http://127.0.0.1:3128
export https_proxy=http://127.0.0.1:3128
export ftp_proxy=http://127.0.0.1:3128 </pre>
<h3>apt</h3>
<p>I had also to setup apt, adding a file /etc/apt/apt.conf.d/02proxy containing those lines:</p>
<pre>Acquire::http::proxy "http://localhost:3128/";
Acquire::ftp::proxy "ftp://localhost:3128/";
Acquire::https::proxy "https://localhost:3128/";
</pre>
<p><span class="discreet">Source: <a class="external-link" href="http://askubuntu.com/questions/89437/how-to-install-packages-with-apt-get-on-a-system-connected-via-proxy">http://askubuntu.com/questions/89437/how-to-install-packages-with-apt-get-on-a-system-connected-via-proxy</a></span></p>
<h3>Subversion</h3>
<p>To use svn for checkouts I had to look in /etc/subversion/servers  for the "[Global]" section and added  a couple of lines like in this example:</p>
<pre>[Global]
...
http-proxy-host=localhost
http-proxy-port=3128
... </pre>
<p><span class="discreet"> Source: <a class="external-link" href="http://stackoverflow.com/questions/82530/svn-over-http-proxy">http://stackoverflow.com/questions/82530/svn-over-http-proxy</a></span></p>
<h3>Buildout</h3>
<blockquote class="pullquote">After a few moments we were friends and decided to join our efforts from freedom.</blockquote>
<p>Once I apt-got all the packages I needed and checked out my Plone buildout I thought everything would be fine.<br /><br /><strong><i>Of course I was wrong!</i></strong><br />There's an issue, apparently with urllib, that makes buildout fail (see <a class="external-link" href="https://bugs.launchpad.net/zc.buildout/+bug/484735">https://bugs.launchpad.net/zc.buildout/+bug/484735</a>, <a class="external-link" href="https://github.com/buildout/buildout/issues/32">https://github.com/buildout/buildout/issues/32</a>).</p>
<p>I forked buildout on github to work around this issue.</p>
<p>You can clone  <a class="external-link" href="https://github.com/ale-rt/buildout/tree/issue-32">https://github.com/ale-rt/buildout/tree/issue-32</a> and can get the source tarball from <a class="external-link" href="https://github.com/downloads/ale-rt/buildout/zc.buildout-1.6.4-issue-32-1.tar.gz">https://github.com/downloads/ale-rt/buildout/zc.buildout-1.6.4-issue-32-1.tar.gz</a>.</p>
<p>To use it I suggest you to create a virtual env in your buildout directory, add in the buildout root a folder (e.g. pypi-local), download to that folder the tarball and "pip install" it, the commands are something like:</p>
<pre>virtualenv cntlm<br />cd cntlm/<br />. bin/activate<br />mkdir pypi-local<br />cd pypi-local/<br />wget https://github.com/downloads/ale-rt/buildout/zc.buildout-1.6.4-issue-32-1.tar.gz<br />cd ..<br />./bin/pip install pypi-local/zc.buildout-1.6.4-issue-32-1.tar.gz </pre>
<p>To use this folder as a <strong>local pypi repository</strong> in your buildout, edit your buildout configuration file and add to the <strong>find-links</strong> this folder. Don't forget to pin the zc.buildout version!</p>
<pre>[buildout]<br />...<br />find-links = <br />    ...<br />    file://${buildout:directory}/pypi-local<br />    ...<br /><br />[versions]<br />...<br />zc.buildout = 1.6.4-issue-32-1</pre>
<p>With this setup buildout runs fine :)!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Alessandro Pisa</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>buildout</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    <dc:date>2013-01-17T15:00:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/how-to-write-custom-plugin-for-nagios-and-check_mk">
    <title>A custom port scanner plugin for Nagios and Check_mk</title>
    <link>http://blog.redturtle.it/how-to-write-custom-plugin-for-nagios-and-check_mk</link>
    <description>“I can't believe how dark was the time without a monitoring system...”. This is what my boss told me last time we spoke about reliability of service</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Some months ago I <a class="external-link" href="http://blog.redturtle.it/plone-e-i-sistemi-di-monitoraggio">posted</a> (Italian only) about <a class="external-link" href="http://http//mathias-kettner.de/check_mk.html">Check_mk</a> as a monitoring tool which can be used also to taking care of Plone instances. This time I will post a small custom plugin I wrote for <a class="external-link" href="http://www.nagios.org">Nagios</a>.</p>
<h3>Check_mk scan_port plugin<img alt="code" class="image-right" src="http://blog.redturtle.it/uploads/code.jpg" title="code" /></h3>
<p> </p>
<p> </p>
<p></p>
<a name="anchor-breaktext"></a><div class="breakText">I was looking for a way (a plugin) to test some specific ports on my servers within the local network. Surfing the net I found many scripts that do almost what I wanted (especially one written in Perl as <a class="external-link" href="http://nmap.org/">nmap</a> wrapper) but despite of all I decide to write my own.</div>
<p>The reason of this choice is because most of them make use of system tools in order to perform port scan action. There is no problem with that, tools like <strong>nmap</strong> are really commons, but what I want is avoid any kind of dependence.</p>
<p>Moreover I prefer have control on the code since the beginning mainly because I can choose the programming language ;)</p>
<h3>The code</h3>
<p>Check_port plugin is written in <a class="external-link" href="http://www.python.org">Python</a> and it uses only the standard library thus it should be portable as long as you have Python installed on your system.</p>
<p>The code is quite simple, for each given port the program test if the connection is active.</p>
<div><strong>Note: </strong><i>This test tells you if something is listening but nothing more.</i></div>
<div><i><br /></i></div>
<div>Source: <a class="external-link" href="https://github.com/nicolasenno/check_mk_plugins/tree/master/check_ports">github repository</a></div>
<h3>Usage</h3>
<div>Plugin accepts 3 arguments:</div>
<div>
<ul>
<li>-o &lt;ip&gt;  : remote host to scan</li>
<li>-p &lt;ports&gt;   : list of ports to scan</li>
<li>-t &lt;timeout&gt;] (optional)</li>
</ul>
</div>
<div><br />Example from command line:</div>
<pre># Scan for port 22 and 25 on a remote server 192.168.1.100<br />./check_ports.py -o 192.168.1.100 -p 22,25
</pre>
<div>Only two return states are allowed:</div>
<div>
<ul>
<li>OK: All the given ports are opened</li>
<li>CRITICAL: At list one of the given port is closed</li>
</ul>
</div>
<div>State: <strong>OK</strong></div>
<pre>./check_ports.py -o 188.165.195.56 -p 22<br />OK; opened 22 ; closed</pre>
<div>State <strong>CRITICAL</strong></div>
<pre>./check_ports.py -o 188.165.195.56 -p 23<br />CRITICAL; opened  ; closed 23</pre>
<h3></h3>
<h3>Check_mk configuration</h3>
<div>The easier way to configure the plugin is define a command as legacy check, and</div>
<div><strong>Check_mk </strong>will do the rest:</div>
<pre># 'check_ports'<br />define command{<br />  command_name check_ports<br />  command_line $USER1$/check_ports.py -o $ARG1$ -p $ARG2$ <br />}</pre>
<div>Then you can specify a list of remote server to test as show below:</div>
<pre>(( "Check_ports!127.0.0.1!22,23", "Check ports", False), ['host'])</pre>
<p><strong>Note:</strong> <i>be sure you are not blocked by a firewall.</i></p>
<div></div>
<div>At the moment this plugin accepts only a list of ports, maybe in future will be possible to use also a range.</div>
<div></div>
<div></div>
<div>If you need more information on how to use <i>lagacy plugin</i> in <strong>Check_mk</strong> you can find some resource <a class="external-link" href="http://mathias-kettner.de/checkmk_legacy_checks.html">here</a>.</div>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicola Senno</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>Networking</dc:subject>
    
    
      <dc:subject>Monitoring</dc:subject>
    
    
      <dc:subject>integration</dc:subject>
    
    <dc:date>2013-01-11T10:00:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/lotus-domino-twitter-way-for-mobile-approach">
    <title>Mixing Lotus Domino and Twitter together - a way for mobile approach</title>
    <link>http://blog.redturtle.it/lotus-domino-twitter-way-for-mobile-approach</link>
    <description>What if you want to create different layouts for different devices in Lotus Domino? This is a way to make this possible, for example using Twitter Bootstrap </description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>It's always more important to develop applications that are accessible from any device so we wondered what it takes to adapt an application, perhaps already developed, to ensure this possibility. In this case, Twitter provides its layout via <a class="external-link" href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a>, so it was decided to integrate it.</p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<div id="_mcePaste">Twitter Bootstrap requires the HTML 5 doctype as you can see in the <a class="external-link" href="http://twitter.github.com/bootstrap/scaffolding.html#global">documentation</a>.</div>
<div>Since version 8.5.3 Lotus Domino allows to define, for the XPages, this kind of doctype, in this way:</div>
<div><br /><img src="http://blog.redturtle.it/uploads/copy_of_doctype.PNG" alt="doctype" class="image-inline" title="doctype" /></div>
<p>Next step: we imported the resources you need to Twitter Bootstrap within the Domino project:</p>
<p><img src="http://blog.redturtle.it/uploads/copy_of_resources.png" alt="resources" class="image-inline" title="resources" /></p>
<p>And finally, we have created an XPages with the following source code:</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xp:view xmlns:xp="http://www.ibm.com/xsp/core"&gt;
    &lt;xp:this.resources&gt;
       &lt;xp:styleSheet href="bootstrap.css"&gt;&lt;/xp:styleSheet&gt;
       &lt;xp:styleSheet href="bootstrap-responsive.css"&gt;&lt;/xp:styleSheet&gt;
       &lt;xp:metaData name="viewport" content="width=device-width, initial-scale=1.0"&gt;
       &lt;/xp:metaData&gt;
    &lt;/xp:this.resources&gt;
    &lt;!-- NAVIGATION BAR --&gt;
    &lt;br/&gt;&lt;br/&gt;
  &lt;xp:panel styleClass="navbar navbar-inverse navbar-fixed-top"&gt;
    &lt;xp:panel styleClass="navbar-inner"&gt;
    &lt;xp:panel styleClass="container"&gt;
    &lt;xp:panel styleClass="row"&gt;
    &lt;xp:panel styleClass="span4"&gt;
      &lt;xp:label id="label1"&gt;
        &lt;xp:this.value&gt;&lt;![CDATA[#{javascript:"Welcome " + @Name("[CN]",@UserName())}]]&gt;&lt;/xp:this.value&gt;
      &lt;/xp:label&gt;
    &lt;/xp:panel&gt;
    &lt;xp:panel styleClass="span6"&gt;
      &lt;ul class="nav nav-pills"&gt;
       &lt;li class="dropdown"&gt;
         &lt;a class="dropdown-toggle" data-toggle="dropdown" href="#"&gt;
           &lt;i class="icon-white icon-file"&gt;&lt;/i&gt; Dropdown &lt;b class="caret"&gt;&lt;/b&gt;
         &lt;/a&gt;
         &lt;ul class="dropdown-menu"&gt;
           &lt;li&gt;&lt;a tabindex="-1" href="#"&gt;Action&lt;/a&gt;&lt;/li&gt;
           &lt;li&gt;&lt;a tabindex="-1" href="#"&gt;Another action&lt;/a&gt;&lt;/li&gt;
           &lt;li&gt;&lt;a tabindex="-1" href="#"&gt;Something else here&lt;/a&gt;&lt;/li&gt;
           &lt;li class="divider"&gt;&lt;/li&gt;
           &lt;li&gt;&lt;a tabindex="-1" href="#"&gt;Separated link&lt;/a&gt;&lt;/li&gt;
         &lt;/ul&gt;
      &lt;/li&gt;
     &lt;/ul&gt;
    &lt;/xp:panel&gt;
    &lt;xp:panel styleClass="span2"&gt;
      &lt;xp:label value="Logout" id="label3"&gt;&lt;/xp:label&gt;
    &lt;/xp:panel&gt;
   &lt;/xp:panel&gt;
  &lt;/xp:panel&gt;
 &lt;/xp:panel&gt;
 &lt;/xp:panel&gt;
 &lt;!-- APPLICATION LAYOUT --&gt;
 &lt;xp:panel styleClass="subhead"&gt;
   &lt;xp:panel styleClass="container"&gt;
     &lt;xp:panel styleClass="row"&gt;
       &lt;xp:panel styleClass="span4"&gt;
        &lt;ul class="nav nav-list sidebar-nav-fixed"&gt;
          &lt;li class="nav-header"&gt;List header&lt;/li&gt;
          &lt;li class="active"&gt;&lt;a href="#"&gt;&lt;i class="icon-white icon-home"&gt;&lt;/i&gt;Home&lt;/a&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;a href="#"&gt;&lt;i class="icon-book"&gt;&lt;/i&gt;Library&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href="#"&gt;&lt;i class="icon-pencil"&gt;&lt;/i&gt;Applications&lt;/a&gt;&lt;/li&gt;
          &lt;li class="nav-header"&gt;Another list header&lt;/li&gt;
          &lt;li&gt;&lt;a href="#"&gt;&lt;i class="icon-user"&gt;&lt;/i&gt;Profile&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href="#"&gt;&lt;i class="icon-cog"&gt;&lt;/i&gt;Settings&lt;/a&gt;&lt;/li&gt;
          &lt;li class="divider"&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href="#"&gt;&lt;i class="icon-flag"&gt;&lt;/i&gt;Help&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
       &lt;/xp:panel&gt;&lt;br/&gt;
       &lt;xp:panel styleClass="span8"&gt;
         &lt;xp:table&gt;
           &lt;xp:tr&gt;
            &lt;xp:td&gt;&lt;xp:label value="First Name" id="label2"&gt;&lt;/xp:label&gt;&lt;/xp:td&gt;
            &lt;xp:td&gt;&lt;xp:inputText id="inputText1"&gt;&lt;/xp:inputText&gt;&lt;/xp:td&gt;
           &lt;/xp:tr&gt;
           &lt;xp:tr&gt;
            &lt;xp:td&gt;&lt;xp:label value="Last Name" id="label4"&gt;&lt;/xp:label&gt;&lt;/xp:td&gt;
            &lt;xp:td&gt;&lt;xp:inputText id="inputText2"&gt;&lt;/xp:inputText&gt;&lt;/xp:td&gt;
           &lt;/xp:tr&gt;
           &lt;xp:tr&gt;
            &lt;xp:td&gt;&lt;xp:label value="Address" id="label5"&gt;&lt;/xp:label&gt;&lt;/xp:td&gt;
            &lt;xp:td&gt;&lt;xp:inputTextarea id="inputTextarea1"&gt;&lt;/xp:inputTextarea&gt;&lt;/xp:td&gt;
           &lt;/xp:tr&gt;
           &lt;xp:tr&gt;
            &lt;xp:td&gt;&lt;xp:label value="Phone" id="label6"&gt;&lt;/xp:label&gt;&lt;/xp:td&gt;
            &lt;xp:td&gt;&lt;xp:inputText id="inputText3"&gt;&lt;/xp:inputText&gt;&lt;/xp:td&gt;
           &lt;/xp:tr&gt;
         &lt;/xp:table&gt;
       &lt;/xp:panel&gt;
      &lt;/xp:panel&gt;
     &lt;/xp:panel&gt;
    &lt;/xp:panel&gt;
  &lt;script type="text/javascript" src="js/jQuery-1.8.0.min.js" clientSide="true"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="js/bootstrap.js" clientSide="true"&gt;&lt;/script&gt;
&lt;/xp:view&gt;</pre>
<p><br /> This is what you get on the page number from the web:<img src="http://blog.redturtle.it/uploads/copy4_of_preview.PNG" alt="preview" class="image-inline" title="preview" /><br />Of course, nothing prevents you from using any other responsive layout: Twitter is only one of the choices!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Baglioni</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>Lotus</dc:subject>
    
    
      <dc:subject>Domino</dc:subject>
    
    
      <dc:subject>HTML5</dc:subject>
    
    
      <dc:subject>xPages</dc:subject>
    
    <dc:date>2012-11-28T15:20:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/internazionalizzazione-javascript-plone">
    <title>Internazionalizzazione e javascript a braccetto in Plone</title>
    <link>http://blog.redturtle.it/internazionalizzazione-javascript-plone</link>
    <description>Ci sono un italiano, uno spagnolo, un francese e un inglese che... vogliono visitare il nostro sito multilingua in Plone. Come trattiamo le traduzioni se c'è tanto javascript?</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Capita di dover sviluppare siti che sfruttano javascript per presentare  dati e testi. Se il sito è multilingua, anche questi dati e questi testi dovranno essere tradotti coerentemente con la lingua in cui si sta visualizzando il sito.</p>
<div class="pageBreak">Se le parole e le frasi da tradurre sono poche, questo compito può essere portato a termine tramite un dizionario. Supponiamo infatti di dover aprire un alert con un messaggio. Sarà sufficiente avere qualcosa di questo tipo:</div>
<pre><br /> // Tramite jQuery si prende la lingua corrente dall'attributo del tag html <br /> var lang = jq('html').attr('lang');<br /><br /> // si dichiara il vocabolario con le traduzioni nelle varie lingue
 var messages = {'it': 'Benvenuti',<br />                 'en': 'Wellcome',<br />                 'es': 'Bienvenido',<br />                 'fr': 'Bienvenue'};<br /><br /> // apriamo l'alert scegliendo la traduzione opportuna.
 alert(messages[lang]);</pre>
<p>Se la pagina web che stiamo creando ha molte parti caricate via javascript, e quindi molti testi e parole da presentare tradotte, questo approccio è scomodo; innanzitutto perché si rischia di dover mantere molto codice solo per le traduzioni.<br />Se siamo in un caso come questo, il sito sviluppato probabilmente è già multilingua e i dati verranno presentati correttamente. Per quanto riguarda i testi da mostrare in javascript, è meglio sfruttare un motore di traduzioni, e in Plone, grazie alla community, un'utility di questo tipo esiste già: <strong>jarn.jsi18n</strong>. Grazie a questo prodotto, infatti, è possibile sfruttare appieno il message factory di plone e tutti i suoi file di traduzioni.</p>
<a name="anchor-breaktext"></a><div class="breakText"></div>
<h3>Come usarlo</h3>
<p>Il funzionamento è semplice. Una volta installato il prodotto nel sito, tutto quello che dobbiamo fare è caricare un catalogo e creare un message factory nel javascript che scriviamo:</p>
<pre><pre><br /> // Tramite jQuery si ottiene la lingua corrente dall'attributo del tag html<br /> var lang = jq('html').attr('lang');<span style="text-align: start; float: none; "><br />  <br /> // Dato un dominio di traduzioni e una lingua si ottiene un catalogo dal server;<br /> // questo restituirà al browser un vocabolario tipo<br /> // {...</span><code class="focusRow wrappedText">"from_date": "Dal"</code><span style="text-align: start; float: none; ">, "to_date": "Al", ...}<br /> jarn.i18n.loadCatalog('fta.at', lang);</span><br /> <br /> // Creiamo un message factory javascript che sfrutta il catalogo restituito dal server<br /> mf = jarn.i18n.MessageFactory('fta.at');</pre>
<span style="text-align: start; float: none; "> <br /></span></pre>
<p><span style="text-align: start; float: none; ">Da qui in poi si potrà procedere utilizzando il message factory in modo molto 'plonish':</span><span class="source-code console-message-text"> </span></p>
<pre><span class="source-code console-message-text"><br /> // Si può iniziare a tradurre stringhe<br /> &gt; mf('How to get here')</span><span class="source-code console-message-text"><span><span class="source-code">:<br /> "<span class="source-code console-formatted-string">Come arrivare</span>"<br /><br /></span></span></span></pre>
<p>E se abbiamo dei testi da costruire dinamicamente: <span class="source-code console-message-text"> </span></p>
<pre><span class="console-message-text source-code"> <br /> // Si possono tradurre anche stringhe in modo più dinamico, esattamente come in plone.<br /> &gt; mf('other_results')</span><span class="console-message-text source-code"><span><span class="source-code">;<br /> "<span class="console-formatted-string source-code">... e altre ${results} voci</span>"</span></span></span><span class="source-code console-message-text"><br /><br /> &gt; mf('other_results', {'results': 10})</span><span class="source-code console-message-text"><span><span class="source-code">;<br /> "<span class="source-code console-formatted-string">... e altre 10 voci</span>"<br /><br /></span></span></span></pre>
<h3><br />Under the hood</h3>
<p>Come funziona tutto questo? Guardando il codice, ci si può rendere conto di come cose utili e funzionali possano essere create in modo semplice. Il pacchetto si compone di una vista e di un javascript. La vista è una classica BrowserView Plone che restituirà una stringa json con i dati richiesti:</p>
<pre> <br /> def __call__(self, domain, language):<br />     ...<br /><br />     # Si ottiene l'utility plone per le traduzioni<br />     td = queryUtility(ITranslationDomain, domain)<br />     if td is None or language not in td._catalogs:<br />         return<br /><br />     # Si ottiene il catalogo per il dominio nella lingua richiesta<br />     mo_path = td._catalogs[language][0]<br />     catalog = td._data[mo_path]._catalog<br />     if catalog is None:<br />         td._data[mo_path].reload()<br />         catalog = td._data[mo_path]._catalog<br />     ...<br /><br />     # si restituisce il catalogo in formato json. <br />     # Qualcosa del tipo: '<span style="text-align: start; float: none; ">{...</span><code class="wrappedText focusRow">"from_date": "Dal"</code><span style="text-align: start; float: none; ">, "to_date": "Al", ...}'</span><br />     response.setBody(json.dumps(catalog))<br />     return response</pre>
<p>Il javascript si occupa della chiamata al server per ottenere il catalogo che verrà gestito interamente lato client: una cosa molto interessante in questa gestione è, nei browser che lo consentono, l'uso dello <a class="external-link" href="http://en.wikipedia.org/wiki/Web_storage">storage del browser</a>, tramite cui si riesce a fare caching di questo catalogo e non ricaricarlo ogni volta che si chiama una pagina:</p>
<pre> <br /> // si salva il catalog nello storage con una chiave associata<br /> var key = domain + '-' + language;<br /><br /> // se abbiamo già quella chiave nello storage verificheremo se il catalogo è ancora valido.<br /> if (key in localstorage) {<br />     if ((Date.now() - parseInt(localstorage.getItem(key + '-updated'), 10)) &lt; ttl) {<br />         var catalog = JSON.parse(localstorage.getItem(key));<br />         jarn.i18n._setCatalog(domain, language, catalog);<br />         return;<br />         }<br />     }</pre>
<p>Come sempre, conoscere bene l'architettura e i componenti di Plone permette di svolgere lavori più o meno complicati in modo semplice, ma soprattutto pulito.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Luca Bellenghi</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>javascript</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    <dc:date>2012-11-21T13:40:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>


  <item rdf:about="http://blog.redturtle.it/content-type-products-contentmigration">
    <title>Trasformiamo i nostri content-type in modo semplice con Products.contentmigration</title>
    <link>http://blog.redturtle.it/content-type-products-contentmigration</link>
    <description>Breve guida pratica su come utilizzare Products.contentmigration per migrare i contenuti di un sito da un content-type a un altro senza diventare matti</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Negli ultimi mesi abbiamo dovuto migrare una serie di portali dal vecchio Plone 3.3.5 alla nuova release 4.2.<br />Questa operazione è anche stata l'occasione per fare un po' di rifattorizzazione dei prodotti (necessaria in alcuni casi, per farli funzionare anche su Plone 4) e di pulizia generale dei contenuti.</p>
<blockquote class="pullquote">"Quei content-type sono vecchi... cambiamoli!"<br /></blockquote>
<p>Su alcuni portali avevamo una serie di content-type creati ad hoc per esigenze passate, poi diventati obsoleti o perché le funzionalità sono state implementate (meglio) in altri prodotti, o perché non abbiamo più bisogno di loro.<br />Per esempio, avevamo creato diversi content-type che aggiungevano funzionalità a quelli base di Plone (al tempo non conoscevamo ancora la potenza di <a class="external-link" href="http://pypi.python.org/pypi/archetypes.schemaextender">archetypes.schemaextender</a>) sovrascrivendoli, e ora dovevamo tornare indietro per poter ricominciare a utilizzare le versioni base.</p>
<p><img alt="shape box" class="image-left" src="topic_images/wooden_toy_shape_sorter_block_box.jpg/@@images/5baeda04-b99b-4491-8e72-a4162ec3d576.jpeg" title="shape box" />Tornare a utilizzare le versioni standard è semplice, basta disinstallare il nostro prodotto.<br />Cosa fare però con i contenuti già creati? Come torniamo a una situazione "standard" senza rompere niente?<br />E' come avere tra le mani una serie di cubi avendo a disposizione solo buchi tondi: non funzioneranno più, ma non li possiamo perdere. Vanno sostituiti.</p>
<p class="callout">La soluzione è facile: li migriamo!</p>
<p> </p>
<p></p>
<a name="anchor-breaktext"></a><div class="breakText">Per sostituire i vecchi content-type abbiamo utilizzato un prodotto chiamato <a class="external-link" href="http://pypi.python.org/pypi/Products.contentmigration">Products.contentmigration</a>, un framework generico che aiuta a creare facilmente una migrazione tra tipi diversi.</div>
<p>Vediamo ora come creare una procedura di migrazione.</p>
<h2>Definizione della procedura</h2>
<pre>def makeTypeMigrator(context, src_type, dst_type):
    """ generate a migrator for the given at-based portal type """
    from Products.contentmigration.archetypes import InplaceATItemMigrator<br />
    class ATItemMigrator(InplaceATItemMigrator):
        src_portal_type = src_type
        src_meta_type = src_type
        dst_portal_type = dst_type
        dst_meta_type = dst_type<br />
    return ATFolderMigrator<br />
def migrateOldContentType(context):
    from Products.contentmigration.walker import CustomQueryWalker
    migrator = makeTypeMigrator(context, 'OldPortalType', 'NewPortalType')
    walker = CustomQueryWalker(context,
                               migrator,
                               src_portal_type="OldPortalType",
                               dst_portal_type="NewPortalType",
                               use_savepoint=True,
                               query={'path': '/siteid/folder-1'})
    walker.go()
    return walker.getOutput().splitlines()</pre>
<p>In questo esempio abbiamo un metodo "<strong>makeTypeMigrator</strong>" che, quando viene chiamato, crea una classe che eredita da <strong>InplaceATItemMigrator</strong> (classe che permette di rimpiazzare il vecchio content-type con il nuovo, sostituendolo in tutto e per tutto) alla quale serve sapere solamente qual'è il tipo di origine e quello di destinazione.</p>
<p>Il metodo "<strong>migrateOldContentType</strong>", invece, è quello che si occupa di preparare e svolgere a tutti gli effetti la migrazione.<br />Per effettuare la migrazione servono 2 cose: il <i>migrator</i> (oggetto che si occupa di tutte le operazioni di migrazione) e un <i>walker</i>. Il walker serve per navigare attraverso il catalogo ed effettuare la migrazione ad ogni elemento che trova.</p>
<p>Come si può vedere, gli vengono passati diversi parametri come il migrator, appunto (per la procedura di migrazione) e i vari content-type (quello di partenza viene utilizzato per fare una ricerca in catalogo, se non si passano ulteriori parametri).</p>
<p>Ci sono poi diversi altri parametri opzionali, per esempio la possibilità di usare dei savepoint e di definire una query per la ricerca in catalogo più sofisticata.<br />Si può anche impostare un metodo da richiamare prima di migrare ogni risultato trovato (attraverso l'attributo "<strong>callBefore</strong>") per fare alcune operazioni o controlli ulteriori (se questo metodo ritorna <strong>False</strong>, il contenuto non viene migrato).</p>
<p>A questo punto, basta solamente richiamare il metodo "<strong>migrateOldContentType</strong>" per lanciare la procedura. Questo ci ritorna le informazioni di tutte le operazioni effettuate, quindi è anche possibile mostrarle all'utente (stampandole nel log, per esempio).</p>
<pre>messages = migrateOldContentType(portal)<br /> for m in messages:<br />    logger.info(m)</pre>
<h2>Funzioni aggiuntive</h2>
<h3>Operazioni post-migrazione</h3>
<p>Come abbiamo visto, con l'attributo callBefore è possibile eseguire operazioni prima della migrazione. Ma è anche possibile eseguire operazioni appena dopo la migrazione.<br />Per fare questo, basta creare dei metodi dentro alla classe ATItemMigrator i cui nomi inizino con "<i>last_migrate_</i>".<br />Questi metodi verranno richiamati una volta completata la migrazione di un contenuto, e si avranno a disposizione sia il vecchio oggetto (self.old) che quello nuovo (self.new).</p>
<h3>Migrazione di oggetti folderish</h3>
<p>L'esempio di prima era riferito ad Archetypes semplici. Se si vogliono migrare degli oggetti folderish bisogna utilizzare la classe <strong>InplaceATFolderMigrator</strong>.</p>
<p>Nota Bene: nelle versioni compatibili per Plone 3.3.5 (fino alla 2.0.1), la migrazione delle cartelle ha dei problemi e non funziona correttamente.</p>
<h3>Migrazione di campi</h3>
<p>E' anche possibile effettuare una migrazione di campi all'interno di un Archetype. Ciò è utile, per esempio, quando si deve cambiare il nome, lo storage o effettuare delle modifiche ai valori memorizzati.<br />Per usare questa funzionalità basta utilizzare la classe <strong>InlineFieldActionMigrator</strong>.</p>
<h2>Conclusioni</h2>
<p>In conclusione, questo è un ottimo tool che permette di effettuare operazioni complesse scrivendo pochissime righe di codice.</p>
<p>Come abbiamo visto, ha anche parecchie funzioni che permettono di creare procedure altamente personalizzate in base ai propri bisogni.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Andrea Cecchi</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>tutorials</dc:subject>
    
    
      <dc:subject>Plone 4</dc:subject>
    
    
      <dc:subject>Plone</dc:subject>
    
    
      <dc:subject>Archetypes</dc:subject>
    
    <dc:date>2012-11-05T13:05:00Z</dc:date>
    <dc:type>Weblog Entry</dc:type>
  </item>





</rdf:RDF>
