Prezentační vrstva v CzechIdM

Tento článek pojednává o tom, jakým stylem je řešena prezentační vrstva v CzechIdM.
Prezentační vrstva CzechIdM je založena na technologiích JavaServer Faces (JSF), Facelets a RichFaces. JavaServer Faces je framework, který usnadňuje vývoj uživatelských rozhraní JavaServer aplikací, dále používáme technologii Facelets, což zjednodušuje a zpřehledňuje samotný vývoj prezentační vrstvy. Podpora Ajaxu je zajištěna použitím knihoven projektu RichFaces. Z tohoto frameworku používáme taktéž některé komponenty. K modelování procesů používáme jBPM workflow, které jsou spouštěny po každém kliknutí na odkaz, tlačítko formuláře atd.

Uživatelské rozhraní CzechIdM

Uživatelské rozhraní CzechIdM je rozděleno na dvě oddělené části. První část je určena pro administrátory systému a druhá část pro běžné uživatele. Po vizuální stránce jsou si obě rozhraní velmi podobná.

Důvodem k tomuto rozdělení je bezpečnost, tedy to, aby se běžný uživatel nemohl dostat k funkcím, které jsou dostupné pouze pro administrátory. Administrátorské rozhraní nabízí větší možnosti správy CzechIdM, jako je správa uživatelů, organizací, rolí, workflow a další.

Workflow

Worfklow slouží k popisu procesů a k ukládání jejich stavů. Za každým formulářem či výpisem dat v CzechIdm se skrývá nějaké worfklow.

Tato workflow slouží k popisu jednotlivých stavů formuláře a k inicializaci potřebných parametrů, tedy hlavně k načtení dat z databáze, aby mohly tyto data dále být zobrazeny uživateli. Workflow se taktéž používají k ukládání dat z formulářů, které do nich zadá uživatel.

Popis workflow

Workflow jsou jednoduché XML soubory. Jsou tvořena pomocí mnoha elementů, kde většinou si vystačíme s pár uzly <node>, stavy <state> a přechody <transition>.
Každé worklfow musí být pojmenováno, protože pomocí tohoto názvu se spouští. Pojmenovává se v elementu <process-definition> jako hodnota atributu name.
Každé workflow má počáteční stav <start-state> a koncový stav <end-state>. Všechny ostatní stavy jsou mezi těmito dvěma. Když se dojde do koncové stavu, workflow se automaticky ukončí.


<process-definition name="ukazka">
<start-state>
<transition to='s' />
</start-state>
<state name='s'>
<transition to='end' />
</state>
<end-state name='end' />
</process-definition>

Příklad jednoduchého workflow. Je zde vidět počáteční stav start-state a koncový stav end-state.

Stavy workflow je potřeba pojmenovávat pomocí atributu name, protože když se na ně odkazujeme v přechodu <transition>, tak se odkazujeme právě na tento název. Tento název musí být jedinečný. Stejně důležité je i pojmenovávat některé přechody <transition>, protože právě na tyto názvy se pak odkazujeme při nějaké akci z formuláře.

Součástí uzlů <node> může být i vykonávání zdrojového kódu. Takto je možné ve chvíli, kdy se vykonávání přesune do našeho uzlu něco vykonat. Můžeme například dělat dotazy do databáze a další věci. Tento kód píšeme v jazyce Java.


<node name="init">
<description>
Zkontroluje, zda bylo vyplněno userName.
</description>
<event type="node-enter">
<script>
<expression>
import eu.bcvsolutions.idm.data.util.StringUtils;
import eu.bcvsolutions.idm.app.Application;

if (StringUtils.isEmpty(userName)) {
throw new IllegalArgumentException(„Parametr ‚userName‘
nemůže být null nebo prázdný“);
}
</expression>
<variable name=“userName“ access=“read“ />
</script>
</event>
<transition to=“retrieveData“></transition>
</node>
Uzel s názvem init má v sobě Java kód, který kontroluje, zda bylo vloženo uživatelské jméno.

V ukázce je vidět, jakým stylem se zapisuje do uzlu Java kód. Tento kód se provede při vstupu do uzlu a je obalen elementy <script> a <expression>. Při psaní kódu se nesmí zapomenout na správné vložení Java importů, protože jinak se kód neprovede a vykonávání workflow skončí s chybou.

Šablony a JSF

V CzechIdM jsou šablony, které se skládají z HTML a různých JSF a RichFaces komponent. Tyto šablony pak ve výsledku zobrazují nějaký formulář či výpis, jsou to vlastně jednotlivé stránky.

Výhodou šablon a JSF je, že pokud některou část používáme na více místech (například menu je použito na každé stránce), tak je vhodné si tento kus kódu uložit do zvláštní šablony a v ostatních šablonách ho jednoduše vložit. Nedochází tak ke zbytečné duplikaci kódu a pokud menu měníme, stačí ho změnit na jediném místě a změna se promítne na všech stránkách. Protože má skoro každý formulář v CzechIdM vlastní šablonu, je vhodné toto využít.


<ui:define name="submenu">
<ui:include src="submenu.xhtml" />
</ui:define>

Do šablony se vkládá jiná šablona submenu.xhtml.

Každá šablona má definovanou základní šablonu, do které se naše šablona vkládá. Toto využíváme k tomu, abychom určité šablony mohli přiřadit buď do administrátorského nebo uživatelského rozhraní. Podle toho, do jakého rozhraní šablona patří, tak od té také dědí.

Psaní formulářů pomocí JSF a workflow

V této části si ukážeme, jak jsou v CzechIdM psány formuláře s využitím JSF a workflow. Řekneme si, jak spustit nové workflow a také jak ho propojit s JSF šablonou.

K práci s workflow slouží v CzechIdM třída GenericManagedBean, která kromě jiného obsahuje metody pro práci s workflow. Tyto metody dokáží buď odstartovat nové workflow, spustit subworkflow či posunout se ve workflow k vykonávání dalšího stavu. Nejpoužívanější jsou metody processAction, která spusti akci jako subworkflow a processSubmit, která se ve workflow přesune do dalšího stavu.


public class GenericManagedBean implements Serializable {

public void processAction() {
...
}

public void processSubmit(String command) {
...
}
}

Ukázka části třídy GenericManagedBean.

Spouštění nového workflow

Spouštění nového workflow uděláme jednoduše ze šablony a to tak, že zavoláme metodu třídy GenericManagedBean, dále jí řekneme, jaké workflow má spustit a jaké případné parametry mu má předat.


<s:button action="#{genericBean.processAction}"
value="#{messages.admin_users_action_new_organisation}" styleClass="menu_button"
rendered="#{s:hasPermission('ORGANISATION', 'CREATE')}">
<f:param name="action" value="organisation.new" />
</s:button>

Tlačítko spouští nové workflow.

Kus kódu šablony zobrazuje tlačítko pro vytvoření nové organizace. Po kliknutí na tlačítko se spustí metoda processAction, která spustí nové workflow definované pomocí elementu <f:param> s atributem name=“action“ a value=“organisation.new“, která definuje název workflow, které se má spustit. Spustí se workflow pojmenované organisation.new.

Za povšimnutí stojí i atribut rendered, který definuje, za jaké podmínky se má tlačítko zobrazit. Zde se zobrazí, pokud má aktuálně přihlášený uživatel právo na vytváření organizací.


<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="organisation.new">
... stavy ve workflow
</process-definition>

Workflow, které chceme spustit musí být pojmenované a tímto názvem ho poté spouštíme.

V elementu <process-definition> se pojmenuje worklfow pomocí atributu name.

Propojení šablon s workflow

V určitou chvíli se stane, že potřebujeme přenášet data z workflow do šablony. Uděláme například ve workflow dotaz na databázi a vybereme všechny uživatele, které potom chceme v šabloně zobrazit v přehledné tabulce. K tomu, abychom to mohli udělat, potřebujeme naše data ve workflow nějak uložit a pak je přenést do šablony a tam zobrazit.

To uděláme tak, že naše data ve workflow uložíme do proměnné a namapujeme je na proměnnou v elementu <variable>. Tato proměnná se potom přenese do šablony.
Element <variable> má atribut name, což je jeho název, na který se pak odkazujeme v šabloně. Dále má atributy mapped-name a access.

Atribut mapped-name slouží k tomu, abychom řekli, že proměnná userName ve zdrojovém kódu workflow je proměnná s názvem (v atributu name) userName, na kterou se odvoláváme poté v šabloně. Hodí se to v případě, když chceme mít stejnou proměnnou pojmenovanou jinak ve zdrojovém kódu workflow a jinak v šabloně. Pokud jsou názvy stejné, tak se atribut mapped-name nemusí používat.

V atributu access se nastaví, jestli chceme do proměnné z našeho zdrojového kódu ukládat, v tomto případě tam dáme hodnotu write, protože pak je tato proměnná dostupná v parametrech stránky a můžeme ji tedy v šabloně vypisovat. Pokud z ní chceme načítat, tedy například nějakou hodnotu z formuláře chceme poslat do workflow, aby ji náš kód ve workflow mohl dál zpracovávat, tak použijeme hodnotu read. Možná je i kombinace read,write a to ve chvíli, kdy chceme proměnnou například načíst ze šablony a poté ji dále zpracovávat ve workflow.

Hodnota read funguje trochu jako filtr. Pokud nevytvoříme žádnou proměnnou <variable>, která má přístup s hodnotou read, máme v aktuálním uzlu dostupné všechny proměnné ke čtení. Pokud ale napíšeme alespoň jednu s proměnnou <variable> a právem read, tak všechny proměnné, které nejsou v uzlu zapsané ve <variable> nejsou dostupné.


<node name="retrieveData">
<description>
Získá userView uživatele.
</description>
<event type="node-enter">
<script>
<expression>
import eu.bcvsolutions.idm.data.Data;
import eu.bcvsolutions.idm.app.Application;
import eu.bcvsolutions.idm.data.view.View;
import eu.bcvsolutions.idm.data.util.Enums;

View userView = Data.checkoutView(View.Type.USER, userName);
</expression>
<variable name=“userName“ access=“read“ mapped-name=“userName“ />
<variable name=“userView“ access=“write“ mapped-name=“userView“ />
</script>
</event>
<transition to=“deleteIdentity“></transition>
</node>

Pomocí elementu <variable> můžeme ukládat data, která budeme zobrazovat v šabloně a nebo data z šablony číst.

V ukázkovém zdrojovém kódu je vidět, kdy načítáme ze šablony proměnnou userName, což je uživatelské jméno uživatele. Tato proměnná je uložená ve <variable> s přístupem read. Poté pomocí této proměnné dostaneme View userView uživatele, které si uložíme do proměnné userView, abychom userView mohli používat v šabloně a nebo v dalších uzlech workflow.

Přenášení parametrů do šablony

K zobrazení dat z workflow musí tato data být uložené v elementech <variable> a poté je můžeme v šabloně dostat z parametrů stránky.

Toto uděláme v šabloně pomocí hodnoty #{pageParameters.variables}, ve kterých jsou přístupné všechny uložené proměnné. Je vhodné si v šabloně #{pageParameters.variables} uložit do nějaké proměnné, aby byl celkový zápis kratší.

<c:set var="vars" value="#{pageParameters.variables}"/>
<h:outputText value="#{vars.userView['firstName']} #{vars.userView['lastName']},
#{vars.userView['email']}" />

Je vhodné si parametry stránky uložit do proměnné, lépe se s nimi pak pracuje.

Parametry stránky jsou v ukázce uloženy do proměnné pomocí komponenty <c:set>. Jako hodnota tlačítka <h:outputText> je zobrazeno křestní jméno, příjmení a email uživatele, které jsou vytáhnuty z parametrů stránky z proměnné userView.

Na stejném principu můžeme přenášet data z formulářů do workflow a to tak, že si úplně stejně do nějaké proměnné do parametrů stránky uložíme danou hodnotu a poté ji můžeme ve worfklow použít a dále s ní pracovat. Ve workflow by se měla nacházet v elementu <variable>, s nastavenými atributy access=“read“ nebo access=“read, write“, pokud ji chceme zároveň ve workflow měnit.

Spouštění určitého stavu v již běžícím workflow

Pokud máme již běžící workflow a chceme posunout naše workflow do dalšího stavu, tak to provedeme taktéž zavoláním metody z naší třídy GenericManagedBean. Tentokrát ale použijeme metodu processSubmit, které dáme do parametru jméno přechodu ve workflow, pomocí kterého se posuneme do dalšího stavu.

<h:commandButton action="#{genericBean.processSubmit('save')}" value="#{messages.cmn_btn_save}" />
Změna stavu ve workflow

Po kliknutí na tlačítko dojde ke spuštění přechodu <transition> s názvem save a ten spustí uzel, který se postará o uložení. Zde je vidět, proč je důležité přechody také pojmenovávat.

Internacionalizace

CzechIdM je aktuálně v češtině, ale je připraveno na internacionalizaci.

K tomu, abychom toho docílili je potřeba, aby nebyl text v šablonách psán přímo, ale přes objekt messages. To se dělá tak, že se jako text použije #{messages.nazev_textoveho_retezce}. Textové řetězce jsou vypsány v souborech .properties, které v sobě obsahují dané texty, na které odkazujeme pomocí jejich názvu v šabloně. Pro každý jazyk je zvláštní .properties soubor.


<h:commandButton action="#{genericBean.processSubmit('save')}"
value="#{messages.cmn_btn_save}" />

<h:commandButton action=“#{genericBean.processSubmit(‚reset‘)}“
value=“#{messages.admin_cmn_reset_view}“ />

<h:commandButton action=“#{genericBean.processSubmit(‚close‘)}“
value=“#{messages.cmn_btn_close}“ />

Do tlačítka nepíšeme text rovnou, ale jako message pomocí #{messages.nazev_retezce}.

Ukázka .properties souboru v módu Properties.

Protože je potřeba řetězce ukládat do .properties souboru zakódované, IDE Eclipse s přidáváním řetězců pomáhá. Soubor je možný prohlédnout v Eclipse v módu Source (zdrojový kód) nebo Properties. Můžeme přidávat další řetězce, editovat a mazat.

Závěr

V tomto článku jste se seznámili s tím, jaké technologie jsou použité pro prezentační vrstvu v CzechIdM. Pokud byste se nás chtěli zeptat na něco ohledně tohoto článku nebo našeho Identity Manageru CzechIdM, neváhejte nám napsat na info@bcvsolutions.eu!