viernes, 22 de noviembre de 2013

Personalización de Alfresco Share IV - cómo añadir componentes

En nuestro webinar de personalización de Alfresco Share mostramos cómo eliminar e incluir nuevos custom actions, templates y componentes.

En esta entrada explicaremos como añadir componentes a una página de Alfresco Share.

¿Qué es una página?
Cuando hablamos de páginas en Share puede parecer obvio que nos referimos a un archivo HTML presentado al usuario a través de un navegador. No obstante, para incluir nuestro componente debemos ir más allá de eso y entender la forma particular en la que Share crea esas páginas a través del framework Surf.

Una página en Share es el resultado de un conjunto de tres archivos:
  1. La definición de la página
  2. Una plantilla (template) donde se definen las regiones que tendrá la página
  3. Una instancia de la plantilla (template-instance) donde se indica qué componente va en cada region

¿Qué es un componente?
Por lo general un componente está formado por un conjunto de subcomponentes que en última instancia están representandos por un webscript de presentación y un evaluador (opcional) que determinará si se muestra el componente en la página.

Los componentes son los que en suma forman cada página de Alfresco Share. Cada componente se situa en una región concreta, indicado en el template-instance.

Objetivo: añadir un componente a la página "document-details"

Nuestro objetivo es añadir una galería que contenga todas las imágenes que se vayan creando a través de la selección de los custom actions para imágenes que hemos creado previamente.

  • En Alfresco
    • Crearemos un webscript de datos
    • Registraremos el webscript
  • En Share
    • Crearemos un webscript de presentación
    • Copiaremos la página original para modificarla
    • Incluiremos una nueva región en el template
    • Incluiremos nuestro componente en el template instance dentro la nueva región
Alfresco
Crear webscript de datos
Del lado del repositorio de Alfresco crearemos un webscript que provea la lista de imágnenes que deben mostrarse en la galería de nuestro componente:

Definción: 
<webscript>
    <shortname>GetAssociatedImages</shortname>
    <description>Get associated images to another image</description>
    <url>/imageTool/associated-images?noderef={noderef}</url>
    <format default="json">argument</format>
    <authentication>user</authentication>
</webscript>

Controlador:
public class GetAssociatedImagesWebScript extends AbstractWebScript {

    protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache) {

        Map<String, Object> model = new HashMap<String, Object>();
        String ref = req.getParameter("noderef");
        NodeRef nodeRef = new NodeRef(ref);
        validateRequiredParams("NodeRef is required", ref);

        List imageList = imageService.getAssociatedImages(nodeRef);
        String jsonResp = JacksonUtil.toJSON(imageList, false, true);

        if (jsonResp != null) {
            model.put("jsonResp", jsonResp);
        }

        return model;
    }
}


JSON (freemarker):
<#if jsonResp??>${jsonResp}<#else>[ ]</#if>

Registrar webscript de datos
Es necesario registrar el webscript en el contexto de Spring para que Alfresco tenga constancia de ello:

<beans>
    <bean id="abstractWebScript"
          class="co.losi.imageTool.webscript.AbstractWebScript"
          abstract="true"
          parent="webscript">
        <property name="serviceRegistry" ref="ServiceRegistry"/>
        <property name="imageService" ref="imageToolService"/>
    </bean>
    <bean id="webscript.module.imageTool.associated-images.get"
          class="co.losi.imageTool.webscript.GetAssociatedImagesWebScript"
          parent="abstractWebScript">
    </bean>
</beans> 

Share
Crear webscript de presentaciónDel lado de Share crearemos un webscript de presentación que con la información provista por el webscript de Alfresco mostrará la galería de imágenes asociadas a la imagen que se esté visualizando:

Definción: 
<webscript>
    <shortname>image-gallery</shortname>
    <description>Display an image gallery</description>
    <url>/imageTool/components/image-gallery</url>
</webscript>

Controlador:
<import resource="classpath:/alfresco/templates/org/alfresco/import/alfresco-util.js"/>

function main() {
    var nodeRef = AlfrescoUtil.param('nodeRef');
    var url = "/imageTool/associated-images?noderef=" + encodeURIComponent(nodeRef);
    var result = remote.connect("alfresco").get(url);

    if (result.status == 200) {
        var jsonObject = eval('(' + result + ')');
        model.imageList = jsonObject;

    } else {
        model.imageList = null;
    }
}

main();

HTML (freemarker):
<div id="imageToolGalleryDiv" class="document-details-panel folder-details-panel imageTool-gallery" xmlns="http://www.w3.org/1999/html">
    <h2 id="rendition-list-heading" class="thin dark">${msg("header")}</h2>
    <br/>
    <#if imageList??>
        <#list imageList as oneImage>
            <div style="float:left; padding-right:5px">
                <img src="/share/proxy/alfresco/api/node/workspace/SpacesStore/${oneImage.nodeRef.id}/content/thumbnails/imageGallery">
            </div>

        </#list>
    <#else>
        ${msg("no.images")}
    </#if>
</div>

Copiar página original
En anteriores secciones pudimos observar que añadir y eliminar custom actions era posible mediante un archivo de configuración. En este caso, para añadir un componente a una página existente en Alfresco debemos sobreescribir dicha página con nuestra vérsión modificada. Como en la mayoría de los casos, es conveniente copiar lo que ya existe en Alfresco y reutilizarlo, por tanto, haremos una copia de los tres archivos relacionados con la página "document-details" que encontraremos en share.war:
  • Template
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/templates/org/alfresco
  • Template Instance
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/site-data/template-instances
  • Page
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/site-data/pages

Incluir nueva región en el template
Queremos que la galería se muestre justo debajo de la imagen visualizada, por tanto crearemos una nueva región a continuación de la región "web-preview" a la que identificaremos como "image-gallery":

<#include "include/alfresco-template.ftl" />
<@templateHeader>
   <@script type="text/javascript" src="${url.context}/res/modules/documentlibrary/doclib-actions.js"></@script>
   <@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/document-details/document-details-panel.css" />
   <@templateHtmlEditorAssets />
</@>

<@templateBody>
   <div id="alf-hd">
      <@region id="header" scope="global"/>
      <@region id="title" scope="template"/>
      <@region id="navigation" scope="template"/>
   </div>
   <div id="bd">
      <@region id="actions-common" scope="template"/>
      <@region id="actions" scope="template"/>
      <@region id="node-header" scope="template"/>
      <div class="yui-gc">
         <div class="yui-u first">
            <#if (config.scoped['DocumentDetails']['document-details'].getChildValue('display-web-preview') == "true")>
               <@region id="web-preview" scope="template"/>
            </#if>
            <@region id="image-gallery" scope="template"/>
            <@region id="comments" scope="template"/>
         </div>
         <div class="yui-u">
            <@region id="document-actions" scope="template"/>
            <@region id="document-tags" scope="template"/>
            <@region id="document-links" scope="template"/>
            <@region id="document-metadata" scope="template"/>
            <@region id="document-sync" scope="template"/>
            <@region id="document-permissions" scope="template"/>
            <@region id="document-workflows" scope="template"/>
            <@region id="document-versions" scope="template"/>
            <@region id="document-publishing" scope="template"/>
            <#if imapServerEnabled>
               <@region id="document-attachments" scope="template"/>
            </#if>
         </div>
      </div>

      <@region id="html-upload" scope="template"/>
      <@region id="flash-upload" scope="template"/>
      <@region id="file-upload" scope="template"/>
   </div>
   <@region id="doclib-custom" scope="template"/>
</@>

<@templateFooter>
   <div id="alf-ft">
      <@region id="footer" scope="global"/>
   </div>
</@>
Incluir componente en el template instance
Recordemos que un componente es un última instancia un webscript. En nuestro componente vamos a llamar al webscript de presentación que creamos y lo incluiremos en dentro de la región "image-gallery":

<component>
   <region-id>image-gallery</region-id>
   <sub-components>
       <sub-component id="default">
           <evaluations>
               <evaluation>
                   <evaluators>
                       <evaluator type="imageTool.evaluator.imageType">
                           <params>
                               <nodeRef>{nodeRef}</nodeRef>
                           </params>
                       </evaluator>
                   </evaluators>
                   <url>/imageTool/components/image-gallery</url>
                   <properties>
                       <nodeRef>{nodeRef}</nodeRef>
                   </properties>
               </evaluation>
           </evaluations>
       </sub-component>
   </sub-components>
</component>

Para mostrar el nuevo compomente sólo mientras se visualizan archivos tipo imagen, crearemos en Share un evaluador "imageTool.evaluator.imageType" que determine si el mimetype del nodo visualizado corresponde a jpeg, gif o png.

Interacción entre Share y Alfresco
Para entender correctamente la interacción que existe entre Share y Alfresco, vamos a seguir la secuencia de eventos que se produce desde que la página "document-details" es solicitida hasta que se muestra en pantalla:



  1. El usuario accede desde Share a la página para visualizar una imagen. Share determina el template correspondiente y la instancia para construir la página. Componente a componente se van realizando las llamadas a los distintos webscripts de presentación.
  2. Cuando Share llega a nuestro componente de galería de imágenes, se ejecuta el webscript de presentación que realiza una llamada al webscript de datos situado en Alfresco.
  3. Alfresco ejecuta el webscript de datos y genera un json con la lista de imágenes que se deben mostrar en la galería.
  4. Alfresco responde a Share enviando el objeto json.
  5. El webscript de presentación en Share recoge los datos propocionados por Alfresco y genera el html que finalmente se mostrará como galería de imágenes
Cómo integrar nuestra personalización en Alfresco Share
A continuación se especifíca dónde debemos colocar nuestras páginas personalizadas y componentes:

Páginas personalizadas:
  • Templates
    • <tomcat>/shared/classes/alfresco/web-extension/templates
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/templates
  • Template Instances
    • <tomcat>/shared/classes/alfresco/web-extension/site-data/template-instances
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/site-data/template-instances
  • Pages
    • <tomcat>/shared/classes/alfresco/web-extension/site-data/pages
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/site-data/pages
Componentes:
  • Share
    • <tomcat>/shared/classes/alfresco/web-extension/site-webscripts/com/midominio/components
    • <tomcat>/webapps/share/WEB-INF/classes/alfresco/site-webscripts/com/midominio/components  
  • Alfresco (webscripts de datos)
    • <tomcat>/shared/classes/alfresco/web-extension/templates/webscripts/com/midominio
    • <tomcat>/webapps/alfresco/WEB-INF/classes/alfresco/templates/webscripts/com/midominio

Nunca hagas cambios directamente sobre los archivos desplegados en el servidor web. Recuerda que siempre puedes crear un AMP con tus archivos e instalar así tus cambios.

 

jueves, 21 de noviembre de 2013

Personalización de Alfresco Share III - cómo añadir custom actions

En nuestro webinar de personalización de Alfresco Share mostramos cómo eliminar e incluir nuevos custom actions, templates y componentes.

En esta entrada explicaremos de forma sencilla cómo añadir custom actions, en la lista de opciones que los usuarios tienen disponible a través del visualizador de documentos de Alfresco Share.

¿Qué es un custom action?
Antes de continuar con la lectura es importante enteder qué son los custom actions y cómo eliminarlos. Si aún no has leído sobre esto te recomendamos echarle un vistazo a la sección previa.

Objetivo: añadir nuevos custom actions
Añadir los custom actions "Escala de grises",  "Invertir colores", "Ajustar contraste", "Múltiples efectos" al menú de opciones, tal y como se muestra en a continuación:



Estos custom actions realizarán operaciones sobre contenido de tipo imagen (jpeg, gif, png) dando la facilidad al usuario de crear copias de las imágenes y aplicarles una serie de efectos.

Para entender con claridad como llevar a cabo nuestro objetivo, analizaremos paso a paso los elementos necesarios para poner en funcionamiento uno de nuestros custom actions (Escala de grises) tanto en el lado de Alfresco como de Share:
  • En Alfresco
    • Crearemos un action
    • Registraremos el action creado
  • En Share
    • Crearemos un custom action
    • Incluiremos el custom action dentro de un actionGroup

Alfresco
Crear action
Del lado de Alfresco crearemos un action que en última instancia realizará las operaciones sobre el nodo de la imagen que el usuario estará visualizando. En nuestro caso lo hemos programado mediante Java (también es posible hacerlo mediante Javascript):

public class SetBlackWhiteColorActionExecuter extends GenericAbstractActionExecuter {
    public static final String NAME = "image-tool-blackWhite";
    public static final String IMAGE_TYPE = "BW";

    @Override
    protected void executeImpl(Action action, NodeRef imageNodeRef) {
        ImageProperties imageProperties = new ImageProperties();
        imageProperties.addBlackWhiteColor(true).setSourceImage(imageNodeRef)

        .setImageType(IMAGE_TYPE);
        Image newImage = imageService.createImage(imageNodeRef, imageProperties, true);
        imageService.createThumbnail(newImage.getNodeRef(), 190, 190, "imageGallery");
    }

    @Override
    protected void addParameterDefinitions(List<ParameterDefinition> paramList) {
    }
}


Registrar action
Para que Alfresco tenga constancia del nuevo action y lo registre dentro del servicio de actions, es imprescindible incluirlo en el contexto de Spring:

<bean id="image-tool-blackWhite"
  class="co.losi.imageTool.action.SetBlackWhiteColorActionExecuter"
  parent="image-tool-executer">
</bean>    

El identificador utilizado para este action, "image-tool-blackWhite", es el mismo que utilizaremos más adelante para realizar la llamada desde Share.

Share
Crear custom action 
En nuestro archivo de configuración de Share (share-config-custom.xml) incluiremos nuestro custom action "Escala de grises". Utilizaremos el id del action creado en Alfresco para atar ambos:

<config evaluator="string-compare" condition="DocLibActions">
  <actions>
    <action id="image-tool-blackWhite" type="javascript"
       label="actions.image.tool.blackWhite" icon="imageTool-add-image">
       <param name="function">onActionSimpleRepoAction</param>
       <param name="action">image-tool-blackWhite</param>
       <param name="success">refreshImageGallery</param>
       <param name="successMessage">message.image.blackWhite.success</param>
       <param name="failureMessage">message.image.blackWhite.failure</param>
       <permissions>
           <permission allow="true">Write</permission>
       </permissions>
       <evaluator>imageTool.evaluator.mimeType</evaluator>
    </action>
  </actions>
</config> 


Incluir custom action dentro de un actionGroup
Nuestro custom action puede mostrarse en diferentes contextos dentro de Share. De forma partircular, en nuestro caso, queremos mostrarlo en el menú de opciones del visualizar de documentos. Para ello incluiremos el id del nuevo custom action dentro del actionGroup "document-details" dentro del archivo de configuración share-config-custom.xml:

<config evaluator="string-compare" condition="DocLibActions">
  ...
 
   <actionGroups>
      <actionGroup id="document-details">
          <action index="500" id="image-tool-blackWhite"/>
      </actionGroup>
   </actionGroups>
</config> 

Interacción entre Share y Alfresco
Para entender correctamente la interacción que se produce entre Share y Alfresco, vamos a seguir la secuencia de eventos que se producen desde que el usuario selecciona el custom action "Escala de grises".


  1. El usuario se encuentra en el visualizando un imagen en Share, en la página "document-details" y selecciona la opción "Escala de grises".
  2. La aplicación Share envía una petición mediante AJAX a la aplicación Alfresco, pasándole la referencia del nodo y el identificador de la acción que se ejecutará sobre el mismo.
  3. Alfresco ejecuta la acción indicada sobre el nodo indicado, creando una copia de la imagen en blanco y negro.
  4. Alfresco responde a Share con un mensaje de éxito o error.

Cómo integrar nuestra personalización en Alfresco Share
Para incluir nuestros cambios en Share debemos colocar los nuevos archivos en el directorio de extensión de Share, <tomcat>/shared/classes/alfresco/web-extension. Los archivos relacionados con Alfresco debemos colocarlos en el directorio <tomcat>/shared/classes/alfresco/extension.

Recuerda que siempre puedes crear un AMP con tus archivos e instalar así tus cambios directamente en alfresco.war y share.war respectivamente.

Próximo paso
En nuestra próxima entrada explicaremos cómo añadir un nuevo componente a una página de Alfresco Share.

 

miércoles, 6 de noviembre de 2013

Teoría sobre la seguridad en Alfresco (Permiso, Authority, ACE, ACL)

 La seguridad es uno de los aspectos críticos en cualquier sistema de gestión documental, incluido Alfresco. Es importante determinar de qué manera se manejará y se expondrá el contenido a los usuarios. Para ello, debemos enteder claramente los elementos principales que establecerán la seguridad en nuestra instalación de Alfresco.

Nodo
Desde el punto de vista del repositorio, un nodo representa la unidad básica para almacenar información, proporcionar estructura al contenido y establecer asociaciones y dependencias en el repositorio. La seguridad partirá de está unidad básica y será propagada a todo Alfresco.

Permiso
Un permiso es la unidad básica de seguridad en Alfresco. Cada acción realizada sobre un nodo estará autorizada por uno o varios permisos que el usuario deberá tener asignados, a través de las autoridades, para ese nodo en concreto.

Autoridad (Authority)
Una autoridad representa un conjunto de permisos para uno nodo concreto. Éstas pueden ser de tipo persona, grupo o rol. Cuando un usuario se autentica en el sistema, Alfresco valida las credenciales del usuario y determina las autoridades que tiene asignadas. Mediante las autoridades el sistema puede determinar, para cada nodo, los permisos de los que el usuario dispone.

ACE
Un ACE (Access Control Entry, por sus siglas en inglés) contiene información sobre un  permiso determinado y cómo éste se relaciona con una autoridad (authority) para un nodo concreto. Es decir, para un nodo A y el permiso LECTURA, el ACE relacionado con la autoridad GRUPO1 indicará si este GRUPO1 tiene o no permiso de LECTURA sobre el nodo A.

ACL
Un ACL (Access Control List, por sus siglas en inglés) es una lista de ACEs para un nodo concreto. Toda acción realizada por un usuario sobre un nodo del repositorio de contenido será validada antes de llevarse a cabo. Para ello, el componente de seguridad verificará si la acción está autorizada por alguno de los permisos, extraídos de la lista ACL que tendría el nodo para ese usuario concreto (otorgados a través de las autoridades asignadas a dicho usuario)

La seguridad en Alfresco está atada a cada elemento almacenado en el repositorio, representados a través de los nodos y está granulada en función de los permisos definidos en la configuración del sistema. La seguridad definida se extiende a todo el sistema, es decir, que toda acción sobre un nodo, que se lleve a cabo desde cualquier sección del sistema, pasará obligatoriamente por el componente de seguridad que determinará si el usuario que realiza la acción está autorizado para ello, verificando el ACL del nodo sobre el que se realizaría la acción y determinando si posee los permisos adecuados que la autorizan.