<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>Guest</title>
    <link>http://blog.d-vel.com/web/blog/home/-/blogs/rss</link>
    <description>Guest</description>
    <item>
      <title>Estrarre asset utilizzando tags e categorie in Liferay 6</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/estrarre-asset-utilizzando-tags-e-categorie-in-liferay-6</link>
      <description>&lt;p&gt;L'asset publisher è lo strumento fornito out of the box da Liferay per estrarre contenuti dal portale in base a una query costruita settando diversi parametri, tra i quali categorie di appartenenza o tag assegnati all'asset.&lt;br /&gt;I casi d'uso coperti da questo strumento sono molteplici, ma per i requisiti che non dovessero essere soddisfatti dalle possibili parametrizzazioni, vediamo come interrogare l'asset framework per mezzo dell'oggetto &lt;em&gt;AssetEntryQuery&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;em&gt;AssetEntryQuery&lt;/em&gt; ci consente di costruire una query specificando quali regole in termini di associazione di tag e categorie devono rispettare i nostri asset: l'esempio di seguito vuole porre particolare attenzione su come utilizzare i metodi &lt;em&gt;setAllTagIds &lt;/em&gt;e &lt;em&gt;setAnyTagIds &lt;/em&gt;per ottenere diverse combinazioni di risultato.&lt;br /&gt;Lo scenario che vogliamo riprodurre è una classificazione di elementi eterogenei tra mezzi a motore di diversa natura; in particolare i nostri asset saranno:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Motore benzina - tags &amp;quot;benzina&amp;quot;&lt;/li&gt;&lt;li&gt;Motore diesel - tags &amp;quot;diesel&amp;quot;&lt;/li&gt;&lt;li&gt;Fiat Punto 1.2 - tags &amp;quot;benzina&amp;quot;, &amp;quot;fiat&amp;quot;&lt;/li&gt;&lt;li&gt;Fiat Punto MJet - tags &amp;quot;diesel&amp;quot;, &amp;quot;fiat&amp;quot;&lt;/li&gt;&lt;li&gt;Yamaha Tmax - tags &amp;quot;benzina&amp;quot;, &amp;quot;yamaha&amp;quot;&lt;/li&gt;&lt;li&gt;Yamaha 422 Sterndrive Diesel - tags &amp;quot;diesel&amp;quot;, &amp;quot;yamaha&amp;quot;, &amp;quot;marine&amp;quot;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;Entrambi i metodi sopra citati accettano come parametro un array di long, pertanto sarà necessario attenere l'id dei tag o delle categorie che vogliamo utilizzare nella query: le due classi contenenti i metodi per l'estrazione sono sostanzialmente &lt;em&gt;AssetTagLocalServiceUtil &lt;/em&gt;e &lt;em&gt;AssetCategoryLocalServiceUtil&lt;/em&gt;. Al fine di identificare gli id, può essere utile anche fare riferimento alle tabelle del portale assettag e assetcategory.&lt;br /&gt;&lt;br /&gt;Vediamo un primo caso nel quale vogliamo ottenere tutti i mezzi contenenti il tag &amp;quot;diesel&amp;quot;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;AssetEntryQuery assetEntryQuery = new AssetEntryQuery();&lt;br /&gt;assetEntryQuery.setAllTagIds(new long[]{17458});&lt;br /&gt;List&amp;lt;AssetEntry&amp;gt; results = AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Con questo codice otteniamo tutti gli asset ai quali è associato il tag &amp;quot;diesel&amp;quot;, che ha id = 17458.&lt;br /&gt;Il risultato sarà quindi:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Yamaha 422 Sterndrive Diesel&lt;/li&gt;&lt;li&gt;Fiat Punto MJet&lt;/li&gt;&lt;li&gt;Motore diesel&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;Allo stesso modo, otteniamo tutti i mezzi contenenti il tag &amp;quot;benzina&amp;quot; (id = 17462):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;AssetEntryQuery assetEntryQuery = new AssetEntryQuery();&lt;br /&gt;assetEntryQuery.setAllTagIds(new long[]{17462});&lt;br /&gt;List&amp;lt;AssetEntry&amp;gt; results = AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);&lt;/code&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Yamaha Tmax&lt;/li&gt;&lt;li&gt;Fiat Punto 1.2&lt;/li&gt;&lt;li&gt;Motore benzina&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;Vediamo ora un esempio un po' più complesso, che prevede un'intersezione di tag e che dimostra le differenze di funzionamento tra il metodo &lt;em&gt;setAnyTagIds &lt;/em&gt;e &lt;em&gt;setAllTagIds&lt;/em&gt;.&lt;br /&gt;Voglio estrarre tutti gli i prodotti (asset) del marchio yamaha (id = 17473), motorizzati diesel (id = 17458):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;AssetEntryQuery assetEntryQuery = new AssetEntryQuery();&lt;br /&gt;assetEntryQuery.setAllTagIds(new long[]{17473, 17458});&lt;br /&gt;List&amp;lt;AssetEntry&amp;gt; results = AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Il risultato sarà:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Yamaha 422 Sterndrive Diesel&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;in quanto è l'unico asset con l'associazione di entrambi i tag &amp;quot;diesel&amp;quot; e &amp;quot;yamaha&amp;quot;&lt;br /&gt;Se l'esigenza fosse stata invece quella di ottenere tutti gli asset contenenti i tag &amp;quot;diesel&amp;quot; o &amp;quot;yamaha&amp;quot;, avremmo dovuto utilizzare il metodo &lt;em&gt;setAnyTagIds&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;AssetEntryQuery assetEntryQuery = new AssetEntryQuery();&lt;br /&gt;assetEntryQuery.setAnyTagIds(new long[]{17473, 17458});&lt;br /&gt;List&amp;lt;AssetEntry&amp;gt; results = AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);&lt;/code&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Yamaha 422 Sterndrive Diesel&lt;/li&gt;&lt;li&gt;Fiat Punto MJet&lt;/li&gt;&lt;li&gt;Yamaha Tmax&lt;/li&gt;&lt;li&gt;Motore diesel&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;L'oggetto &lt;em&gt;AssetEntryQuery &lt;/em&gt;mette a disposizione metodi per l'utilizzo di altri parametri per restringere ulteriormente la selezione: è possibile settare categorie, className e className id e tutti i metodi fin qui elencati in logica negata.&lt;br /&gt;Infine, la presenza dei metodi setStart e setEnd invita lo sviluppatore a ingegnerizzare il proprio codice utiilzzando il classico searchContainer come elemento per la visualizzazione dei risultati.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Fri, 06 Apr 2012 10:56:15 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/estrarre-asset-utilizzando-tags-e-categorie-in-liferay-6</guid>
      <dc:creator>Andrea Previati</dc:creator>
      <dc:date>2012-04-06T10:56:15Z</dc:date>
    </item>
    <item>
      <title>Inidicizzazione e ricerca in Liferay</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/inidicizzazione-e-ricerca-in-liferay</link>
      <description>&lt;p&gt;Nei post precedenti abbiamo appreso come service builder e asset framework siano gli strumenti principali candidati alla gestione dello storage di dati e metadati all'interno di Liferay. Gli stessi strumenti possono essere opportunamente utilizzati anche per la ricerca, ma le possibilità offerte sono strettamente dipendenti dai metodi esposti dal service builder o dall'asset framework.&lt;br /&gt;Se volessimo effettuare una ricerca full text?&lt;br /&gt;Liferay utilizza nativamente Lucene (&lt;a href="http://lucene.apache.org"&gt;http://lucene.apache.org&lt;/a&gt;) per effettuare le ricerche all'interno del portale e lo scopo di questo articolo è illustrare come rendere ricercabili le nostre entità.&lt;br /&gt;Occorre innanzitutto normalizzare il nostro formato dati verso un modello basato su Document e Field e saremo noi stessi a definire quali dati appartenenti al nostro modello entreranno a far parte degli indici di Lucene per mezzo delle classi indexer. &lt;br /&gt;&lt;br /&gt;Importante: il codice di seguito fa riferimento alla versione 6.0.6 di Liferay. A partire dalla 6.1 alcuni metodi sono stati rifattorizzati, ma le linee guida di seguito descritte sono comunque applicabili.&lt;br /&gt;&lt;br /&gt;In liferay-portlet.xml:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;indexer-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; it.dvel.test.indexer.MyEntityIndexer&lt;br /&gt;&amp;lt;/indexer-class&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;La classe deve estendere com.liferay.portal.kernel.search.BaseIndexer e implementare i seguenti metodi:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- getClassNames&lt;/strong&gt;&lt;br /&gt;Restituisce un array di stringo contenenti i nomi delle classi relative ai model che vogliamo indicizzare.&lt;br /&gt;Esempio:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;public String[] getClassNames() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new String[]{MyEntity.class.getName()};&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- getPortletId&lt;/strong&gt;&lt;br /&gt;Restituisce il portletID.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="portlet-msg-alert"&gt;ATTENZIONE: per portletID si intende &amp;lt;portlet-name in  portlet.xml&amp;gt;_WAR_&amp;lt;webapp name&amp;gt;. Un errore nell'implementazione  di questo metodo causerà NullPointerException nascoste nelle JSP del  portale in quanto il valore restituito viene utilizzato per recuperare  la classe indexer: se compilato in maniera errata, nessun tipo di  validazione vi segnalerà l'errore e sarete costretti a una sessione  estenuante di debug.&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;- getSummary&lt;/strong&gt;&lt;br /&gt;Restituisce un oggetto di tipo Summary che deve contenere un titolo, una sintesi del contenuto e un url per la visualizzazione dell'elemento.&lt;br /&gt;Esempio:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;public Summary getSummary(Document document, String snippet,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; PortletURL portletURL) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String title = document.get(Field.TITLE);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String content = StringUtil.shorten(snippet, 200);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String resourcePrimKey = document.get(Field.ENTRY_CLASS_PK);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; portletURL.setParameter(&amp;quot;resourcePrimKey&amp;quot;, resourcePrimKey);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new Summary(title, content, portletURL);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- doGetDocument&lt;/strong&gt;&lt;br /&gt;Converte l'oggetto passato come parametro in un oggetto di tipo Document.&lt;br /&gt;Esempio:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;protected Document doGetDocument(Object obj) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MyEntity entry = (MyEntity) obj;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; long companyId = entry.getCompanyId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; long groupId = getParentGroupId(entry.getGroupId());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; long scopeGroupId = entry.getGroupId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; long userId = entry.getUserId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; long resourcePrimKey = entry.getPrimaryKey();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String title = entry.getTitle();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String content = entry.getText();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; String description = entry.getText();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Date modifiedDate = entry.getModifiedDate();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = new DocumentImpl();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addUID(PORTLET_ID, resourcePrimKey);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addModifiedDate(modifiedDate);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.COMPANY_ID, companyId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.PORTLET_ID, getPortletId());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.GROUP_ID, groupId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.USER_ID, userId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addText(Field.TITLE, title);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addText(Field.CONTENT, content);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addText(Field.DESCRIPTION, description);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.ENTRY_CLASS_NAME, MyEntity.class.getName());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addKeyword(Field.ENTRY_CLASS_PK, resourcePrimKey);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;&lt;br /&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="portlet-msg-alert"&gt;ATTENZIONE: fate molta attenzione alle seguenti righe di codice:&lt;br /&gt;&lt;br /&gt;document.addKeyword(Field.PORTLET_ID, getPortletId());&lt;br /&gt;&lt;br /&gt;e&lt;br /&gt;&lt;br /&gt;document.addModifiedDate(modifiedDate);&lt;/div&gt;&lt;p&gt;L'assenza della prima pregiudica il risultato della ricerca, escludendo le vostre entità dal risultato: non verrà lanciato nessun tipo di eccezione e nessun messaggio d'errore verrà loggato; l'assenza della seconda riga, invece, genererà un'inspiegabile &amp;quot;NumberFormatException for input string ''&amp;quot;: la string vuota che viene segnalata è esattamente la data di modifica.&lt;br /&gt;Altri due giorni di lavoro risparmiati. &lt;img alt="" src="http://blog.d-vel.com/html/js/editor/fckeditor/editor/images/smiley/msn/regular_smile.gif" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- doReindex&lt;/strong&gt;&lt;br /&gt;I 3 metodi doReindex vengono utilizzato dal portale in fase di richiesta di reindicizzazione del repository.&lt;br /&gt;Data l'istanza della nostra entità passata come parametro, è possibile effettuare l'aggiornamento utilizzando il metodo &lt;br /&gt;&lt;code&gt;&lt;br /&gt;SearchEngineUtil.updateDocument(entry.getCompanyId(), document);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;L'array di stringhe presente come parametro in uno dei tre metodi rappresenta le companyIds sulle quali iterare per effettuare in reindex completo. Sarà quindi necessario implementare un ciclo sulle company passate, per ognuna delle quali sarà necessario iterare sui groups associati, per ognuno dei quali sarà necessario iterare sulle entity appartenenti al gruppo in questione. L'operazione di update verrà effettuata esattamente come indicato sopra, per mezzo del metodo SearchEngineUtil.updateDocument.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- doDelete&lt;/strong&gt;&lt;br /&gt;Questo metodo serve a rimuovere l'indice dal repository e verrà invocato dal portale contestualmente alle operazioni di cancellazione delle nostre entity.&lt;br /&gt;L'implementazione è molto banale:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;protected void doDelete(Object obj) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MyEntity entry = (MyEntity)obj;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = new DocumentImpl();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.addUID(getPortletId(), entry.getGroupId(), Long.toString(entry.getPrimaryKey()));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SearchEngineUtil.deleteDocument(entry.getCompanyId(), document.get(Field.UID));&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Fine dell'implementazione della classe indexer.&lt;br /&gt;&lt;br /&gt;Vediamo ora di le funzionalità di ricerca per mezzo di &lt;strong&gt;OpenSearch&lt;/strong&gt;.&lt;br /&gt;Come per le classi indexer, andiamo a censire in liferay-portlet.xml la classe open search relativa al nostro portlet:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;open-search-class&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; it.dvel.test.search.MyEntityOpenSearch&lt;br /&gt;&amp;lt;/open-search-class&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;La classe deve estendere com.liferay.portal.kernel.search.HitsOpenSearchImpl e implementare i seguenti metodi:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- getPortletId&lt;/strong&gt;&lt;br /&gt;Restituisce il portletID. &lt;strong&gt;LO STESSO portletID RESTITUITO NELL'INDEXER&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- getTitle&lt;/strong&gt;&lt;br /&gt;Restituisce un titolo per la ricerca effettuata&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;- getSearchPath&lt;/strong&gt;&lt;br /&gt;Restituisce un path descrittivo del contesto di ricerca&lt;br /&gt;Esempio:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;public String getSearchPath() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return &amp;quot;/c/myentity/open_search&amp;quot;;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Fine dell'implementazione della classe open search.&lt;br /&gt;&lt;br /&gt;Deploy del vostro codice, aggiungete il portlet di ricerca in pagina e...fatto!&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Mon, 26 Mar 2012 13:11:46 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/inidicizzazione-e-ricerca-in-liferay</guid>
      <dc:creator>Andrea Previati</dc:creator>
      <dc:date>2012-03-26T13:11:46Z</dc:date>
    </item>
    <item>
      <title>Esporre le proprie entity custom come Asset di portale</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/esporre-le-proprie-entity-custom-come-asset-di-portale</link>
      <description>&lt;p&gt;Facendo tantissimi &lt;a target="_blank" href="http://www.d-vel.com/web/dvel/formazione-liferay" name="corsi-su-liferay" title="corsi-su-liferay"&gt;corsi su Liferay&lt;/a&gt; in giro per l'Italia ho l'opportunità di confrontarmi con differenti gruppi di sviluppo sui casi d'uso specifici dei loro progetti.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;La risposta che sempre più spesso do alle domande del tipo&amp;nbsp;&amp;quot;come facciamo a fare questa cosa con &lt;strong&gt;Liferay&lt;/strong&gt; è &amp;quot;fatelo con un asset&amp;quot;! ;)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Per chi conosce già la piattaforma, il concetto di &lt;strong&gt;Asset&lt;/strong&gt; non sarà di certo nuovo. Anzi:&amp;nbsp;è una delle funzionalità che rende il portale &amp;quot;potente e versatile&amp;quot; come noi lo vediamo da fuori!&lt;/p&gt; &lt;p&gt;Nel dettaglio, il portale gestisce due tipi di oggetti: &amp;quot;&lt;strong&gt;Resource&lt;/strong&gt;&amp;quot; e &amp;quot;&lt;strong&gt;Asset&lt;/strong&gt;&amp;quot;.&lt;/p&gt; &lt;p&gt;Una &amp;quot;&lt;em&gt;Resource&lt;/em&gt;&amp;quot; non è altro che un oggetto inserito all'interno del portale sul quale può insistere il permission system. Ogni entry che fate con i metodi del &lt;a target="_blank" href="http://www2.mokabyte.it/cms/article.run?articleId=CR2-58D-J7P-OHN_7f000001_11434483_1760ab6e"&gt;service builder&lt;/a&gt; dovrebbe essere inserita anche nel ResourceFramework, proprio per permettere al sistema di gestione dei permessi di gestirne l'accesso.&lt;/p&gt;&lt;p&gt;Un &amp;quot;&lt;em&gt;Asset&lt;/em&gt;&amp;quot;, invece, è l'elevamento a &amp;quot;oggetto gestibile dagli utenti&amp;quot;&amp;nbsp;di una risorsa. In poche parole, un Asset non è altro che uno speciale tipo di risorsa che però è compresa dal portale come &amp;quot;oggetto di dominio&amp;quot;&amp;nbsp;che può essere utilizzato, ad esempio, direttamente all'interno dell'AssetPublisher oppure sul quale si possono creare &lt;a target="_blank" title="expando-table-custom-fields-su-liferay" name="expando-table-custom-fields-su-liferay" href="http://blog.d-vel.com/web/blog/home/-/blogs/expandotable-sulle-vostre-entita-custom!"&gt;custom fields&lt;/a&gt; e collegare tag e categories.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In questo post realizzaremo una nostra entità custom che il portale utilizzerà come Asset.&lt;/p&gt;&lt;p&gt;Come al solito, poniamoci un obiettivo da raggiungere e vediamo come, attraverso il portale, riusciremo a realizzare la funzionalità.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Nel nostro esempio, poniamo di voler creare una entità &amp;quot;Progetto&amp;quot; che possa essere &amp;quot;estratta&amp;quot; dagli utenti solamente configurando l'AssetPublisher.&lt;/p&gt;&lt;p&gt;Per raggiungere questo obiettivo, dovremo agire su quattro fronti:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#creare-entita-custom-con-service-builder-di-liferay"&gt;creeremo la nostra entità con il service builder di Liferay&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#creare-permission-per-una-risorsa-custom-in-liferay"&gt;creeremo le permission per la nostra risorsa&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#inserire-entita-custom-asset-framework-resource-framework-liferay"&gt;inseriremo la nostra risorsa nel ResourceFramework e nell'AssetFramework&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#asset-renderer-liferay"&gt;forniremo al portale le informazioni relative a come visualizzare la risorsa&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a name="creare-entita-custom-con-service-builder-di-liferay"&gt;Creare entità custom con il service builder&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Per prima cosa dobbiamo creare la nostra entità. Lo scopo di questo post non è quello di spiegare il funzionamento del service builder; quindi andremo velocemente su questo punto.&lt;/p&gt;&lt;p&gt;Vi ricordo però l'&lt;a href="http://www2.mokabyte.it/cms/article.run?articleId=CR2-58D-J7P-OHN_7f000001_11434483_1760ab6e" target="_blank"&gt;articolo che abbiamo scritto su MokaByte&lt;/a&gt; relativo a questo tema, al quale potete fare riferimento se volete approfondire il dettaglio della tematica qui esposta.&lt;/p&gt;&lt;p&gt;Molto rapidamente:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;create attravero la Liferay IDE un nuovo &amp;quot;Liferay Service Builder&amp;quot;&lt;/li&gt;&lt;li&gt;mappate la vostra entità custom&lt;/li&gt;&lt;li&gt;lanciate il task ant &amp;quot;&lt;code&gt;build-service&lt;/code&gt;&amp;quot;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;fate refresh del progetto&lt;/strong&gt;! ;)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Se tutto va come deve, otterrete tutti gli oggetto di modello, persistenza e servizi per gestire l'entità appena mappata.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2&gt;&lt;a name="creare-permission-per-una-risorsa-custom-in-liferay"&gt;Creare le permission per una risorsa custom&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A questo punto dobbiamo &amp;quot;insegnare&amp;quot; a Liferay a gestire la nostra entità in relazione al permission system.&lt;/p&gt;&lt;p&gt;Questo passo è necessario perchè il portale richiede, per funzionare con il ResourceFramework e l'AssetFramework che siano presenti delle permission sull'entità che vogliamo gestire.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Per raggiungere questo obiettivo dobbiamo creare un file, all'interno della nostra src di progetto che chiameremo &lt;code&gt;portlet.properties&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Fate attenzione! È &lt;strong&gt;portlet&lt;/strong&gt; non &lt;strong&gt;portal&lt;/strong&gt; il nome del file! È molto comune fare questo errore; ve lo segnalo perchè anche io ogni tanto, facendo le cose &amp;quot;di corsa&amp;quot;, ci casco! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;All'interno di questo file inserite questa riga:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;resource.actions.configs=resource-actions/default.xml&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Questa istruzione, al momento del deploy, dirà a Liferay dove si trova il nostro file relativo alle action sulle nostre risorse. Gli dice, in sostanza, quali permessi deve permettere di applicare su una risorsa per consentire agli utenti, attraverso l'interfaccia di portale, di configurare i ruoli e le relative permission sia sulla nostra portlet che sulle risorse da essa gestite.&lt;/p&gt;&lt;p&gt;Approfondirò in un post successivo questa tematica, perchè il permission system di Liferay è davvero molto ben fatto e dominarlo correttamente ci può essere di grande aiuto per realizzare soluzioni integrate nel portale!&amp;nbsp;:)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Una volta creato il file ed inserita la riga, dobbiamo creare, sempre dentro alla nostra &lt;code&gt;src&lt;/code&gt; di progetto, il file &lt;code&gt;default.xml&lt;/code&gt; nel folder &lt;code&gt;resource-actions&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Qui un esempio di questo file:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;resource-action-mapping&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;portlet-resource&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &amp;lt;!-- Qui mappiamo il portlet-name del nostro portlet&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        letto dal file portlet.xml --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;portlet-name&amp;gt;gestioneprogetto&amp;lt;/portlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;permissions&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;supports&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                &amp;lt;!-- come vedete possiamo usare anche chiavi &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                custom, figata!&amp;nbsp;:) --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;ADD_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;UPDATE_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;DELETE_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/supports&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;community-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/community-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guest-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/guest-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guest-unsupported&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;ADD_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;UPDATE_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;DELETE_PROGETTO&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/guest-unsupported&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/permissions&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/portlet-resource&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;model-resource&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &amp;lt;!-- qui mappiamo il model che abbiamo generato e sul&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        quale potremo agire per dare le permission --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;model-name&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            it.dvel.test.service.builder.model.Progetto&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &amp;lt;/model-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;portlet-ref&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;portlet-name&amp;gt;gestioneprogetto&amp;lt;/portlet-name&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/portlet-ref&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;permissions&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;supports&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                &amp;lt;!-- anche qui, volendo, potremmo impostare &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                azioni custom per il nostro modello; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                altra figata!&amp;nbsp;:) --&amp;gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;DELETE&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;PERMISSIONS&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;UPDATE&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/supports&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;community-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/community-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guest-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;VIEW&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/guest-defaults&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guest-unsupported&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;UPDATE&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;action-key&amp;gt;DELETE&amp;lt;/action-key&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/guest-unsupported&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/permissions&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/model-resource&amp;gt;&lt;br /&gt;&amp;lt;/resource-action-mapping&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Credo che il file sopra riportato sia auto esplicativo; nel caso ci fossero domande potete farle commentando questo post! :)&lt;br /&gt;Sarò ben felice di rispondervi!! :D&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Una volta salvato questo file, al momento del deploy, Liferay caricherà questo file e mapperà nel permission system queste risorse e le relative azioni configurabili; così che dall'interfaccia del pannello di controllo l'utente possa profilarle secondo le proprie esigenze.&lt;/p&gt;&lt;p&gt;Andiamo avanti! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2&gt;&lt;a name="inserire-entita-custom-asset-framework-resource-framework-liferay"&gt;Inserire la risorsa nel ResourceFramework e nell'AssetFramewok&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;È qui che accade la magia: ogni volta che inseriamo una entity nel database attraverso gli oggetti del service builder, dobbiamo dire ai due framework sopra citati come gestirle e come mapparle.&lt;/p&gt;&lt;p&gt;La domanda che vi sorgerà spontanea sarà sicuramente &amp;quot;ma perchè non lo fa in automatico il service builder&amp;quot;?&amp;nbsp;:)&lt;/p&gt;&lt;p&gt;Bhe, perchè come sempre accade in Liferay, i progettisti della piattaforma vi lasciano la libertà di fare quello che volete. Ovviamente dandovi la possibilità di scegliere ma mettendovi a supporto tutto il necessario per fare le cose &amp;quot;in fretta e bene&amp;quot;! :)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Nel dettaglio, comunque, prima di procedere con il codice, dobbiamo iniettare nel nostro service gli oggetti che utilizzano l'AssetFramework perchè, al contrario di quelli che gestiscono il ResourceFramework, questi non sono presenti di default nei nostri servizi custom.&lt;/p&gt;&lt;p&gt;Per fare questa iniezione dobbiamo nuovamente agire sul file &lt;code&gt;service.xml&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Andate all'interno della nostra entity &amp;quot;Progetto&amp;quot;&amp;nbsp;e, in fondo a tutto, mettete questa riga:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;reference package-path=&amp;quot;com.liferay.portlet.asset&amp;quot; entity=&amp;quot;AssetEntry&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Una volta fatto questo:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;rigenerate con il task di ant '&lt;code&gt;build-service&lt;/code&gt;' i servizi&lt;/li&gt;&lt;li&gt;&lt;strong&gt;fate refresh del progetto&lt;/strong&gt;! ;)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Una volta compiuta questa operazione, troverete all'interno dell'oggetto &amp;quot;&lt;code&gt;ProgettoServiceBaseImpl&lt;/code&gt;&amp;quot; l'iniezione degli oggetti dell'AssetEntry:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @BeanReference(type = AssetEntryLocalService.class)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected AssetEntryLocalService assetEntryLocalService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @BeanReference(type = AssetEntryService.class)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected AssetEntryService assetEntryService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @BeanReference(type = AssetEntryPersistence.class)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected AssetEntryPersistence assetEntryPersistence;&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Ora possiamo inserire le nostre entity custom anche all'interno dell'AssetFramework! ;)&lt;/p&gt;&lt;p&gt;Vediamo come!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Per prima cosa dobbiamo far si che ogni volta che si inserisce / modifica / cancella una entity nel database i due framework sopra citati siano notificati di questa cosa.&lt;/p&gt;&lt;p&gt;Per farlo quindi occorrerà modificare i nostri metodi di persistenza perchè si prendano carico di fare queste operazioni.&lt;/p&gt;&lt;p&gt;Nel dettaglio, quindi, andremo ad agire sulla nostra classe di implementazione, in modo che sia &amp;quot;trasparente&amp;quot; per i nostri client che l'entity sia censita anche in questi framework.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Nel dettaglio, quindi, editate la classe &amp;quot;&lt;code&gt;ProgettoLocalServiceImpl&lt;/code&gt;&amp;quot; che trovate tra gli oggetti generati dal service builder.&lt;/p&gt;&lt;p&gt;Qui dovremo aggiungere qualche metodo che sarà poi utilizzato dai nostri client per gestire le entity custom verso il database.&lt;/p&gt;&lt;p&gt;Una nota:&amp;nbsp;noi modificheremo solo la parte &amp;quot;&lt;strong&gt;Local&lt;/strong&gt;&amp;quot;; la parte, cioè, che va verso il database.&lt;/p&gt;&lt;p&gt;Se volete portare questo comportamento anche alle entity gestite con i web service SOAP o REST, potete farlo incapsulando questa logica anche nell'oggetto &amp;quot;&lt;code&gt;ProgettoServiceImpl&lt;/code&gt;&amp;quot;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Nella nostra classe di implementazione, aggiungiamo questi medoti:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public Progetto addProgetto(Progetto progetto, &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      ServiceContext ctx) throws PortalException, SystemException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; super.addProgetto(progetto);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; resourceLocalService.addResources(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; progetto.getCompanyId(), progetto.getGroupId(), &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                progetto.getUserId(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Progetto.class.getName(), progetto.getPrimaryKey(), &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                false,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; true, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; assetEntryLocalService.updateEntry(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; progetto.getUserId(), progetto.getGroupId(), &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                    Progetto.class.getName(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; progetto.getPrimaryKey(), &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                    ctx.getAssetCategoryIds(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ctx.getAssetTagNames());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void deleteProgetto(long progettoId) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code&gt;throws PortalException,&lt;/code&gt;&lt;code&gt; SystemException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Progetto progetto = getProgetto(progettoId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; deleteProgetto(progetto);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void deleteProgetto(Progetto progetto) &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      throws SystemException, PortalException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; resourceLocalService.deleteResource(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; progetto.getCompanyId(), Progetto.class.getName(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceConstants.SCOPE_INDIVIDUAL, &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                progetto.getPrimaryKey());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; assetEntryLocalService.deleteEntry(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Progetto.class.getName(), progetto.getPrimaryKey());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; super.deleteProgetto(progetto);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Cosa facciano questi tre metodi è evidente:&amp;nbsp;ogni volta che una entity viene aggiunta o rimossa notificano questa operazione anche ai nostri due framework.&lt;/p&gt;&lt;p&gt;Bhe, che ci crediate o no, il più è fatto!&lt;/p&gt;&lt;p&gt;Ora dobbiamo dare una &amp;quot;visualizzazione&amp;quot; del nostro Asset che Liferay utilizzerà ogni volta che un estrattore dinamico o all'interno di una componente d'interfaccia standard -come, ad esempio, il motore di ricerca che analizzerò in un post successivo!&amp;nbsp;:)-&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2&gt;&lt;a name="asset-renderer-liferay"&gt;Creazione di un AssetRenderer per un'entità custom&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;E siamo così arrivati in fondo al nostro lavoro! Dobbiamo ora creare gli oggetti che istruiranno il portale su come visualizzare il nostro Asset!&lt;/p&gt; &lt;p&gt;&amp;nbsp;Le classi che ci interessa creare sono delle estensioni di queste due classi di Liferay:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;La factory che produce le view degli Asset: &lt;code&gt;BaseAssetRendererFactory&lt;/code&gt;&lt;/li&gt;&lt;li&gt;L'implementazione della nostra view:&amp;nbsp;&lt;code&gt;BaseAssetRenderer&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;La prima cosa da fare è però, come sempre, dire a Liferay come trattare le nostre classi al momento del deploy; per farlo, quindi, andiamo ad agire sul nostro &lt;code&gt;liferay-portlet.xml&lt;/code&gt; ed inseriamo questa riga (dopo il tag &amp;quot;&lt;code&gt;icon&lt;/code&gt;&amp;quot;, così fate prima che a guardare la DTD.. :)).&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;asset-renderer-factory&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; it.dvel.test.renderer.ProgettoRenderFactory&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/asset-renderer-factory&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Questa riga dirà infatti a Liferay qual'è la nostra implementazione da utilizzare quando deve gestire Asset sull'interfaccia.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Vediamo ora l'implementazione di questa classe; premetto che è una cosa molto semplice da fare:&amp;nbsp;la classe base, infatti, nasconde ed implementa già le funzionalità &amp;quot;core&amp;quot; del portale; a noi non resta che dare una nostra implementazione, in modo che sia &amp;quot;veloce&amp;quot; creare questo tipo di struttura.&lt;/p&gt;&lt;p&gt;Nel dettaglio, ecco la classe &amp;lt;omissis voluto degli import.. :)&amp;gt;:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;code&gt;public class ProgettoRenderFactory extends BaseAssetRendererFactory {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public AssetRenderer getAssetRenderer(long classPK, int type)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; throws PortalException, SystemException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Progetto progetto = &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;           ProgettoLocalServiceUtil.getProgetto(classPK);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return new ProgettoAssetRenderer(progetto);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getClassName() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return Progetto.class.getName();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getType() {&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // Questa, francamente, è una cosa non &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // corretta, però così funziona.. :)&lt;/code&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return &amp;quot;progetto&amp;quot;; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Fatto! :)&lt;/p&gt;&lt;p&gt;Questa factory è in grado di fornire al portale la nostra implementazione custom per la visualizzazione degli Asset!&lt;/p&gt;&lt;p&gt;Manca però ancora una classe; quella che fisicamente decide come visualizzare il nostro codice.&lt;/p&gt;&lt;p&gt;Vediamo quindi la seconda classe: &lt;code&gt;ProgettoAssetRenderer&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Anche qui, come prima, omissis voluto degli import..&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;public class ProgettoAssetRenderer extends BaseAssetRenderer {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Progetto progetto = null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // L'istanza di model da visualizzare ci viene passata dalla &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // factory; così noi ce la salviamo internamente&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // per riusarla &amp;quot;alla bisogna&amp;quot;!&amp;nbsp;:)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ProgettoAssetRenderer(Progetto progetto) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.progetto = progetto;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public long getClassPK() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto.getPrimaryKey();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public long getGroupId() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto.getGroupId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getSummary(Locale locale) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto.getDescrizione();&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // O&amp;nbsp;quello che volete che sia la descrizione..&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getTitle(Locale locale) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto.getNome(); &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &lt;/code&gt;&lt;code&gt;// O&amp;nbsp;quello che volete che sia il titolo..&lt;/code&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public long getUserId() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return progetto.getUserId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getUuid() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return null;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // Nel mio progetto non ho generato gli UUID per la&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // mia entity, così qui non ritorno nulla. Se lo avessi &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // fatto, però, qui avrei usato il metodo che mi avrebbe&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // generato il service builder sulla entity.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String render(RenderRequest renderRequest,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; RenderResponse renderResponse, String template) &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      throws Exception {&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // passo in pagina la entity&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; renderRequest.setAttribute(&amp;quot;progetto&amp;quot;, this.progetto);&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // decido, nel caso sia in visualizzazione &amp;quot;completa&amp;quot;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // dell'Asset, quale JSP usare.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (TEMPLATE_FULL_CONTENT.equalsIgnoreCase(template)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return &amp;quot;/html/gestioneprogetto/progetto_full_template.jsp&amp;quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // Se ritorno null per il TEMPLATE_ABSTRACT ci penserà&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        // il portale a generare la view usando title e description&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code&gt; // ovviamente potrei passare anche qui una JSP&amp;nbsp;e fornire &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;       // una mia view custom dell'Asset nel formato lista &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;       // dell'AssetPublisher; semplicemente ritornando la JSP opportuna..&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;That's it!&lt;/p&gt;&lt;p&gt;Compilate, fate deploy e &amp;quot;provare per credere&amp;quot;! ;)&lt;/p&gt;&lt;p&gt;Le nostre entity saranno presenti all'interno dell'AssetPublisher semplicemente selezionando il nostro model come &amp;quot;discriminante di visualizzazione&amp;quot;; nel sistema di gestione dei ruoli potremo impostare le nostre permission sui modelli e sul portlet e anche dalle preference di portlet vedremo esposte le nostre azioni!&lt;/p&gt;&lt;p&gt;Direi che abbiamo raggiunto l'obiettivo! :)&lt;/p&gt;&lt;p&gt;Ora siete pronti a giocare con il portale come meglio credete! ;D&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Fatemi sapere se funziona tutto a dovere; ho scritto questo post di domenica mattina prima di portare mia figlia al parco!&amp;nbsp;:) Tutto il codice che c'è qui riportato, però, è preso da un progetto che funziona e che fa quello che ci eravamo proposti di fare!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A presto!&amp;nbsp;:)&lt;/p&gt;</description>
      <pubDate>Sun, 18 Mar 2012 07:20:43 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/esporre-le-proprie-entity-custom-come-asset-di-portale</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2012-03-18T07:20:43Z</dc:date>
    </item>
    <item>
      <title>Convenzioni nello sviluppo all'interno di Liferay Portal</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/convenzioni-nello-sviluppo-all-interno-di-liferay-portal</link>
      <description>&lt;p&gt;Liferay è una piattaforma di sviluppo open source, con un buon grado di aderenza alle Java Specification Requests e con una molteplicità di tecnologie disponibili per lo sviluppo di risorse custom.&lt;/p&gt;&lt;p&gt;La flessibilità del portale, però, può portare a una situazione di caos nella quale il medesimo risultato atteso di un'implementazione può essere ottenuto perseguendo molte, a volte troppe,&amp;nbsp; strade: tutto bene nel migliore dei casi, ma un grosso problema a fronte dell'eventuale presentarsi di anomalie di difficile circoscrizione.&lt;/p&gt;&lt;p&gt;Qual è quindi la modalità corretta? Potenzialmente (quasi) tutte, ma può essere una buona idea attenersi a quella che d'ora in poi definiremo la &amp;quot;Liferay way&amp;quot;...ovvero...&amp;quot;come fa Liferay&amp;quot;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;Strumenti&lt;/h3&gt;&lt;p&gt;SDK e bundle della versione prescelta, meglio se una coppia per ogni progetto: eviterete problemi di identificazione dell'SDK in uso e ogni singola istanza di portale conterrà esclusivamente risorse realative al progetto in corso (portet, hook, temi, ecc...)&lt;/p&gt;&lt;p&gt;Ambiente di sviluppo a vostra scelta, personalmente mi oriento sempre su Liferay IDE, ottimo plugin di Eclipse.&lt;/p&gt;&lt;h4&gt;&amp;nbsp;&lt;/h4&gt;&lt;h3&gt;Struttura del progetto&lt;/h3&gt;&lt;p&gt;Per un progetto di tipo portlet basato su Liferay MVC Framework, la struttura è esattamente quella generata da Liferay IDE:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre style="text-align: center;"&gt;&lt;img align="middle" width="360" height="373" alt="Struttura di progetto Liferay Portlet all'interno di Liferay IDE" src="http://blog.d-vel.com/image/image_gallery?uuid=008fb4b7-572f-4abc-85e5-a3b04fdccfb3&amp;amp;groupId=12536&amp;amp;t=1331820639849" /&gt;
&lt;/pre&gt;&lt;p&gt;All'interno del progetto, utilizzaremo per le nsotre risorse le seguenti convenzioni:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;nome progetto:&lt;/strong&gt; minuscolo, separato da dash (il &amp;quot;trattino&amp;quot; -)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;nome portlet:&lt;/strong&gt; utilizziamo un valore numerico. Esempio:&lt;/p&gt;&lt;pre&gt;
&amp;lt;portlet&amp;gt;
    &amp;lt;portlet-name&amp;gt;1&amp;lt;/portlet-name&amp;gt;
    ...
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;docroot&lt;/strong&gt;: ogni portlet dovrebbe contenere le jsp in una struttura di directory a partire dalla root identificata dal percorso /html/portlet/&amp;lt;nome_portlet&amp;gt;. Le parole che compongono il nome del portlet dovrebbero essere separate da underscore (il &amp;quot;trattino basso&amp;quot; _). Esempio:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;
/html/portlet/my_custom_asset_publisher&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;All'interno dell'ultima directory posizioneremo le jsp preposte all'inizializzazione delle variabili (init.jsp), alla presentazione della modalità view (view.jsp) ed eventualmente edit.jsp e help.jsp.&lt;br /&gt;Solitamente view.jsp include init.jsp.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;configurazione:&lt;/strong&gt; esistono sostanzialmente due modi per rendere configurabile un portlet: edit mode e configuration. Liferay utilizza il secondo approccio, seguendo il quale sarà possibile implementare una pagina di configurazione accessibile per mezzo del menu contestuale del portlet:&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;img align="middle" width="324" height="136" src="http://blog.d-vel.com/image/image_gallery?uuid=0d23b9a4-f408-4f3c-8f8e-de30f7140c56&amp;amp;groupId=12536&amp;amp;t=1331822962242" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Nel file liferay-portlet.xml occorre definire la configuration action class:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;
&amp;lt;portlet&amp;gt;
	&amp;lt;portlet-name&amp;gt;1&amp;lt;/portlet-name&amp;gt;
	&amp;lt;icon&amp;gt;/icon.png&amp;lt;/icon&amp;gt;
	&amp;lt;configuration-action-class&amp;gt;it.dvel.test.portlet.ConfigurationActionImpl&amp;lt;/configuration-action-class&amp;gt;
&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;La classe appena censita nel file deve estendere BaseConfigurationAction e implementare due metodi:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;
public void processAction(
			PortletConfig portletConfig, ActionRequest actionRequest,
			ActionResponse actionResponse)
		throws Exception
		
public String render(
			PortletConfig portletConfig, RenderRequest renderRequest,
			RenderResponse renderResponse)
		throws Exception
&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;processAction salva nelle portlet preferences il contenuto del form presente in configuration.jsp, il cui percorso è restituito dal metodo render.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;Standard di codifica&lt;/h3&gt;&lt;p&gt;Se almeno una volta avete avuto occasione di analizzare i sorgenti del portale e avete un minimo di buon senso nelle best practices di programmazione e modellazione del software, vi sarete accorti che Liferay dal punto di vista della pulizia del codice fa rimpiangere la più becera delle pagine CGI della metà degli anni '90.&lt;/p&gt;&lt;p&gt;Nelle pagine jsp c'è di tutto: chiamate a servizi, definizione di metodi all'interno di scriptlet Java, inlcusioni di jsp con taglib che, a loro volta, includono jsp.&lt;/p&gt;&lt;p&gt;Personalmente non condivido questo approccio, in quanto crea confusione, rende il codice meno leggibile rispetto a un classico approccio &amp;quot;MVC&amp;quot; e, soprattutto, rende meno efficiente il processo di debug: debuggare una jsp in Eclipse, ad esempio, non è così comodo come debuggare una action e il debug è un'attività fondamentale per capire il funzionamento di Liferay.&lt;/p&gt;&lt;p&gt;Tuttavia a fronte di eventuali richieste di adeguamento agli standard, suggerisco ove possibile di utilzzare i custom tag della libreria aui, preferire implementazioni Javascript per mezzo del framework AlloyUI e modellare la struttra delle vostre jsp utilizzando i tag aui:layout e aui:column.&lt;/p&gt;&lt;p&gt;Fate attenzione anche all'elemento css-class-wrapper del file liferay-portlet.xml: questo valore è la classe wrapper relativa al vostro portlet, per mezzo della quale potrete agevolmente identificare univocamente il vostro portlet dalle classi definite nel tema:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;
	...	
	&amp;lt;css-class-wrapper&amp;gt;my-liferay-plugin-portlet&amp;lt;/css-class-wrapper&amp;gt;
&amp;lt;/portlet&amp;gt;

 
.my-liferay-plugin-portlet .content {
...


&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Thu, 15 Mar 2012 12:08:14 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/convenzioni-nello-sviluppo-all-interno-di-liferay-portal</guid>
      <dc:creator>Andrea Previati</dc:creator>
      <dc:date>2012-03-15T12:08:14Z</dc:date>
    </item>
    <item>
      <title>Liferay 6.1.0 GA1 e StackOverflowError</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/liferay-6-1-0-ga1-e-stackoverflowerror</link>
      <description>&lt;p&gt;Liferay mette da tempo a disposizione degli sviluppatori un comodo plugin di Eclipse (&lt;a href="http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE" target="_blank"&gt;www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE&lt;/a&gt;) per gestire tutte le configurazioni ed i deploy; il suo utilizzo consente infatti di creare nuovi progetti (portlet, temi, ...) all'interno del proprio workspace e di deployarli direttamente nel server interno censito in Eclipse, dando la possibilità di gestire proficuamente hot code deploy, debug, ..., senza l'ausilio dello script Ant.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Recentemente ho lavorato per un progetto sulla versione di Liferay 6.1.0 GA1 (Community Edition), secondo le modalità sopra, senza avere troppi problemi imprevisti.&lt;/p&gt;&lt;p&gt;Arrivati ad un certo punto dello sviluppo il server ha iniziato a sollevare inspiegabili eccezioni di tipo StackOverflowError in seguito a qualsiasi tipo di interazione con le portlet custom che erano state sviluppate; di fatto era diventato inutilizzabile.&lt;/p&gt;&lt;p&gt;Dopo alcune prove e ricerche ho scoperto che il problema erano gli hook, nello specifico la presenza del file &lt;em&gt;liferay-hook.xml&lt;/em&gt; all'interno del mio progetto custom; non potendo rimuovere il file (necessario all'applicazione), l'unica soluzione è stata quella di rimuovere il mio progetto dal server interno di Eclipse e di ripristinare l'utilizzo dello script Ant. Avvio ed arresto del server rimangono comunque all'interno di Eclipse.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In conclusione esiste un bug all'interno di una classe di Liferay adibita al deploy che genera questo malfunzionamento; il bug è documentato qui, &lt;a href="http://issues.liferay.com/browse/LPS-24723" target="_blank"&gt;http://issues.liferay.com/browse/LPS-24723&lt;/a&gt;, ed è stato risolto già a partire con il rilascio della versione 6.1.1 GA2.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Wed, 14 Mar 2012 09:14:16 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/liferay-6-1-0-ga1-e-stackoverflowerror</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2012-03-14T09:14:16Z</dc:date>
    </item>
    <item>
      <title>Modificare il timezone del bundle di Tomcat su Liferay 6.1</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/modificare-il-timezone-del-bundle-di-tomcat-su-liferay-6-1</link>
      <description>&lt;p&gt;State sviluppando su Liferay 6.1 e vi siete accorti che i messaggi di Log4J sono indietro di un'ora e non sempre i test sulle date funzionano?&lt;/p&gt;&lt;p&gt;E' un semplice problema nella configurazione di Tomcat7 fornito in bundle con Liferay, che è impostato su GMT, mentre in Italia siamo su GMT+1.&lt;/p&gt;&lt;p&gt;Per risolvere il problema è sufficiente&amp;nbsp;accedere alla cartella di Tomcat7 e modificare il file bin/setenv.bat, sostituendo la dicitura GMT con GMT+1.&lt;/p&gt;&lt;p&gt;Nel caso in cui stiate utilizzando Tomcat direttamente da Eclipse, dovete modificare le impostazioni del server anche lì: facendo doppio click sul server nella vista Server accederete alle sue impostazioni, tra cui il timezone.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Thu, 16 Feb 2012 23:22:53 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/modificare-il-timezone-del-bundle-di-tomcat-su-liferay-6-1</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2012-02-16T23:22:53Z</dc:date>
    </item>
    <item>
      <title>Thunderbird - Allegati corrotti</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/thunderbird-allegati-corrotti</link>
      <description>&lt;p&gt;&lt;img width="95" height="94" align="left" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=f761b09d-5664-46ad-a0aa-396249bafd72&amp;amp;groupId=12536&amp;amp;t=1324025797112" /&gt;&lt;/p&gt; &lt;p style="text-align: left;"&gt;Puo' capitare che thunderbird possa avere problemi nel download degli allegati delle mail.&lt;/p&gt; &lt;p style="text-align: left;"&gt;Purtroppo questo problema non e' risolvibile riscaricando la mail ma bisogna modificare dei parametri di configurazione di thunderbird.&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: left;"&gt;Come prima cosa dobbiamo aprire le &lt;strong&gt;preferenze&lt;/strong&gt; di Thunderbird e cliccare sul tab &lt;strong&gt;avanzate&lt;/strong&gt;.&lt;/p&gt; &lt;p style="text-align: center;"&gt;&lt;img width="475" height="344" alt="" style="" src="http://blog.d-vel.com/image/image_gallery?uuid=e96476cc-54dd-47ba-b236-eb211a2ce13d&amp;amp;groupId=12536&amp;amp;t=1324026184799" /&gt;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: left;"&gt;Arrivati a questo punto per editare il file di configurazione dobbiamo cliccare sul pulsante &lt;strong&gt;Editor di configurazione...&lt;/strong&gt; , questo editor visualizza tutte le opzioni di thunderbird in maniera testuale.&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&lt;img alt="" style="width: 388px; height: 273px;" src="http://blog.d-vel.com/image/image_gallery?uuid=3791453d-8122-4812-a783-b29b034550d9&amp;amp;groupId=12536&amp;amp;t=1324026336339" /&gt;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: left;"&gt;Prima di poter visualizzare i parametri il programma avvisa sul pericolo dell'utilizzo di questo editor ma possiamo procedere senza problemi.&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&lt;img width="475" height="335" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=ad71bb55-84dd-43ab-9505-9eb2bf86c30f&amp;amp;groupId=12536&amp;amp;t=1324026628404" /&gt;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: justify;"&gt;Arrivati a questo punto dovremo cercare attraverso l'uso del filto i seguenti parametri:&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;mail.server.default.fetch_by_chunks&lt;/li&gt;     &lt;li&gt;mail.imap.mime_parts_on_demand&lt;/li&gt;     &lt;li&gt;mail.server.default.parts_on_demand&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Facendo doppio click su ogni opzione il parametro passera' da &lt;strong&gt;true&lt;/strong&gt; a &lt;strong&gt;false&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Ora non ci rimane che chiudere tutte le schermate e far partire la riparazione della casella di posta, cliccando con il tasto destro sulla nostra casella e proprieta'&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: center;"&gt;&lt;img alt="" style="width: 443px; height: 282px;" src="http://blog.d-vel.com/image/image_gallery?uuid=23952434-27cb-4f33-afac-e7c010d1436e&amp;amp;groupId=12536&amp;amp;t=1324026964774" /&gt;&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p style="text-align: left;"&gt;Cliccando su &lt;strong&gt;Ripara la cartella&lt;/strong&gt;, riscarichera' tutti i messaggi scaricando correttamente anche tutti gli allegati.&lt;/p&gt; &lt;p style="text-align: left;"&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Fri, 16 Dec 2011 08:52:14 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/thunderbird-allegati-corrotti</guid>
      <dc:creator>Luca Leone</dc:creator>
      <dc:date>2011-12-16T08:52:14Z</dc:date>
    </item>
    <item>
      <title>Collegare database esistenti a portlet sviluppate in Liferay</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/collegare-database-esistenti-a-portlet-sviluppate-in-liferay</link>
      <description>&lt;p&gt;&lt;strong&gt;Problema&lt;/strong&gt;: vi chiedono di scrivere dei portlet per Liferay che leggono dei dati da un database esistente.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Soluzione &amp;quot;sporca&amp;quot;&lt;/strong&gt;: vi fate il vostro accesso ai dati e integrate il database esterno alla vostra portlet.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Soluzione&amp;nbsp;&amp;quot;pulita&amp;quot;&lt;/strong&gt;: istruite il service builder adeguatamente e gli fate wrappare le tabelle dell'altro database, così avrete la stessa modalità d'accesso ai dati che avete quando andate sul database di Liferay e chi usa la vostra API non dovrà impazzire per comprenderla!&amp;nbsp;:)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Vediamo un po' nel dettaglio questa soluzione!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Per prima cosa, istruiamo il Service Builder ad utilizzare il database esterno.&lt;/p&gt;&lt;p&gt;Prima un concetto chiave: su Liferay è possibile specificare &lt;strong&gt;per singola entity&lt;/strong&gt; il datasource da utilizzare. Quindi il grosso del lavoro viene fatto dal framework del Service Builder -e dalle classi che saranno generate-; ora vediamo nel dettaglio un po' di codice.&lt;/p&gt;&lt;p&gt;Creiamo una entity che mappi una tabella esistente nel database target -esterno al database di Liferay-. Di seguito evidenzio i pezzi &amp;quot;importanti&amp;quot;, quelli che permettono la &lt;em&gt;magia&lt;/em&gt;.. :)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;entity name=&amp;quot;TimeSheet&amp;quot; table=&amp;quot;public.time_sheet&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   local-service=&amp;quot;true&amp;quot; remote-service=&amp;quot;false&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   &lt;strong&gt;data-source&lt;/strong&gt;=&amp;quot;intranetDataSource&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   &lt;strong&gt;session-factory&lt;/strong&gt;=&amp;quot;intranetSessionFactory&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   &lt;strong&gt;tx-manager&lt;/strong&gt;=&amp;quot;intranetTxManager&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;...&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;/entity&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Come vedete, nel nostro file &lt;code&gt;service.xml&lt;/code&gt;, specifichiamo quale dev'essere il &lt;code&gt;data-source&lt;/code&gt; da utilizzare, quale il &lt;code&gt;session-factory&lt;/code&gt; e quale &lt;code&gt;transaction manager&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;È ovvio che chi conosce bene Spring ha già chiaro di che cosa stiamo parlando.&lt;/p&gt;&lt;p&gt;Per gli altri, invece, il concetto è molto semplice: Liferay farà in modo che, per l'entità in questione, siano utilizzate connessioni, persistenza e transazioni su un db diverso e non quelle &amp;quot;dirette&amp;quot; che creerebbe verso il suo database.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Una volta creato il nostro file &lt;code&gt;service.xml&lt;/code&gt; dobbiamo buildarlo, in modo da farci generare gli oggetti che ci serviranno dopo.&lt;/p&gt;&lt;p&gt;Per farlo, dal build file del vostro portlet, eseguite il task &lt;code&gt;build-service&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Dopo che il Service Builder avrà generato le classi e gli oggetti a partire dal nostro file, dovremo istruire il framework Spring a creare ed utilizzare gli oggetti che, per ora, abbiamo solo referenziato nel &lt;code&gt;service.xml&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Per farlo, nella META-INF del nostro source folder (quindi &lt;em&gt;docroot/WEB-INF/src/META-INF/&lt;/em&gt;), creiamo un file chiamato &lt;code&gt;ext-spring.xml&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Dentro a questo file, mettiamoci questo codice:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE beans PUBLIC &amp;quot;-//SPRING//DTD BEAN//EN&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    &amp;quot;http://www.springframework.org/dtd/spring-beans.dtd&amp;quot;&amp;gt;&lt;br /&gt;&amp;lt;beans&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   &amp;lt;!-- Qui specifichiamo il JNDI name da utilizzare &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   per la nostra connessione. La risorsa JNDI va messa all'interno&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   del file &amp;lt;liferay_dir&amp;gt;/&amp;lt;tomcat_dir&amp;gt;/conf/context.xml --&amp;gt;&lt;br /&gt;   &amp;lt;bean id=&amp;quot;intranetDataSourceTarget&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      class=&amp;quot;com.liferay.portal.spring.jndi.JndiObjectFactoryBean&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      lazy-init=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;property name=&amp;quot;jndiName&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;          &amp;lt;value&amp;gt;jdbc/intranetDB&amp;lt;/value&amp;gt;&lt;br /&gt;      &amp;lt;/property&amp;gt;&lt;br /&gt;   &amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   &amp;lt;!-- Qui creiamo il nostro data source collegato alla connessione&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   JNDI che abbiamo referenziato poco sopra. --&amp;gt;&lt;br /&gt;   &amp;lt;bean id=&amp;quot;intranetDataSource&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;      class=&amp;quot;org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy&amp;quot;&amp;gt;&lt;br /&gt;       &amp;lt;property name=&amp;quot;targetDataSource&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;           &amp;lt;ref bean=&amp;quot;intranetDataSourceTarget&amp;quot; /&amp;gt;&lt;br /&gt;       &amp;lt;/property&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;  	&lt;br /&gt;    &amp;lt;bean id=&amp;quot;intranetHibernateSessionFactory&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        class=&amp;quot;com.liferay.portal.spring.hibernate.PortletHibernateConfiguration&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;dataSource&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            &amp;lt;ref bean=&amp;quot;intranetDataSource&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;     &amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;     &amp;lt;!-- Qui specifichiamo la nostra SessionFactory --&amp;gt;	&lt;br /&gt;     &amp;lt;bean id=&amp;quot;intranetSessionFactory&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;         class=&amp;quot;com.liferay.portal.dao.orm.hibernate.SessionFactoryImpl&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;         &amp;lt;property name=&amp;quot;sessionFactoryImplementor&amp;quot;&amp;gt;&lt;br /&gt;	      &amp;lt;ref bean=&amp;quot;intranetHibernateSessionFactory&amp;quot; /&amp;gt;&lt;br /&gt;	    &amp;lt;/property&amp;gt;&lt;br /&gt;     &amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;     &amp;lt;!-- E qui creiamo il Transaction&amp;nbsp;Manager --&amp;gt;&lt;br /&gt;     &amp;lt;bean id=&amp;quot;intranetTxManager&amp;quot; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        class=&amp;quot;org.springframework.orm.hibernate3.HibernateTransactionManager&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;dataSource&amp;quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            &amp;lt;ref bean=&amp;quot;intranetDataSource&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;sessionFactory&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;ref bean=&amp;quot;intranetHibernateSessionFactory&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;     &amp;lt;/bean&amp;gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Una volta che abbiamo creato questo file il gioco è fatto!&lt;/p&gt;&lt;p&gt;Ci basterà deployare il nostro portlet e usare la nostra API&amp;nbsp;Liferay per accedere &lt;strong&gt;anche&lt;/strong&gt; alle tabelle del secondo database!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Spero d'esservi stato d'aiuto!&lt;/p&gt;&lt;p&gt;Alla prossima!&amp;nbsp;:)&lt;/p&gt;</description>
      <pubDate>Sun, 11 Dec 2011 15:15:00 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/collegare-database-esistenti-a-portlet-sviluppate-in-liferay</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-12-11T15:15:00Z</dc:date>
    </item>
    <item>
      <title>Aggiornato il sistema a Java 1.6.0_29? Usate MS SQL? Attenzione al bug! ;)</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/aggiornato-il-sistema-a-java-1-6-0_29-usate-ms-sql-attenzione-al-bug!</link>
      <description>&lt;p&gt;Ieri ho installato l'aggiornamento per OSX a java 1.6.0_29 e, dopo qualche minuto, ho lanciato una applicazione che ho realizzato per fare del debug da un cliente.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Problema&lt;/strong&gt;:&amp;nbsp;l'applicazione non mi loggava!&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Memore di una mail di qualche giorno prima dei miei colleghi che mi segnalavano questo problema con la versione del JDK&amp;nbsp;incriminata, ho fatto un po' di debug e ho scoperto che il problema risiede in una classe del JDK incriminato.&lt;/p&gt; &lt;p&gt;Nella versione 1.6.0_29, al momento di creare una connessione JDBC verso SQL&amp;nbsp;Server,&amp;nbsp; rimane appesa la chiamata all'infinito e la VM non riesce ad istanziare la connessione.&lt;/p&gt; &lt;p&gt;Facendo un po' di scouting su internet, ho scoperto che &lt;strong&gt;c'è un bug&lt;/strong&gt; nella versione che ho appena installato! (come dice sempre il mio socio si, sono un uomo fortunato.. ;))&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Nel dettaglio, questo è il problema (preso dal bug sul sito di Sun):&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;pre&gt;
Creating a JDBC connection through customer JDBC Driver 2.0, 3.0, 4.0 CTP 3,
or jTDS 1.2.5 driver to a  customer  SQL server 2008R2 database using 
JDK 1.6.0_29 results in an indefinite wait for a connection.
This worked fine on all previous versions from 1.6.0_27.

&lt;/pre&gt;&lt;p&gt;A questo punto, la soluzione è stata semplice: ho sovrascritto il jar jsse.jar che si trova tra le lib del JDK (è qui dentro che sono specificate le classi incriminate)&amp;nbsp;con lo stesso preso da una versione precedente del JDK e tutto è tornato ad andare senza problemi.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Oppure potete optare anche per le soluzioni proposte da Oracle / Sun:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;
Revert to Java 1.6.0_27 or earlier version, or upgrade to 1.7.0_01.
Or switch customer database server.
SQL Server 2008, 2005, 2000 all work.&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Anche se mi sembravano un pochino forti come soluzioni; per questo ho optato per la sovrascrittura del jar! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Infine, ecco un po' di riferimenti al bug:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7105007" target="_blank"&gt;Il bug segnalato su&amp;nbsp;SDN&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/97dce8fd-6487-4bca-80b0-492167db3e0d" target="_blank"&gt;La segnalazione su&amp;nbsp;MSDN di Microsoft&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://forums.oracle.com/forums/thread.jspa?threadID=2301826&amp;amp;start=0&amp;amp;tstart=0" target="_blank"&gt;Un interessante thread sui forum di Oracle che riguardano il problema&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Spero d'esservi stato d'aiuto! ;)&lt;/p&gt;&lt;p&gt;Alla prossima!&amp;nbsp;;)&lt;/p&gt;</description>
      <pubDate>Tue, 15 Nov 2011 06:45:53 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/aggiornato-il-sistema-a-java-1-6-0_29-usate-ms-sql-attenzione-al-bug!</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-11-15T06:45:53Z</dc:date>
    </item>
    <item>
      <title>Pubblicare un contenuto con dei documenti la stage a live: problema di permessi sui file</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/pubblicare-un-contenuto-con-dei-documenti-la-stage-a-live:-problema-di-permessi-sui-file</link>
      <description>&lt;p&gt;&lt;strong&gt;Problema&lt;/strong&gt;:&amp;nbsp;ho un contenuto sull'ambiente di stage che ha, nella sua structure, il collegamento a più documenti della mia document library. Quando pubblico su live questo contenuto, i file che sono in esso referenziati, nonostante io sia __CERTO__&amp;nbsp;che sull'ambiente di stage hanno i permessi di view per guest, non sono accessibili.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Causa&lt;/strong&gt;: dalle mie indagini, Liferay 5.2.3, quando pubblica da stage a live un web content, non si porta dietro correttamente le permission dei documenti ad esso collegati, per cui mi ritrovo ad avere stage &lt;strong&gt;disallineato&lt;/strong&gt; con live. Parlo ovviamente a livello di permission del document library.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; non ho verificato se questa cosa accade sistematicamente oppure se è una cosa che accade solo quando faccio determinate operazioni (ad esempio pubblico un documento con le permission sbagliate, pubblico il web content che lo referenzia e quindi, quando in stage vado a modificare le permission, non riesco a sincronizzarle su live). Mi riservo di fare una update a questo post quando riuscirò a riprodurre il problema su un ambiente non di produzione! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Soluzione&lt;/strong&gt;: la soluzione, che poi è un workaround e non una soluzione.. :|, è quella di pubblicare da qualche parte su live il portlet della document library. Da qui, quindi, andare a sistemare i permessi sui documenti interessati, andando di fatto a sistemare il problema sopra descritto.&lt;/p&gt;&lt;p&gt;È tutto! :)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Come ultima nota vi lascio il mio parere sull'ambiente stage di Liferay 5.2.3.&amp;nbsp;Nel corso di questi anni parecchi capelli bianchi sulla mia testa sono stati dati da questo environment; di fatto l'ambiente di stage è un potentissimo strumento a supporto di chi ha necessità redazionali complesse e un processo di composizione delle pagine e dei contenuti articolato.&lt;/p&gt;&lt;p&gt;La funzionalità presente quindi OOTB del prodotto è indubbiamente importantissima per questo utilizzo.&lt;/p&gt;&lt;p&gt;Mi auguro che sulla 6.0 queste problematiche siano state risolte, perchè altrimenti i miei capelli diventeranno ancora più bianchi e i post come questo sui workaround cresceranno esponenzialmente! ;)&lt;/p&gt;&lt;p&gt;Ma anche di questo vi aggiornerò non appena manderemo live il primo progetto con Liferay 6.0 e l'ambiente di stage abilitato! ;D&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A presto!&lt;/p&gt;&lt;br /&gt;</description>
      <pubDate>Fri, 07 Oct 2011 04:57:53 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/pubblicare-un-contenuto-con-dei-documenti-la-stage-a-live:-problema-di-permessi-sui-file</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-10-07T04:57:53Z</dc:date>
    </item>
    <item>
      <title>Duplicare un database in SQL Server 2005</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/duplicare-un-database-in-sql-server-2005</link>
      <description>&lt;p&gt;Se il vostro scopo è creare un nuovo database con identici contenuti di uno già presente, quindi stessa struttura e stessi record, una procedura veloce è la seguente:&amp;nbsp;innanzitutto aprire Microsoft SQL Server Management Studio, selezionare dall'elenco dei database quello che farà da &amp;quot;sorgente&amp;quot; e cliccare col destro. Dal menu a tendina selezionare Task -&amp;gt; Export Data.&lt;/p&gt;&lt;p&gt;&lt;img width="300" height="271" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=29e59f81-b3e2-4e12-9b1a-c9eaaf777864&amp;amp;groupId=12536&amp;amp;t=1319446206337" /&gt;&lt;img width="300" height="284" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=7927fd8b-e193-4f6d-a6bc-7b28bfeb17f2&amp;amp;groupId=12536&amp;amp;t=1319446211007" /&gt;&lt;/p&gt;&lt;p&gt;Si aprirà un wizard che vi permetterà di selezionare il database sorgente (di default sarà selezionato quello da cui siete &amp;quot;partiti&amp;quot;),&lt;/p&gt;&lt;p style="text-align: center; "&gt;&lt;img width="400" height="378" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=5ff9c22f-275e-4690-9970-b055f4d6ccdf&amp;amp;groupId=12536&amp;amp;t=1319446215174" /&gt;&lt;/p&gt;&lt;p&gt;vi chiederà analogamente un database di destinazione, con la possibilità di crearne uno nuovo sul momento.&lt;/p&gt;&lt;p style="text-align: center; "&gt;&lt;img width="400" height="378" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=fbaffd93-eab6-4277-a10e-c4e59733a761&amp;amp;groupId=12536&amp;amp;t=1319446219202" /&gt;&lt;/p&gt;&lt;p&gt;Si passerà poi alla scelta della copia effettiva della struttura e dei dati o l'inserimento di query per restringere la quantità di dati da migrare o modificarli.&lt;/p&gt;&lt;p style="text-align: center; "&gt;&lt;img width="400" height="377" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=cc6bd706-4517-40f0-ab24-4e626d41c719&amp;amp;groupId=12536&amp;amp;t=1319446223437" /&gt;&lt;/p&gt;&lt;p&gt;Nella schermata successiva, l'ultima, si potranno scegliere le singole tabelle da migrare ed eventualmente chiedere a SQL Server di farlo all'interno di una transazione.&lt;/p&gt;&lt;p style="text-align: center; "&gt;&lt;img width="450" height="396" alt="" src="http://blog.d-vel.com/image/image_gallery?uuid=d6809675-af93-43e3-9ad9-d91a6573cd3e&amp;amp;groupId=12536&amp;amp;t=1319446227409" /&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 30 Sep 2011 14:08:08 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/duplicare-un-database-in-sql-server-2005</guid>
      <dc:creator>Andrea Tomasello</dc:creator>
      <dc:date>2011-09-30T14:08:08Z</dc:date>
    </item>
    <item>
      <title>Gestione dei file XML su Bonita Open Solution</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/gestione-dei-file-xml-su-bonita-open-solution</link>
      <description>&lt;p&gt;Il formato XML è ormai profondamente diffuso in quasi ogni applicativo, e la suite Bonita Open Solution non è da meno fornendo proprio un tipo di dato specifico per manipolare i dati in formato XML.&lt;/p&gt; &lt;p&gt;Peccato che sembra non esistere una guida che spieghi come utilizzare tale tipo di dato; vediamo quindi come fare.&lt;/p&gt; &lt;p&gt;L'obiettivo è quello di creare un processo attraverso il quale fare l'upload di un file XML e visualizzarne il contenuto. A tale scopo utilizzeremo Bonita 5.5.1 e creeremo il seguente processo (do per scontato che Bonita lo sappiate usare):&lt;/p&gt; &lt;p&gt;&lt;img width="502" height="171" src="http://blog.d-vel.com/image/image_gallery?uuid=10599beb-c826-440d-ac32-8df5495d6efd&amp;amp;groupId=12536&amp;amp;t=1314783762383" alt="" /&gt;&lt;/p&gt; &lt;p&gt;Come prima cosa definiamo 2 variabili globali di processo:&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;&lt;em&gt;xmlData&lt;/em&gt;, di tipo XML a cui associare a priori il file XSD che descrive il formato del file ed in cui selezionare a priori l'elemento radice;&lt;/li&gt;     &lt;li&gt;&lt;em&gt;xmlFile&lt;/em&gt;, di tipo Attachment.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Nel primo task &lt;em&gt;Scegli file&lt;/em&gt; creare semplicemente una nuova &lt;em&gt;Entry Pageflow&lt;/em&gt; in autocomposizione contenente il widget &lt;em&gt;File&lt;/em&gt; per caricare il nostro file XML, da associare alla variabile &lt;em&gt;xmlFile&lt;/em&gt;. Tutto molto semplice per ora.&lt;/p&gt; &lt;p&gt;Passiamo al secondo task &lt;em&gt;Visualizza&lt;/em&gt; in cui aggiungeremo un connettore di tipo Groovy sull'evento &lt;em&gt;Enter&lt;/em&gt; del task. Il codice da inserire nel connettore è il seguente e non fa altro che leggere il contenuto del file &lt;em&gt;xmlFile&lt;/em&gt; e memorizzarlo in una stringa:&lt;/p&gt; &lt;p&gt;&lt;code&gt;import org.ow2.bonita.util.AccessorUtil&lt;br /&gt;&lt;br /&gt;byte[] content = AccessorUtil.getQueryRuntimeAPI().getDocumentContent(xmlFile.UUID)&lt;br /&gt;new String(content)&lt;/code&gt;&lt;/p&gt;&lt;p&gt;L'output del connettore associarlo alla variabile &lt;em&gt;xmlData&lt;/em&gt;, su tutta la variabile (&lt;em&gt;Whole variable&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A questo punto il grosso del lavoro è stato fatto, la variabile &lt;em&gt;xmlData&lt;/em&gt; contiene i dati da elaborare; cosa che andremo a fare mediante XPath.&lt;/p&gt;&lt;p&gt;Pertanto aggiungiamo una nuova &lt;em&gt;Entry Pageflow&lt;/em&gt; al task &lt;em&gt;Visualizza&lt;/em&gt; in cui andremo a visualizzare qualche dato.&lt;/p&gt;&lt;p&gt;Inseriamo un widget di tipo &lt;em&gt;Text&lt;/em&gt; e selezioniamo la variabile &lt;em&gt;xmlData&lt;/em&gt; dalla combo; comparirà un popup con la struttura dei dati in cui selezionare cosa visualizzare. Verrà generata un'espressione Groovy del tipo (che contiene il percorso XPath):&lt;/p&gt;&lt;p&gt;&lt;code&gt;${providedscripts.BonitaXML.evaluateXPathOnVariable(xmlData, &amp;quot;/NOMETAG/@ATTRIBUTO&amp;quot;)}&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;L'unica cosa &lt;em&gt;fondamentale&lt;/em&gt; da segnalare (e ricordare) è che il metodo &lt;em&gt;evaluateXPathOnVariable&lt;/em&gt; restituisce una stringa nel caso in cui l'espressione XPath riguardi un attributo o contenga la funzione &lt;em&gt;text()&lt;/em&gt;; in tutti gli altri casi restituisce un oggetto di tipo &lt;em&gt;org.w3c.dom.Node&lt;/em&gt; (che deve quindi essere manipolato con i suoi metodi).&lt;/p&gt;&lt;p&gt;Purtroppo non viene mai restituito un oggetto &lt;em&gt;org.w3c.dom.NodeList&lt;/em&gt;, quindi in caso di liste di &lt;em&gt;tag&lt;/em&gt; è conveniente recuperare il nodo che li contiene e poi ciclare su di esso.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Wed, 31 Aug 2011 09:38:20 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/gestione-dei-file-xml-su-bonita-open-solution</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-08-31T09:38:20Z</dc:date>
    </item>
    <item>
      <title>Gestire il concetto di "back" su Bonita Open Solution</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/gestire-il-concetto-di-back-su-bonita-open-solution</link>
      <description>&lt;p&gt;Premessa doverosa: Bonita è una piattaforma di Business Process Management e non serve per fare &amp;quot;applicazioni web&amp;quot;. Di conseguenza non esiste il concetto di &amp;quot;Back&amp;quot;, ossia &amp;quot;ritorna alla schermata precedente&amp;quot; in quanto le schermate sono collegate ai task di un processo e di norma non è bene saltare avanti/indietro.&lt;/p&gt;&lt;p&gt;Tuttavia possono essere casi in cui la retroazione dei task sia un requisito del processo, ad esempio per modificare il valore di un campo.&lt;/p&gt;&lt;p&gt;Vediamo quindi come fare:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;come prima cosa definiamo il task di tipo human ed associamo una nuova Entry Pageflow&lt;/li&gt;&lt;li&gt;all'interno della pagina inseriamo 2 pulsanti di submit che definiamo &lt;em&gt;Back&lt;/em&gt; e &lt;em&gt;Next&lt;/em&gt;&lt;/li&gt;&lt;li&gt;aggiungiamo al task (non al processo) una variabile di tipo &lt;em&gt;boolean &lt;/em&gt;che chiamiamo &lt;em&gt;proceed&lt;/em&gt;&lt;/li&gt;&lt;li&gt;a questo punto è sufficiente aggiungere a ciascun pulsante una action che imposti il valore di &lt;em&gt;proceed&lt;/em&gt;:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;per il pulsante &lt;em&gt;Back &lt;/em&gt;assegnamo &lt;em&gt;false &lt;/em&gt;a &lt;em&gt;proceed&lt;/em&gt;&lt;/li&gt;&lt;li&gt;per il pulsante &lt;em&gt;Next &lt;/em&gt;assegnamo &lt;em&gt;true &lt;/em&gt;a &lt;em&gt;proceed&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;a questo punto creiamo 2 transizioni in uscita dal nostro task: una collegata al task precedente ed una collegata al task seguente&lt;/li&gt;&lt;li&gt;alla transizione &lt;em&gt;precedente &lt;/em&gt;associamo la condizione &lt;em&gt;proceed==false&lt;/em&gt;&lt;/li&gt;&lt;li&gt;alla transizione &lt;em&gt;seguente&lt;/em&gt; associamo la condizione &lt;em&gt;proceed==true&lt;/em&gt; o, in alternativa, la impostiamo come &lt;em&gt;Default flow&lt;/em&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;A questo punto il Bonita Engine è in grado di gestire correttamentre la &lt;em&gt;navigazione&lt;/em&gt; tra i task, attivando la transizione giusta a seconda del valore della variabile &lt;em&gt;proceed&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Fri, 26 Aug 2011 09:35:54 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/gestire-il-concetto-di-back-su-bonita-open-solution</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-08-26T09:35:54Z</dc:date>
    </item>
    <item>
      <title>Ordinare una lista di liste in Groovy</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/ordinare-una-lista-di-liste-in-groovy</link>
      <description>&lt;p&gt;Ordinare una lista in Groovy è un'operazione piuttosto semplice, anche con liste di oggetti complessi. Infatti è sufficiente invocare il metodo sort() come negli esempi seguenti:&lt;/p&gt;&lt;p&gt;&lt;code&gt;// ordinamento di un elenco di numeri&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;def numbers = [4,2,5,3,1]&lt;br /&gt;assert [1,2,3,4,5] = numbers.sort()&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;// ordinamento di un elenco di stringhe&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;def letters = [c,d,a,b,e]&lt;br /&gt;assert [a,b,c,d,e] = letters.sort()&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;// ordinamento di un elenco di oggetti in base ai campi&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;def people = [ new Person(id: 2, name: Marco),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;       &amp;nbsp;  new &lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;(id: 1, name: Jader),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;         new &lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;(id: 3, name: Andrea) ]&lt;br /&gt;&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;people&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;.sort{ it.id }    // produce [Jader, Marco, Andrea]&lt;br /&gt;&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;people&lt;/code&gt;&lt;/code&gt;&lt;code&gt;&lt;code&gt;.sort{ it.name }&lt;/code&gt;  // produce [Andrea, Jader, Marco]&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Però, nel caso in cui ci si trovi a lavorare con un oggetto di tipo &lt;em&gt;List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;&lt;/em&gt; le cose sono un pò più complesse (ma non poi troppo).&lt;/p&gt;&lt;p&gt;Supponiamo quindi di avere una lista di oggetti che, per qualche motivo applicativo, dobbiamo trasformare in una lista di liste.&lt;/p&gt;&lt;p&gt;Come prima cosa creiamo la nostra lista di liste:&lt;/p&gt;&lt;p&gt;&lt;code&gt;def list = new ArrayList&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;()&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;// &lt;/code&gt;&lt;code&gt;people &lt;/code&gt;&lt;code&gt;rappresenta una List&amp;lt;Person&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;people.each { person-&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def objCols = new ArrayList&amp;lt;String&amp;gt;()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; objCols.add &lt;/code&gt;&lt;code&gt;person&lt;/code&gt;&lt;code&gt;.id       // indice posizionale = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; objCols.add &lt;/code&gt;&lt;code&gt;person&lt;/code&gt;&lt;code&gt;.name     // &lt;/code&gt;&lt;code&gt;indice posizionale = 1&lt;/code&gt;&lt;br /&gt;&lt;code&gt;    objCols.add &lt;/code&gt;&lt;code&gt;person&lt;/code&gt;&lt;code&gt;.surname  // &lt;/code&gt;&lt;code&gt;indice posizionale = 2&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    //...e così via&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; list.add objCols&lt;br /&gt;}&lt;/code&gt;&lt;/p&gt;&lt;p&gt;A questo punto è necessario definire un apposito comparatore che effettui il confronto posizionale tra gli elementi.&lt;/p&gt;&lt;p&gt;&lt;code&gt;byName = [&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; compare:{ a,b-&amp;gt; a[1]&amp;lt;=&amp;gt;b[1] }&lt;br /&gt;] as Comparator&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Per capire lo snippet occorre ricordare che siamo in presenza di una &lt;em&gt;List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;&lt;/em&gt; pertanto il singolo elemento della lista è una &lt;em&gt;List&amp;lt;String&amp;gt;&lt;/em&gt;; di conseguenza è necessario specificare l'indice posizionale dell'elemento della lista interna che si vuole confrontare, nel nostro caso 1 ossia il nome.&lt;/p&gt;&lt;p&gt;A questo punto è possibile ordinare la lista:&lt;/p&gt;&lt;p&gt;&lt;code&gt;list.sort(byName)            // modalità ascendente&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;list.sort(byName).reverse()  // modalità discendente&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Fri, 26 Aug 2011 08:34:50 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/ordinare-una-lista-di-liste-in-groovy</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-08-26T08:34:50Z</dc:date>
    </item>
    <item>
      <title>Impostare IDfQueryBuilder con attributi repeating multipli</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/impostare-idfquerybuilder-con-attributi-repeating-multipli</link>
      <description>&lt;p&gt;Mi sono recentemente scontrato con un problema apparentemente banale durante la realizzazione di una ricerca custom per un'applicazione Webtop-based.&lt;/p&gt; &lt;p&gt;La problematica era realizzare, tramite l'interfaccia &lt;em&gt;IDfQueryBuilder&lt;/em&gt;, una query DQL del tipo:&lt;/p&gt;   &lt;pre&gt;
SELECT *
FROM my_document
WHERE ANY (attr1 = ? AND attr2 = ?);

&lt;/pre&gt;&lt;p&gt;Entrambi gli attributi &lt;em&gt;attr1 &lt;/em&gt;e &lt;em&gt;attr2 &lt;/em&gt;sono di tipo repeating e devono essere racchiusi all'interno delle parentesti perchè mi serve fare una ricerca posizionale sui valori degli attributi, ossia una cosa del tipo:&lt;/p&gt;&lt;pre&gt;
attr1[0]=? AND attr2[0]=?
attr1[1]=? AND attr2[1]=?
attr1[2]=? AND attr2[2]=?
...e così via
&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Peccato che &lt;em&gt;IDfQueryBuilder&lt;/em&gt; non consenta di creare un'espressione composta di quel tipo, ma consente solamente le 2 parti separate:&lt;/p&gt;&lt;pre&gt;
SELECT *
FROM my_document
WHERE ANY attr1 = ? AND ANY attr2 = ?;&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Ecco quindi come fare a risolvere il problema.&lt;/p&gt;&lt;p&gt;Innanzitutto, nella classe Java che prepara la ricerca (il metodo &lt;em&gt;buildQuery&lt;/em&gt; per intenderci) è necessario aggiungere le 2 espressioni di ricerca come se gli attributi NON fossero repeating:&lt;/p&gt;&lt;pre&gt;
IDfQueryBuilder qb = ...
IDfExpressionSet set = qb.getRootExpressionSet();
set.addSimpleAttrExpression(&amp;quot;attr1&amp;quot;, IDfValue.DF_STRING,
                            IDfSimpleAttrExpression.SEARCH_OP_EQUAL,
                            true, &lt;u&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/u&gt;, &amp;quot;valore&amp;quot;);
set.addSimpleAttrExpression(&amp;quot;attr2&amp;quot;, IDfValue.DF_STRING,
                            IDfSimpleAttrExpression.SEARCH_OP_EQUAL,
                            true, &lt;u&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/u&gt;, &amp;quot;valore&amp;quot;);&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Dopodichè è necessario modificare il file &lt;em&gt;dfc-dqlhints.xml&lt;/em&gt; al fine di forzare la ricerca ad utilizzare la DQL hint &lt;em&gt;ROW_BASED&lt;/em&gt; che consente di effettuare una ricerca su attributi repeating multipli senza specificare la clausola &lt;em&gt;ANY&lt;/em&gt;.&lt;/p&gt;&lt;pre&gt;
&amp;lt;Rule&amp;gt;
   &amp;lt;Condition&amp;gt;
      &amp;lt;From&amp;gt;
         &amp;lt;Type&amp;gt;my_document&amp;lt;/Type&amp;gt;
      &amp;lt;/From&amp;gt;
      &amp;lt;Where condition=&amp;quot;all&amp;quot;&amp;gt;
         &amp;lt;Attribute&amp;gt;attr1&amp;lt;/Attribute&amp;gt;
         &amp;lt;Attribute&amp;gt;attr2&amp;lt;/Attribute&amp;gt;
      &amp;lt;/Where&amp;gt;
   &amp;lt;/Condition&amp;gt;
   &amp;lt;DQLHint&amp;gt;ENABLE(ROW_BASED)&amp;lt;/DQLHint&amp;gt;
&amp;lt;/Rule&amp;gt;
&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Questa nuova rule da aggiungere ha il significato: &lt;em&gt;per ogni query fatta sul tipo my_document che includa entrambi gli attributi attr1 e attr2, utilizzare la DQL hint ROW_BASED.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Ecco quindi che la query DQL che viene eseguita dal sistema sarà:&lt;/p&gt;&lt;pre&gt;
SELECT *
FROM my_document
WHERE attr1 = ? AND attr2 = ?
ENABLE(ROW_BASED);
&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Fri, 05 Aug 2011 10:53:29 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/impostare-idfquerybuilder-con-attributi-repeating-multipli</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-08-05T10:53:29Z</dc:date>
    </item>
    <item>
      <title>Ancora sulle Expando Tables di Liferay</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/ancora-sulle-expando-tables-di-liferay</link>
      <description>&lt;p&gt;Alcuni clienti ci hanno chiesto approfondimenti relative all'utilizzo dei campi expando sulle entità custom; siccome potrebbe interessare anche altri, eccoli di seguito anche per voi! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Estrarre un singolo field&lt;/h1&gt;&lt;p&gt;Per estrarre un singolo field dal database, Liferay ci mette a disposizione, attraverso la sua tag library, un metodo di utility:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;cite&gt;&amp;lt;liferay-ui:custom-attribute &lt;/cite&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;cite&gt;    classPK=&amp;quot;&amp;lt;%= &lt;code&gt;myEntity.getPrimaryKey()&lt;/code&gt; %&amp;gt;&amp;quot; &lt;/cite&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;cite&gt;    name=&amp;quot;&lt;strong&gt;NomeCampo&lt;/strong&gt;&amp;quot; &lt;/cite&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;cite&gt;    className=&amp;quot;it.dvel.stupid.custom.model.SimpleTable&amp;quot;&amp;gt;&lt;/cite&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;cite&gt;&amp;lt;/liferay-ui:custom-attribute&amp;gt;&lt;/cite&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Questo tag mi consente di estrarre e di visualizzare dal database di Liferay il campo di nome &lt;em&gt;NomeCampo&lt;/em&gt; del tipo &lt;em&gt;it.dvel.stupid.custom.model.SimpleTable&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Attenzione: questa tag library restituisce solamente il valore del campo scelto, in formato &lt;strong&gt;text field editabile&lt;/strong&gt; oppure &lt;strong&gt;come sola label&lt;/strong&gt;. Come faccio a pilotare questo rendering? Ma con i permessi, ovvio! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Gestire i permessi sui campi expando&lt;/h1&gt;&lt;p&gt;I&amp;nbsp;permessi sui custom fields, attivabili direttamente dal pannello di controllo, sezione Expando, mi consentono di decidere &lt;strong&gt;la politica di visualizzazione&lt;/strong&gt; dei singoli campi.&lt;/p&gt;&lt;p&gt;Nel dettaglio, agendo sulle leve di &lt;strong&gt;view&lt;/strong&gt; e&lt;strong&gt; update&lt;/strong&gt;, possiamo dire indirettamente alla tag library se i miei utenti &lt;strong&gt;possono vedere il valore&lt;/strong&gt; oppure se &lt;strong&gt;lo possono anche editare&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;La tag library, a runtime, si occuperà per noi di presentare il campo nella maniera più opportuna a seconda dei permessi impostati sui singoli campi!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Casi d'uso in cui usare i campi Expando e i permessi&lt;/h1&gt;&lt;p&gt;Bhe, ovviamente ce ne sono un'infinità!&lt;/p&gt;&lt;p&gt;Vi do' i più semplici e i più immediati come riscontro.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Ad esempio, facciamo l'ipotesi che aggiungiamo il campo salario sull'anagrafica dell'utente.&lt;/p&gt;&lt;p&gt;Vorremo fare in modo che:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;l'utente possa solo visualizzare il campo (permesso di view)&lt;/li&gt;&lt;li&gt;l'utente amministrativo possa anche modificarlo (permesso di view + update)&lt;/li&gt;&lt;li&gt;gli altri utenti non vedano proprio il campo (togliamo il permesso di view)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Oppure: vogliamo creare una form di data entry, usando i campi Expando, e permetterne il popolamento a due figure differenti.&lt;/p&gt;&lt;p&gt;In questo caso mi basterà giocare sui permessi (come ho spiegato nell'esempio sopra relativo agli utenti) e sarà un gioco da ragazzi farlo! ;)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Spero che questo tip vi sia stato utile! :)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Alla prossima! :)&lt;/p&gt;</description>
      <pubDate>Fri, 08 Jul 2011 06:12:25 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/ancora-sulle-expando-tables-di-liferay</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-07-08T06:12:25Z</dc:date>
    </item>
    <item>
      <title>ExpandoTable sulle vostre entità custom!</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/expandotable-sulle-vostre-entita-custom!</link>
      <description>&lt;p&gt;&lt;strong&gt;Problema&lt;/strong&gt;:&amp;nbsp;vorreste poter aggiungere degli attributi custom alle vostre entità &lt;strong&gt;a runtime,&lt;/strong&gt; ma non sapete come fare.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Soluzione&lt;/strong&gt;:&amp;nbsp;usate le expando tables di Liferay! ;)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Per farlo dovete procedere in questo modo:&lt;/p&gt; &lt;ol&gt;     &lt;li&gt;&lt;a href="#registrare-entita-per-utilizzare-il-framework-expando-blog-su-liferay"&gt;Registrare l'entità perchè utilizzi il framework delle expando&lt;/a&gt;&lt;/li&gt;     &lt;li&gt;&lt;a href="#predisporre-la-jsp-per-mostrare-i-campi-expando-blog-su-liferay"&gt;Predisporre la JSP&amp;nbsp;per mostrare i campi expando&lt;/a&gt;&lt;/li&gt;     &lt;li&gt;&lt;a href="#predisporre-la-action-per-salvare-i-campi-expando-blog-su-liferay"&gt;Predisporre la action per salvare i campi expando&lt;/a&gt;&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;h1&gt;&lt;a name="registrare-entita-per-utilizzare-il-framework-expando-blog-su-liferay"&gt;Registrare l'entità perchè utilizzi il framework delle expando&lt;/a&gt;&lt;/h1&gt; &lt;p&gt;Per registrare un'entità custom al fine di poterla &amp;quot;collegare&amp;quot; alle tabelle expando, dobbiamo creare una classe che estenda &lt;code&gt;com.liferay.portlet.expando.model.BaseCustomAttributesDisplay&lt;/code&gt;&lt;/p&gt; &lt;p&gt;Questa classe, infatti, contiene tutta la logica che serve per poter gestire l'interfaccia nel pannello di controllo sulla parte dei custom fields.&lt;/p&gt; &lt;p&gt;Questa è l'implementazione della nostra classe:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;package it.dvel.stupid.custom.display;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; import it.dvel.stupid.custom.model.*;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; import com.liferay.portal.theme.*;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; import com.liferay.portlet.expando.model.*;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;  public class SimpleTableCustomAttributesDisplay extends&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; BaseCustomAttributesDisplay {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public static final String CLASS_NAME = &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      SimpleTable.class.getName(); &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;    // Qui gli diciamo come si chiama la nostra classe&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public String getClassName() {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return CLASS_NAME;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;    // Qui gli diciamo dove trovare l'icona che deve &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;    //usare per la nostra entità&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;    public String getIconPath(ThemeDisplay themeDisplay) {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return themeDisplay.getPathThemeImages() + &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;           &amp;quot;/common/user_icon.png&amp;quot;;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; }&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Una volta creata la nostra classe, dobbiamo dire a Liferay di caricarla quando installiamo il nostro portlet.&lt;/p&gt; &lt;p&gt;Come sempre, dobbiamo andarla a mappare nel &lt;code&gt;liferay-portlet.xml&lt;/code&gt; dentro a &lt;code&gt;WEB-INF&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;code&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;portlet&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      &amp;lt;portlet-name&amp;gt;helloworldportlet&amp;lt;/portlet-name&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      &amp;lt;icon&amp;gt;/icon.png&amp;lt;/icon&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      ...&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      &amp;lt;custom-attributes-display&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;        it.dvel.stupid.custom.display.SimpleTableCustomAttributesDisplay&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      &amp;lt;/custom-attributes-display&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;      ...&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;    &amp;lt;/portlet&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;h1&gt;&lt;a name="predisporre-la-jsp-per-mostrare-i-campi-expando-blog-su-liferay"&gt;Predisporre la JSP&amp;nbsp;per mostrare i campi expando&lt;/a&gt;&lt;/h1&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Ok: a questo punto siamo pronti per &lt;strong&gt;mostrare&lt;/strong&gt; nella nostra pagina i campi. Tenete presente che quello che stiamo per fare serve solamente per estrarre gli attributi. Non stupitevi quando, provando, non li vedrete: se non andate &lt;strong&gt;prima&lt;/strong&gt; nel pannello di controllo ad inserire i campi custom, è normale che poi non li vediate.. ;)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Comunque, andate nella vostra JSP e incollate questo codice:&lt;/p&gt; &lt;p&gt;&lt;br /&gt; &lt;code&gt; &amp;lt;h3&amp;gt;&amp;lt;liferay-ui:message key=&amp;quot;custom-fields&amp;quot; /&amp;gt;&amp;lt;/h3&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;lt;aui:fieldset&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;liferay-ui:custom-attribute-list&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; className=&amp;quot;it.dvel.stupid.custom.model.SimpleTable&amp;quot;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; classPK=&amp;quot;&amp;lt;%= (myEntity != null)&amp;nbsp;?&amp;nbsp;myEntity.getPrimaryKey()&amp;nbsp;:&amp;nbsp;0 %&amp;gt;&amp;quot;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; editable=&amp;quot;&amp;lt;%= true %&amp;gt;&amp;quot;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; label=&amp;quot;&amp;lt;%= true %&amp;gt;&amp;quot;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; /&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;lt;/aui:fieldset&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Ovviamente &lt;strong&gt;questo va all'interno di un campo form&lt;/strong&gt;, perche' dopo i valori che l'utente immetterà dovranno essere passati in post alla nostra action.&lt;/p&gt; &lt;p&gt;Non commento la tag library perchè mi pare sia self explanatory.. ;)&lt;/p&gt; &lt;p&gt;Interessante notare, tuttavia, che posso scegliere di mostrare la lista degli attributi &lt;strong&gt;editabile&lt;/strong&gt; o meno; in questo modo, mettendo &lt;code&gt;true&lt;/code&gt; o &lt;code&gt;false&lt;/code&gt;, posso scegliere come il componente deve renderizzare i miei campi.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;h1&gt;&lt;a name="predisporre-la-action-per-salvare-i-campi-expando-blog-su-liferay"&gt;Predisporre la action per salvare i campi expando&lt;/a&gt;&lt;/h1&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Molto bene! A questo punto siamo pronti per &lt;strong&gt;salvare&lt;/strong&gt; i valori immessi dagli utenti sull'interfaccia generata dalla nostra JSP.&lt;/p&gt; &lt;p&gt;Bene, tenetevi forte amici, perchè è qui che si vede tutta la potenza di un framework applicativo come Liferay! ;)&lt;/p&gt; &lt;p&gt;Questo è il codice che salva i valori immessi nel database:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;// Ovviamente sono all'interno di un metodo di tipo processAction()..&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;// &lt;/code&gt;&lt;code&gt;Per prima cosa mi serve una istanza di ServiceContext&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &lt;p&gt;ServiceContext serviceContext =&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ServiceContextFactory.getInstance(&lt;/p&gt;&lt;p&gt;SimpleTable.class.getName(), actionRequest);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; SimpleTable table =&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SimpleTableLocalServiceUtil.createSimpleTable(&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CounterLocalServiceUtil.increment(SimpleTable.class.getName()));&lt;/p&gt; &lt;p&gt;// Faccio tutte le mie set sulla mia entità&lt;/p&gt; &lt;p&gt;...&lt;/p&gt; &lt;p&gt;// Ed ecco dove accade la magia! ;)&lt;/p&gt; &lt;p&gt;table.setExpandoBridgeAttributes(serviceContext);&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;// Poi faccio la insert&lt;/p&gt; &lt;p&gt;SimpleTableLocalServiceUtil.updateSimpleTable(table, false);&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;That's it!&amp;nbsp;:)&lt;/p&gt; &lt;p&gt;Abbiamo salvato i valori che l'utente immette nella form con uno sforzo minimo! ;)&lt;/p&gt; &lt;p&gt;Come abbiamo fatto?!&lt;/p&gt; &lt;p&gt;La magia accade all'interno dell'oggetto &lt;code&gt;ServiceContext&lt;/code&gt;. In breve, questo oggetto è l'implementazione di un pattern che ci consente di avere delle funzionalità common a più oggetti senza avere l'onere di estendere una classe padre.&lt;/p&gt; &lt;p&gt;Come vedete, il framework offre delle utility per accedere ad una istanza di questo oggetto a partire da una request; questo ci offre il vantaggio di disinteressarci completamente della logica di popolamento di questo oggetto, perchè gestita direttamente dal framework, ma ci consente di beneficiare enormemente delle sue funzionalità.&lt;/p&gt; &lt;p&gt;Nello specifico, il &lt;code&gt;serviceContext&lt;/code&gt; è l'oggetto all'interno del quale io posso trovare, ad esempio, il &lt;code&gt;companyId&lt;/code&gt; o lo &lt;code&gt;scopeGroupId&lt;/code&gt; in cui l'utente stà operando.&lt;/p&gt; &lt;p&gt;Altra funzionalità molto importante del &lt;code&gt;serviceContext&lt;/code&gt;, ad esempio, è la gestione trasparente dei &lt;code&gt;tags&lt;/code&gt; e delle &lt;code&gt;categories&lt;/code&gt; che l'utente può scegliere dall'interfaccia di Liferay, ma di questo parlerò più diffusamente in un post successivo! :)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Alla prossima! :)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;  &lt;h1&gt;Referenze&lt;/h1&gt; &lt;ul&gt;     &lt;li&gt;&lt;a target="_blank" href="http://www.liferay.com/community/wiki/-/wiki/Main/Developing+with+Expando"&gt;Expando&lt;/a&gt; sul wiki di Liferay&lt;/li&gt;     &lt;li&gt;&lt;a target="_blank" href="http://www.liferay.com/community/wiki/-/wiki/Main/ServiceContext+Pattern"&gt;ServiceContext&lt;/a&gt; sul wiki di Liferay&lt;/li&gt; &lt;/ul&gt;</description>
      <pubDate>Fri, 01 Jul 2011 18:10:47 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/expandotable-sulle-vostre-entita-custom!</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-07-01T18:10:47Z</dc:date>
    </item>
    <item>
      <title>Documentum - IDfQueryBuilder ed esecuzione subquery DQL</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/documentum-idfquerybuilder-ed-esecuzione-subquery-dql</link>
      <description>&lt;p&gt;In un precedente blog abbiamo visto come realizzare query DQL su attributi repeating mediante l'oggetto &lt;code&gt;IDfQueryBuilder&lt;/code&gt;; quello che vedremo adesso è un pò più complesso e riguarda l'esecuzione di subquery, sempre all'interno di un'applicazione Webtop/WDK.&lt;br /&gt;Supponiamo quindi di dover eseguire la seguente query DQL:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;SELECT *&lt;br /&gt;FROM my_document1&lt;br /&gt;WHERE my_field IN&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT r_object_id FROM my_document2 WHERE ...);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;L'idea alla base della soluzione che stiamo per vedere è quella di eseguire la subquery &lt;em&gt;separatamente&lt;/em&gt;, utilizzando poi l'elenco dei valori nella clausola &lt;code&gt;IN&lt;/code&gt;.&lt;br /&gt;Come prima cose vediamo come eseguire la subquery e ricavare i valori.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;protected IDfQueryBuilder buildQuery() throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //[...]&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // creazione della query DQL&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfPassThroughQuery query = getSearchInfo().getSearchService()&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            .newQueryMgr()&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            .newPassThroughQuery(&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                &amp;quot;SELECT r_object_id FROM my_document2 WHERE ...&amp;quot;&lt;/code&gt;&lt;code&gt;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // si associano alla query creta tutti i DOCBASE presenti&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // nella connessione a Documentum&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfEnumeration availableSources = getSearchInfo().getSearchService()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; .getSourceMap().getAvailableSources();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (availableSources.hasMoreElements()) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IDfSearchSource source =&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                (IDfSearchSource) availableSources.nextElement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; query.addSelectedSource(source.getName());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // creazione del processor della query&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfQueryProcessor queryProc = getSearchInfo().getSearchService()&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;            .newQueryProcessor(query, true);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // si esegue la query DQL e si valorizza la lista dei risultati&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfList listForINClause = new DfList(IDfList.DF_STRING);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfResultsSet resultsSet = queryProc.getResults();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (resultsSet.next()) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IDfResultEntry entry = resultsSet.getResult();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; listForINClause.appendString(entry.getString(&amp;quot;r_object_id&amp;quot;));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //[...]&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;A questo punto è sufficiente aggiungere la clausola &lt;code&gt;IN&lt;/code&gt; al nostro oggetto &lt;code&gt;IDfQueryBuilder &lt;/code&gt;ed il gioco è fatto.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;IDfExpressionSet expressionSet = qb.getRootExpressionSet();&lt;br /&gt;expressionSet.addValueListAttrExpression(&amp;quot;my_field&amp;quot;,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                 IDfValue.DF_STRING,&lt;br /&gt;                    &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IDfValueListAttrExpression.SEARCH_OP_IN,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                 false,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                 false,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                 listForINClause);&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;Il punto fondamentale è il metodo &lt;code&gt;addValueListAttrExpression &lt;/code&gt;pertanto analizziamone i parametri usati (confrontandoli con la query DQL vista sopra):&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;&amp;quot;my_field&amp;quot;&lt;/code&gt;, è il nome del campo su cui dobbiamo fare la ricerca IN;&lt;/li&gt;&lt;li&gt;&lt;code&gt;IDfValue.DF_STRING&lt;/code&gt;, è il tipo del campo (stringa, intero, boolean, ...);&lt;/li&gt;&lt;li&gt;&lt;code&gt;IDfValueListAttrExpression.SEARCH_OP_IN&lt;/code&gt;, è l'operazione di filtro da eseguire (nel nostro caso IN);&lt;/li&gt;&lt;li&gt;&lt;code&gt;false&lt;/code&gt;, abilita/disabilita la ricerca case sensitive;&lt;/li&gt;&lt;li&gt;&lt;code&gt;false&lt;/code&gt;, configura l'attributo come repeating in modo che il framework utilizzi la clausola ANY;&lt;/li&gt;&lt;li&gt;&lt;code&gt;listForINClause&lt;/code&gt;, &lt;u&gt;&lt;strong&gt;elenco dei valori da cercare&lt;/strong&gt;&lt;/u&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;br /&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Fri, 01 Jul 2011 13:34:18 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/documentum-idfquerybuilder-ed-esecuzione-subquery-dql</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-07-01T13:34:18Z</dc:date>
    </item>
    <item>
      <title>Documentum - IDfQueryBuilder e query su attributi repeating</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/documentum-idfquerybuilder-e-query-su-attributi-repeating</link>
      <description>&lt;p&gt;Per realizzare una ricerca custom all'interno di un'applicazione web Documentum-based (mediante l'utilizzo di Webtop oppure di WDK) è solitamente necessario creare un'apposito &amp;quot;component&amp;quot; e conseguentemente una classe Java per le eventuali funzionalità custom di business.&lt;br /&gt;Questa classe Java solitamente estende le superclassi &lt;code&gt;AdSearchEx &lt;/code&gt;(oppure direttamente &lt;code&gt;AdvSearchExBase&lt;/code&gt;) andando a specificare il metodo:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;protected IDfQueryBuilder buildQuery() throws Exception;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;che si occupa di configurare e restituire un oggetto &lt;code&gt;IDfQueryBuilder &lt;/code&gt;contenente la struttura della query da eseguire.&lt;br /&gt;Supponiamo quindi di dover eseguire una query DQL su di un campo di tipo repeating e vediamo come configurare l'oggetto &lt;code&gt;IDfQueryBuilder&lt;/code&gt;; la query da eseguire è la seguente (notare la clausola &lt;code&gt;ANY &lt;/code&gt;per la ricerca su attributi repeating):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;SELECT * FROM my_document WHERE ANY my_field = 'text';&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Il codice Java che andremo a scrivere è il seguente:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;protected IDfQueryBuilder buildQuery() throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // si recupera dalla superclasse un oggetto preconfigurato&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // su cui lavorare&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfQueryBuilder qb = super.buildQuery();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // in alternativa sarebbe stato possibile creare un nuovo&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // oggetto da zero ricordandosi però di configurare&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    // correttamente la DOCBASE di riferimento&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // IDfQueryBuilder qb = getSearchInfo().getSearchService()&lt;/code&gt;&lt;code&gt;.newQueryMgr()&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;    //                            .newQueryBuilder(&amp;quot;my_document&amp;quot;, -1, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // qb.addSelectedSource(getDfSession().getDocbaseName());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // [...] eventuali colonne da restituire in output&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // si recupera l'insieme delle clausole di WHERE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IDfExpressionSet expressionSet = qb.getRootExpressionSet();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expressionSet.addSimpleAttrExpression(&amp;quot;my_field&amp;quot;,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                  IDfValue.DF_STRING,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                  IDfSimpleAttrExpression.SEARCH_OP_EQUAL,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                  false,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                  true,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;                                  &amp;quot;text&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // [...] eventuali clausole di ordinamento&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return qb;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Il punto fondamentale è il metodo &lt;code&gt;addSimpleAttrExpression &lt;/code&gt;pertanto analizziamone i parametri usati (confrontandoli con la query DQL vista sopra):&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;&amp;quot;my_field&amp;quot;&lt;/code&gt;, è il nome del campo su cui dobbiamo fare la ricerca;&lt;/li&gt;&lt;li&gt;&lt;code&gt;IDfValue.DF_STRING&lt;/code&gt;, è il tipo del campo (stringa, intero, boolean, ...);&lt;/li&gt;&lt;li&gt;&lt;code&gt;IDfSimpleAttrExpression.SEARCH_OP_EQUAL&lt;/code&gt;, è l'operazione di filtro da eseguire (=, &amp;gt;, &amp;lt;, ...);&lt;/li&gt;&lt;li&gt;&lt;code&gt;false&lt;/code&gt;, abilita/disabilita la ricerca case sensitive;&lt;/li&gt;&lt;li&gt;&lt;code&gt;true&lt;/code&gt;, &lt;u&gt;&lt;strong&gt;configura l'attributo come repeating&lt;/strong&gt;&lt;/u&gt; in modo che il framework utilizzi la clausola &lt;code&gt;ANY&lt;/code&gt;;&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;quot;text&amp;quot;&lt;/code&gt;, testo da cercare.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Fri, 01 Jul 2011 12:50:50 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/documentum-idfquerybuilder-e-query-su-attributi-repeating</guid>
      <dc:creator>Marco Napolitano</dc:creator>
      <dc:date>2011-07-01T12:50:50Z</dc:date>
    </item>
    <item>
      <title>Utilizzare il captcha in Liferay</title>
      <link>http://blog.d-vel.com/web/blog/home/-/blogs/utilizzare-il-captcha-in-liferay</link>
      <description>&lt;p&gt;In questo post vediamo come possiamo utilizzare il &lt;a target="_blank" href="http://it.wikipedia.org/wiki/Captcha"&gt;captcha&lt;/a&gt; all'interno delle nostre portlet.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Per prima cosa dobbiamo mostrare l'immagine del captcha sulla nostra pagina, per permettere all'utente di leggerlo.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Per fare questo dobbiamo:&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;sovrascrivere un metodo nella nostra portlet che viene specificato dal JSR286:&amp;nbsp;serveResource&lt;/li&gt;     &lt;li&gt;utilizzare una taglibrary di Liferay per poter mostrare l'immagine&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Vediamo l'implementazione del metodo che dobbiamo implementare nella nostra action:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp; public void serveResource(ResourceRequest resourceRequest, &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;        ResourceResponse resourceResponse)&amp;nbsp;&lt;/code&gt;&lt;code&gt;throws IOException, PortletException {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;     CaptchaUtil.serveImage(resourceRequest, resourceResponse);&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;Come vedete, questo metodo serve l'immagine utilizzando la classe CaptchaUtil messa a disposizione da Liferay.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Ora dobbiamo mostrare l'immagine nella nostra JSP; per fare questo dobbiamo:&lt;/p&gt; &lt;ol&gt;     &lt;li&gt;importare la tag library che contiene la facility per il captcha&lt;/li&gt;     &lt;li&gt;utilizzare la tag library&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Per importare nella JSP la tag library usiamo questo statement:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;lt;%@ taglib uri=&amp;quot;http://liferay.com/tld/ui&amp;quot; prefix=&amp;quot;liferay-ui&amp;quot; %&amp;gt;&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Mentre, per stampare il captcha sulla pagina, utilizzamo questa chiamata:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;lt;portlet:resourceURL var=&amp;quot;captchaURL&amp;quot;/&amp;gt;&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;...&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;lt;liferay-ui:captcha url=&amp;quot;&amp;lt;%= captchaURL %&amp;gt;&amp;quot; /&amp;gt;&lt;br /&gt; &lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;La prima chiamata genera, utilizzando la API di portale, la stringa per chiamare il nostro metodo nella action; la seconda, invece, è la faciliy messa a disposizione da Liferay per mostrare in pagina il captcha.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Ora, una volta messo tutto dentro ad un tag form, possiamo andare nel codice a verificare che l'utente immetta correttamente il valore del captcha:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp; public void processAction(ActionRequest actionRequest,&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;       ActionResponse actionResponse) throws IOException, PortletException {&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; try {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;           // Controllo l'input utente&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;   CaptchaUtil.check(actionRequest);&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (CaptchaException e) {&lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;            // in caso d'errore segnalo in pagina l'errore&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; SessionErrors.add(actionRequest, e.getClass().getName());&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;  }&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;   }&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Per gestire in pagina l'errore, possiamo utilizzare le tag library di Liferay:&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;code&gt;&amp;lt;liferay-ui:error exception=&amp;quot;&amp;lt;%= CaptchaTextException.class %&amp;gt;&amp;quot; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;   message=&amp;quot;Verificare il testo del captcha!&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;liferay-ui:error exception=&amp;quot;&amp;lt;%= CaptchaMaxChallengesException.class %&amp;gt;&amp;quot; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&lt;code&gt;   message=&amp;quot;Numero massimo di tentativi raggiunti!&amp;quot; /&amp;gt;&lt;br /&gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;Se tutto va come deve, abbiamo il captcha nella nostra form di dati e riusciamo a verificarlo senza problemi! ;)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;Alla prossima!&amp;nbsp;:)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Sun, 26 Jun 2011 19:23:14 GMT</pubDate>
      <guid isPermaLink="false">http://blog.d-vel.com/web/blog/home/-/blogs/utilizzare-il-captcha-in-liferay</guid>
      <dc:creator>Jader Jed Francia</dc:creator>
      <dc:date>2011-06-26T19:23:14Z</dc:date>
    </item>
  </channel>
</rss>


