domingo, 15 de abril de 2012

Document library custom actions

La version 4.0 del gestor documental Alfresco incluye un nuevo mecanismo para personalizar y extender las acciones de la biblioteca de documentos del cliente Share. Este mecanismo pretende ser una simplificación del modelo anterior y permite tener más control sobre como se presentan las acciones.

La configuración por defecto de las acciones se encuentra en el fichero share-documentlibrary-config.xml. Este cambio permite que las acciones se pueden personalizar o extender en el fichero share-config-custom.xml.

Para ver como funciona este mecanismo voy a realizar un ejemplo aprovechando parte de lo que presenté en este post. La idea es crear una acción que cambie el store en el que está almacenado un documento.

Web client tier 

El primer paso es definir la acción en el fichero share-config-custom.xml. La acción se debe definir dentro del elemento de configuración llamado DocLibActions.

      <actions>
        <action id="set-content-store-selector" type="javascript" label="actions.set-content-store-selector">
            <param name="function">onActionChangeStore</param> 
            <permissions>
               <permission allow="true">Write</permission>
            </permissions>
            <evaluator negate="true">evaluator.doclib.action.isLocked</evaluator>
        </action>     
      </actions>

De esta forma hemos definido una acción llamada set-content-store-selector, de tipo javascript, que llamará a la función de javascript onActionChangeStore, que sólo estará disponible para los usuarios con permiso de escritura sobre el contenido y que aparecerá cuando el documento no esté bloqueado por otro usuario.

El icono para la acción se ha de llamar set-content-store-selector-16.png y ha deir ubicado en la carpeta \components\documentlibrary\actions.

A continuación hay que indicar en qué grupo de acciones se ha de mostrar la nueva acción. Con el nuevo mecanismo, las acciones se han agrupado según el lugar donde se han de mostrar y no según el estado del nodo como sucedía en las versiones previas de Alfresco. Esta definición va en el mismo bloque de configuración que la definición de las acciones. En el ejemplo, queremos que aparezca en el conjunto de acciones del detalle de un documento y en las acciones de navegación de los documentos:

      <actionGroups>
         <actionGroup id="document-browse">

            <action index="340" id="set-content-store-selector" />
         </actionGroup>
         <actionGroup id="document-details">
            <action index="360" id="set-content-store-selector" />
         </actionGroup>       
      </actionGroups>

Con esta configuración ya se puede comprobar como quedará la acción en el menú:



El siguiente paso es escribir la función javascript a la que se invocará desde el botón. Hay varias opciones para ello ya que en el fichero donde se definen las opciones por defecto existen algunos métodos genéricos que se pueden reutilizar. El fichero por defecto es documentlibrary-actions.js. En el ejemplo vamos a escribir una función nueva:

      onActionChangeStore: function dlA_onActionChangeStore(record)
      {
         var displayName = record.displayName;

         this.modules.actions.genericAction(
         {
            success:
            {
               event:
               {
                  name: "metadataRefresh"
               },
               message: this.msg("message.set-content-store-selector.success", displayName)
            },
            failure:
            {
               message: this.msg("message.set-content-store-selector.failure", displayName)
            },
            webscript:
            {
               method: Alfresco.util.Ajax.POST,
               name: "set-content-store-selector/node/{nodeRef}",
               params:
               {
                  nodeRef: record.jsNode.nodeRef.uri
               }
            }
         });
      },

Esta función hace una llamada AJAX por método POST a un webscript de Alfresco pasándole como parámetro la referencia al nodo sobre el que se está ejecutando la acción. Este webscript ha de ser  del package slingshot/doclib/action. Como resultado de la llamada, mostrará un mensaje de éxito o de fracaso.

Server tier

En la parte del servidor hemos de crear el web script invocado desde el método javascript. La construcción de este webscript es estándar pero aprovecharé el mecanismo de ejecución de acciones que proporciona Alfresco para simplificar la tarea.

En primer lugar definiremos el webscript:

<webscript>
  <shortname>set-content-store-selector</shortname>
  <description>Document List Action - Changes the store</description>
  <url>/slingshot/doclib/action/set-content-store-selector/node/{store_type}/{store_id}/{id}</url>
  <format default="json">argument</format>
  <authentication>user</authentication>
  <transaction>required</transaction>
  <lifecycle>internal</lifecycle>
</webscript>

Lo único a destacar es que el formato de respuesta por defecto será JSON y que el nivel de autenticación será user puesto que la acción implica un cambio en los metadatos del objeto afectado.

Para el controlador del webscript utilizaré la librería de accciones action.lib.js, que proporciona métodos para recoger los parámetros y formatear la respuesta:

<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js">
/**
 * Entrypoint required by action.lib.js
 */
function runAction(p_params)
{
   var results;

   try
   {
      var aspectAdded = p_params.destNode.addAspect("cm:storeSelector");
      p_params.destNode.properties["cm:storeName"] = "storeA";
      p_params.destNode.save();
      if (!aspectAdded)
      {
         status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not change store " + url.extension);
         return;
      }

      var resultId = p_params.destNode.name,
         resultNodeRef = p_params.destNode.nodeRef.toString();

      // Construct the result object
      results = [
      {
         id: resultId,
         nodeRef: resultNodeRef,
         action: "changeStore",
         success: true
      }];
   }
   catch(e)
   {
      e.code = status.STATUS_INTERNAL_SERVER_ERROR;
      e.message = e.toString();     
      throw e;
   }

   return results;
}

main();

Por último tenemos que crear la plantilla de visualización para el formato JSON. En este caso también utilizaremos la librería de plantillas action.lib.ftl. La plantilla sería la siguiente:

<#import "action.lib.ftl" as actionLib />
<@actionLib.resultsJSON results=results />

Una vez grabados los ficheros, hay que copiarlos a la carpeta de extenxión de Alfresco, por ejemplo en /alfresco/extension/templates/webscripts/org/alfresco/components/documentlibrary/actions. Ac ontinuación refrescaremos la lista de webscripts de Alfresco y la acción ya estará disponible para probar.

Para más detalles, se puede consultar la documentación de Alfresco.