Login

Para implementar el login, creamos una pagina de nombre Login que usara el componente kayestry:Login. La definicion de la pagina tendria:

    <component id="login" type="kayestry:Login">
        <binding name="delegate" expression="beans.delegate"/>
    </component>

Para realizar el logout, se usa la pagina kayestry:Logout.

Control de Acceso

El control de acceso se realiza solo sobre las paginas de la aplicacion. Este metodo tiene la desventaja de no poder controlar el acceso sobre el resto de los recursos de la aplicacion que pueden accederse por un url, como imagenes, css, js, etc.

Para usar este control de acceso, las paginas deben implementar alguna interfaz que extienda SecurePage (tambien deben heredar de KayaBasePage, pero todas las paginas de la aplicacion ya deberian hacerlo!). Para mas informacion sobre diferentes escenarios del control de acceso, leer KayaBasePage.validate(PageEvent) y PageAccess.

Dependendiendo de la interfaz que implemente la pagina, es el metodo de autenticacion a ser usado. Ahora vamos seguir un ejemplo de agregar el control de acceso a una pagina usando BeanSecurePage.

BeanSecurePage toma los roles permitidos desde un bean definido en la definicion de la pagina. Agregamos en el .page el bean:

    <bean name="allowedRoles" class="sf.net.kayestry.pages.access.AccessBean">
        <set-property name="roles" expression="{'admin'}"/>
    </bean>

Luego hacemos que la pagina implemente la interfaz (en este caso no hay que implementar ningun metodo) y eso es todo.

Redirecciones

Hay dos tipos de redirecciones posibles, uno es enviando al usuario una respuesta http, lo que hace que se produzca un nuevo pedido y cambie el url. Y otro es una redireccion interna, haciendo que se invoque otra pagina pero manteniendo el mismo url. Por ejemplo, en el caso de usar la respuesta http para redireccionar, realizo un pedido a htttp://ejemplo.org/Page1 y el servidor me devuelve un nuevo url, http://ejemplo.org/Page2. El browser tiene que realizar otro pedido al nuevo url. En el otro caso, hago un pedido a http://ejemplo.org/Page1, la clase de Page1 redirecciona internamente a Page2 y me devuelve el contenido de esta ultima, como si fuera el contenido del primer url.

En Tapestry, para la primera redireccion (usando la redireccion http) se usa una RedirectException. Se puede usar el wrapper que hay en TapestryUtils para simplificar esto.

Para realizar la segunda redireccion solo hace falta usar una PageRedirectException.

Como hacer abms (CRUD)?

Para agregar las paginas de listados y abm de una entidad, si seguimos con las convenciones especificadas en Nombres de las entidades, debemos seguir los pasos siguientes:

Suponiendo que la entidad para la que queremos crear el crud es X y el paquete de la aplicacion es ar.com.ejemplo

  • Crear los metodos para eliminar y crear/editar solo en la interfaz del business facade. Ver cuales son los metodos en de las entidades. FIXME: esto habria que pasarlo a otro lado, el businessfacade en algun momento se explotara en varios facade.
  • Crear una clase que herede de AbstractCRUD. Y sobreescribimos tambien el metodo initialize(). Para la entidad de ejemplo que estamos usando la clase creada seria:
    package ar.com.ejemplo.pages;
    
    import ar.com.ejemplo.model.X;
    ...
    
    public class XCRUD extends AbstractCRUD<X> {
    ...
        public void initialize() {
            setModelObject(new X());
            super.initialize();
        }
    }
  • Creamos una pagina para el listado. La creamos en WEB-INF/pages/crud/XBrowse.page:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE page-specification PUBLIC
      "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
      "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
    <page-specification class="ar.com.ejemplo.pages.XCRUD">
        <description>
            Listado de X
        </description>
        <bean name="delegate" class="org.apache.tapestry.valid.ValidationDelegate"/>
        <component id="border" type="Border">
            <binding name="delegate" expression="beans.delegate"/>
            <static-binding name="pageTitle" value="Listado de X"/>
        </component>
        <component id="form" type="Form">
            <binding name="listener" expression="listeners.removeObjects"/>
        </component>
        <component id="botones" type="kayestry:BrowseButtons">
            <static-binding name="CRUDPage" value="XCRUD"/>
        </component>
        <component id="checkbox" type="Checkbox">
            <binding name="selected" expression="modelObjectSelected"/>
        </component>
        <component id="editLink" type="ActionLink">
            <binding name="listener" expression="listeners.editObject"/>
        </component>
        <bean name="evenOdd" class="org.apache.tapestry.bean.EvenOdd"/>
        <component id="table" type="contrib:Table">
            <binding name="source" expression="dataPage"/>
            <static-binding name="columns"
                value="!checkbox, !editLink, COLUMNAS"/>
            <binding name="rowsClass" expression="beans.evenOdd.next"/>
            <binding name="pageSize" expression="25"/>
            <binding name="row" expression="currentModelObject"/>
        </component>
        <context-asset name="editAsset" path="images/page_edit.png"/>
        <component id="imgEdit" type="Image">
            <binding name="image" expression="assets.editAsset"/>
        </component>
    </page-specification>

Y el .html seria WEB-INF/pages/crud/XBrowse.html:

<html>
<head></head>

<body jwcid="$content$">

    <span jwcid="border">
        <div id="contenu">
        <form jwcid="form">
        <span jwcid="botones"/>
        <table class="browsetable" jwcid="table">
            <span jwcid="checkboxColumnHeader@Block"></span>
            <span jwcid="checkboxColumnValue@Block"><span jwcid="checkbox"/></span>
            <span jwcid="editLinkColumnHeader@Block"></span>
            <span jwcid="editLinkColumnValue@Block"><a jwcid="editLink"><span jwcid="imgEdit"> [editar]</span></a></span>
        </table>
        </form>
        </div>
    </span>

</body>
</html>

Reemplazar X por la clase de la entidad y COLUMNAS por los nombres de las columnas a mostrar en la tabla.

  • Creamos la pagina para la edicion, en WEB-INF/pages/crud/XCRUD.page:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE page-specification PUBLIC
      "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
      "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
    <page-specification class="sf.net.kayestry.example.pages.XCRUD">
        <bean name="delegate" class="org.apache.tapestry.valid.ValidationDelegate"/>
        <component id="border" type="Border">
            <binding name="delegate" expression="beans.delegate"/>
            <static-binding name="pageTitle" value="X"/>
            <binding name="withMenu" expression="true"/>
        </component>
        <component id="CRUDForm" type="Form">
            <binding name="listener" expression="listeners.saveObject"/>
            <binding name="delegate" expression="beans.delegate"/>
        </component>
        <component id="id" type="Hidden">
            <binding name="value" expression="modelObject.id"/>
        </component>
        <component id="description" type="ValidField">
            <binding name="value" expression="modelObject.description"/>
            <static-binding name="class" value="input-text"/>
            <binding name="validator" expression="beans.descValidator"/>
            <static-binding name="displayName" value="Descripcion"/>
        </component>
        
        <!-- ********* Botones para guardar / cancelar ***** -->
        <component id="CrudMenu" type="kayestry:CRUDButtons">
            <static-binding name="returningPage" value="XBrowse"/>
        </component>
        <!-- ********* VALIDATORS ***** -->
        <bean name="descValidator" class="org.apache.tapestry.valid.StringValidator">
            <set-property name="clientScriptingEnabled" expression="true"/>
            <set-property name="required" expression="true"/>
            <set-property name="minimumLength" expression="4"/>
        </bean>
    </page-specification>

Y el .html en WEB-INF/pages/crud/XCRUD.html:

<html>
<head></head>

<body jwcid="$content$">

    <span jwcid="border">
        <div id="contenu">
        <form jwcid="form">
        <span jwcid="botones"/>
        <table class="browsetable" jwcid="table">
            <span jwcid="checkboxColumnHeader@Block"></span>
            <span jwcid="checkboxColumnValue@Block"><span jwcid="checkbox"/></span>
            <span jwcid="editLinkColumnHeader@Block"></span>
            <span jwcid="editLinkColumnValue@Block"><a jwcid="editLink"><span jwcid="imgEdit"> [editar]</span></a></span>
        </table>
        </form>
        </div>
    </span>

</body>
</html>

En el codigo de arriba se crea una pagina donde solo hay un campo con nombre description. Modificarlo de acuerdo a los atributos de la entidad a editar.

  • Creamos nombres para acceder a las paginas recien creadas. Editamos app.application y agregamos:
        <page name="XBrowse" specification-path="pages/crud/XBrowse.page"/>
        <page name="XCRUD" specification-path="pages/crud/XCRUD.page"/>

Agregando un combo

Una forma mas sencilla de utilizar el componente de Tapestry PropertySelection es utilizar Combo. El componente acepta una lista de elementos o una enumeracion y a partir de eso crea el combo html.

Por ejemplo, queremos crear un combo con una lista de sistemas operativos. En la clase de la pagina tenemos un metodo que devuelve la lista:

    public List<OS> getOsList() {
        // obtenemos la lista de algun lado.
    }

En la pagina agregamos el componente del combo:

    <component id="select" type="kayestry:Combo">
        <binding name="model" expression="osList"/>
        <binding name="value" expression="selected"/>
    </component>

El parametro value es donde setear la opcion que elegio el usuario. Tambien podriamos llamar a un metodo de cierto facade directamente con:

  <binding name="model" expression="@ar.com.ejemplo.Application@getService().osFacade.osList"/>

Y en el html lo usamos:

    <select jwcid="select"></select>

Para ver que id y etiqueta se usa para cada opcion leer la documentacion de Combo.

Reportes

En el paquete sf.net.kayestry.reports hay varias clases para manejar reportes, asi como tambien un service de Tapestry para hacer download de los reportes generados. Las clases mencionadas antes son un wrapper a JasperReports.

La forma de usar reportes seria la siguiente:

  • Crear los reportes. Por ejemplo, usando iReports.
  • Compilarlos.
  • Definir los reportes del sistema. Se hace en un properties, leer ReportSpecManager para ver el formato.
  • Generar el reporte. Se puede usar ReportsFacade o el service de Tapestry, que termina usando ''ReportsFacade'' y permite hacer download del reporte. Para usar el servicio en la aplicacion, se debe agregar al archivo ''.application'':
        <service name="reports" class="sf.net.kayestry.reports.ReportService"/>

    Luego podemos acceder al servicio como cualquier servicio de Tapestry, por ejemplo usando una redireccion dentro de una pagina o usando un ServiceLink. Los parametros que hay que enviar estan definidos en la documentacion de ReportService.

    TODO: falta documentar ->crear facade para reportes TODO: falta documentar ->agregarlo al service

Agregando un menu

Se puede usar el componente kayestry:Menu para agregar un menu a la aplicacion. Este componente genera un menu con javascript y a cada item de menu se le pueden agregar controles de acceso por roles. Para saber como configurarlo, leer la documentacion de PageMenu.