bloggers bloggers

Jader Jed Francia
Messaggi: 62
Stelle: 0
Data: 15/12/20
Marco Napolitano
Messaggi: 78
Stelle: 0
Data: 12/06/20
Paolo Gambetti
Messaggi: 2
Stelle: 0
Data: 11/11/19
Katia Pazzi
Messaggi: 1
Stelle: 0
Data: 27/06/19
Ezio Lombardi
Messaggi: 11
Stelle: 0
Data: 10/04/18
Chiara Mambretti
Messaggi: 25
Stelle: 0
Data: 27/02/17
Serena Traversi
Messaggi: 3
Stelle: 0
Data: 21/07/16
Francesco Falanga
Messaggi: 8
Stelle: 0
Data: 14/06/16
Antonio Musarra
Messaggi: 2
Stelle: 0
Data: 18/11/13
Simone Celli Marchi
Messaggi: 6
Stelle: 0
Data: 09/07/13
Indietro

Come usare i typeSettings sui Layout!

 

Ciao a tutti!

Tutti sapete che la tabella Layout contiene le pagine che vengono generate dal portale; forse non tutti però conoscete i typeSettings, un campo della tabella Layout all'interno del quale, tipicamente, il portale scrive l'associazione tra gli spazi dei singoli layouttpl e le relative portlet.

Ma come possiamo usarli "a nostro piacimento"?

Questa domanda, come sempre, mi è stata fatta da un gruppo di ragazzi che sta facendo un ottimo lavoro mentre fanno il porting dalla 6.2 EE alle 7.2 EE di una applicazione che non hanno scritta loro ma che hanno ereditato.

(N.d.J: Della serie: doppio carpiato rovesciato, ma bendati e legati dopo aver girato in tondo per 5 minuti e poi fatti saltare da 35 metri d'altezza... Per darvi un'idea! :))

E la domanda, più che lecita, mi è stata rivolta perché chi ha codificato prima di loro l'applicativo, ha pensato bene di utilizzare i typeSettings anche per salvare caratteristiche delle pagine.

Posto che noi in D'vel è una vita che facciamo queste robe (ma ce la siamo sempre spicciata facile usando i Custom Attribute :)), m'intrigava la soluzione che avevano realizzato e quindi ho lavorato con loro per riuscire a fare il porting del codice sulla 7.2.

Il caso funzionale

Il caso funzionale che avevano mappato era semplice: in alcune pagine è presente una portlet (scusate: widget, siamo sulla 7.. :D); questo widget però deve ereditare alcuni parametri per essere configurato, così chi ha codificato l'applicativo ha pensato bene di salvare nei typeSettings questi parametri.

(N.d.J: faccio notare che la portlet poteva essere semplicemente configurata sulle singole pagine, senza stare tanto ad impazzire; ma non chiedetemi perché è stata scelta questa strada: se la vita fosse semplice a noi non ci cercherebbe nessuno, quindi.. ;D).

La vecchia implementazione

Per riuscire a fare questa implementazione, i vecchi developer avevano proceduto in questo modo:

  • avevano fatto un bell'hook sulla portlet che gestiva il back end della gestione delle pagine;
  • avevano fatto una bella JSP che si agganciava al form-navigator della gestione delle pagine;
  • accedendo alla nuova voce all'interno del form-navigator avevano messo la loro bella JSP;
  • al submit i dati venivano salvati trasparentemente nella Layout, così che il gioco fosse fatto!

Analizzando nel dettaglio la loro implementazione, in effetti posso anche riconoscere che è sicuramente più elegante e figa della nostra:

  • il cliente non accede ai "Campi personalizzati" ma al back end standard di prodotto;
  • all'interno delle voci di configurazione della pagina c'era la loro voce;
  • accedendo alla loro voce c'era una form "impaginata a modo" (e non autogenerata come quella dei Custom Fields) che non era in effetti niente male..

Cavolo, allora la sfida si faceva interessante! :)

La nuova implementazione

La prima cosa che c'era da fare, quindi, era sostituire l'hook che, sulla 6.2, si agganciava con una JSP deployata sul portale, al form-navigator presente all'interno della gestione delle pagine.

Nella 7, il form-navigator ovviamente si è evoluto ed è quindi diventato necessario sviluppare due Components per poterlo utilizzare / per potercisi collegare in maniera trasparente.

Il primo serve per creare la categoria all'interno del menù di navigazione del portale; il secondo per creare le singole sezioni che ci sono all'interno di questo menu.

Quindi abbiamo proceduto in questo modo; prima abbiamo creato la categoria:


package it.dvel.playground.web.layout;

import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorCategory;
import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorConstants;

import java.util.Locale;

import org.osgi.service.component.annotations.Component;

@Component(
   immediate = true, 
   property = "form.navigator.category.order:Integer=10", 
   service = FormNavigatorCategory.class)
public class CustomLayoutFormNavigatorCategory implements FormNavigatorCategory {

   @Override
   public String getFormNavigatorId() {
      return FormNavigatorConstants.FORM_NAVIGATOR_ID_LAYOUT;
   }

   @Override
   public String getKey() {
      // Io ho fatto una PoC; voi fate i bravi e usate una COSTANTE!! :)
      return "custom-category";
   }

   @Override
   public String getLabel(Locale locale) {
      return LanguageUtil.get(locale, "custom-category");
   }
}

Fatto questo, abbiamo creato il pezzo di pagina che ci interessava.. O meglio: la entry che si sarebbe agganciata alla nostra category custom:


package it.dvel.playground.web.layout;

import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.model.Layout;
import com.liferay.portal.kernel.servlet.taglib.ui.BaseJSPFormNavigatorEntry;
import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorConstants;
import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorEntry;

import java.util.Locale;

import javax.servlet.ServletContext;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component(
     property = "form.navigator.entry.order:Integer=100", 
     service = FormNavigatorEntry.class)
public class CustomLayoutFormNavigatorEntry extends BaseJSPFormNavigatorEntry
    implements FormNavigatorEntry {

   @Override
   protected String getJspPath() {
      return "/html/admin_layout/my_custom_fields_to_manage.jsp";
   }

   @Override
   public String getCategoryKey() {
      // Le costanti ragazzi: voi usate le costanti! ;)
      return "custom-category";
   }

   @Override
   public String getFormNavigatorId() {
      return FormNavigatorConstants.FORM_NAVIGATOR_ID_LAYOUT;
   }

   @Override
   public String getKey() {
      // Non mi stancherò mai di ripetermi: usate le costanti! ;)
      return "custom-entry";
   }

   @Override
   public String getLabel(Locale locale) {
      return LanguageUtil.get(locale, getKey());
   }

   @Override
   @Reference(
      target = "(osgi.web.symbolicname=my-poc-module-web)",
      unbind = "-")
   public void setServletContext(ServletContext servletContext) {
      // Questo setter è strategico: permette al container di recuperare
      // la JSP che abbiamo indicato sopra direttamente dal nostro bundle
      // e non a raglio da chissà quale pacchetto!
      // Ricordatevi che questa roba sarà eseguita dal bundle standard della
      // gestione delle pagine!
      super.setServletContext(servletContext);
   }
}

E anche questa è fatta!

Ora non rimaneva che sistemare la nostra JSP, all'interno del nostro bundle, e far gestire tutto al componente standard di portale.

Ed ecco qui la JSP:

<%@ include file="/META-INF/resources/html/init.jsp"%>
<%

// Recupero il plid del Layout visualizzato dalla request
Long selPlid = ParamUtil.getLong(renderRequest,"selPlid");

// Recupero il Layout usando il plid
Layout selLayout = LayoutLocalServiceUtil.getLayout(selPlid);

UnicodeProperties layoutTypeSettings = null;

if (selLayout != null) {
    // Se il Layout non è nullo, recupero le typeSettings
	layoutTypeSettings = selLayout.getTypeSettingsProperties();
}
%>

<liferay-ui:error-marker key="error-section" value="my-custom-fields-to-manage-error-message" />

<aui:model-context bean="<%= selLayout %>" model="<%= Layout.class %>" />

<h3><liferay-ui:message key="my-custom-fields-to-manage-title" /></h3>
<aui:fieldset cssClass="lfr-portrait-editor">
<%
    String riskType = GetterUtil.getString(layoutTypeSettings.getProperty("risk-type"));
%>
   <aui:select label="risk-type" 
      name="TypeSettingsProperties--risk-type--" showemptyoption="<%= true %>">
      <aui:option label="type-car" 
         selected="<%= "CAR".equals(riskType) %>" value="<%=Constants.CAR%>"/>
      <aui:option label="type-motorbike" 
         selected="<%= "MOTORBIKE".equals(riskType)%>" value="<%=Constants.MOTORBIKE%>"/>
      <aui:option label="type-easy" 
         selected="<%= "EASY".equals(riskType) %>" value="<%= Constants.EASY %>"/>
      <aui:option label="type-quote" 
         selected="<%= "QUOTE".equals(riskType) %>" value="<%= Constants.VERTIQUOTE %>"/>
      <aui:option label="type-home" 
         selected="<%= "HOME".equals(riskType) %>" value="<%= Constants.HOME %>"/>
      <aui:option label="type-other-vehicle" 
         selected="<%= "OTHER_VEHICLE".equals(riskType) %>" value="<%=Constants.OTHER_VEHICLE%>"/>
      <aui:option label="type-ivass" 
         selected="<%= "IVASS".equals(riskType) %>" value="<%= Constants.IVASS %>"/>
   </aui:select>
</aui:fieldset>

Ed eccolo qui, l'utimo tassello del puzzle! :)

Una nota importante: come potete vedere, nel nome della select è stato inserito:

TypeSettingsProperties--risk-type--

che i più attenti di voi avranno già riconosciuto come un meccanismo "automatico" che viene usato dal portale per leggere e salvare arbitrari valori che arrivano dal web (di fatto è la stessa convenzione che si utilizza sul salvataggio delle configurazioni!).

Detto questo, ovviamente, deploy, navigazione, test e... Funziona! ;)

A questo punto, anche voi (come noi :D), adesso potete utilizzare le typeSettings di pagina come un modo più elegante e furbo per far configurare parametri al Cliente.

.. Sempre che non abbiate una fretta del diavolo e i Custom Attribute non vi sembrino molto più smart e semplici da utilizzare!!

Direi che anche per oggi è tutto: se avete dubbi o domande, come sempre, sono a vostra disposizione; scriveteci nei commenti e fateci sapere che ne pensate!

Buona giornata a tutti! ;)

Successivo
Commenti
Aggiungi Commento
Marco Napolitano
Ammetto di essere molto curioso di sapere quale fosse l'esatto requisito, visto che la configurazione della portlet è una caratteristica di portale che sulla 6.2 è anche semplice da implementare.
Scomodare i TypeSettings, facendo addirittura un hook sul portale mi sembra la peggiore delle soluzioni; tra l'altro sui TypeSettings non c'è alcun tipo di controllo..
I custom field almeno sono tipizzati, indicizzabili, ricercabili, profilabili; Ok, l'interfaccia non sarà bellissima però sono molto robusti e completamente integrati.
Inviato il 15/12/20 10.45.
Jader Jed Francia
Hai perfettamente ragione Napo!
Però l'avevano sviluppata così e siccome è un porting questo era il risultato che dovevamo ottenere.. Lo sai come sono i clienti in generale, no? ;)
Detto questo, come scrivo anche nell'articolo, anch'io concordo che i custom fields sono una soluzione molto più smart (infatti è quella che adottiamo anche noi più spesso! emoticon).
Aggiungo poi che, proprio se vogliamo essere pignoli ;), introdurre anche solo una configuration di portlet in questo caso poteva bastare, senza fare tutti questi castelli che hanno fatto. Soprattutto perché, da specifica JSR, le preference che vengono salvate possono anche essere validate, così da gestire eventuali casi di "impostazioni sbagliate".. emoticon
Inviato il 15/12/20 19.34 in risposta a Marco Napolitano.