Custom Action con Nintex y SharePoint 2013

En esta entrada vamos a ver cómo crear una Custom Action para utilizar en flimageujos de Nintex. Esto puede ser necesario cuando necesitamos realizar alguna acción en el flujo que no podemos realizar con las propias que ya nos proporciona la herramienta.

Se puede consultar el SDK de Nintex para ver cómo realizar estas Custom Actions, aunque ha día de hoy, todavía no han liberado la de Nintex Workflow 2013.

Es un proceso que puede parecer complejo al principio. Yo recomiendo ir trazando el código para localizar los posibles errores que puedan surgir durante la ejecución posterior.

En esta caso, la Custom Action va a ser muy sencilla, simplemente vamos a crear un componente que logue excepciones en el ULS.

Vamos a empezar creando una solución de SharePoint 2013 vacía:

image

Para este proyecto, necesitaremos al menos las siguientes referencias a DLL:

Microsoft.SharePoint.WorkflowActions.dll

Nintex.Workflow.dll

Nintex.Workflow.ServerControls.dll

System.Workflow.Activities.dll

System.Workflow.ComponentModel.dll

image

Aunque luego pongo el esqueleto del proyecto, ahora vamos a seguir, creando una Feature con ámbito de Web Application para desplegar la Custom Action:

image

Lo siguiente es crear una estructura para el fichero NWA (la definición XML de la Custom Action), y las clases para la activity y el adapter, similar a la siguiente:

image

Y otra rama en la carpeta mapeada de Layouts para guardar los iconos y un formulario de configuración de la Custom Action, con una estructura similar a la siguiente (importante que la carpeta que cuelga de Layouts sea NintexWorkflow, que es donde se guardan los archivos de definición y recursos de la herramienta, y va a ir ahí en tiempo de ejecución):

image

Bien, vamos a ver un resumen de la estructura que necesitamos:

image

Tenemos por lo tanto los siguientes elementos:

  • Feature: Que va a ser necesaria para activar la Custom Action a nivel de Web Application. Esta feature debe llevar un Event Receiver donde colocaremos el código necesario para la correcta instalación de la definición de la Custom Action.
  • Un fichero NWA, que contendrá la definición XML de la Custom Action
  • Una clase Activity, que contendrá entre otras cosas la lógica que debe hacer nuestra Custom Action
  • Una clase Adapter, que lleva ciertas lógicas para guardar y obtener las propiedades de nuestra Custom Action
  • Las imáges de nuestra Custom Action. Por el momento son dos, la que se muestra en el panel de componentes, y la que se muestra una vez que la hemos insertado en el área de trabajo.
  • Un formulario ASPX, que pinta los controles para poder dar valor a las propiedades de nuestra Custom Action.
  • Y una clase Logger que contiene la lógica del loguer que queremos implementar.

Bien, vamos lo primero a implementar un loguer propio, que será el que llame nuestra activity. No tiene mucha importancia está clase, ya que es para el ejemplo, pero podría ser algo así:

image

Ahora vamos a implementar la activity. Lo primero es que esta clase debe heredar de la clase ProgressTrackingActivity. Además debe de tener una serie de propiedades definidas (__listItem, __Context, __ListId). Y también las propiedades que vayamos a usar en nuestra Custom Action; en esta caso añadimos una más (ULSMessage, que será el mensaje que trazaremos en el ULS).

public static DependencyProperty __ListItemProperty = DependencyProperty.
Register("__ListItem", typeof(SPItemKey), typeof(NintexULSLogActivity));
public static DependencyProperty __ContextProperty = DependencyProperty.
Register("__Context", typeof(WorkflowContext), typeof(NintexULSLogActivity));
public static DependencyProperty __ListIdProperty = DependencyProperty.
Register("__ListId", typeof(string), typeof(NintexULSLogActivity));

public static DependencyProperty ULSMessageProperty = DependencyProperty.
Register("ULSMessage", typeof(string), typeof(NintexULSLogActivity));

public WorkflowContext __Context
{
get { return (WorkflowContext)base.GetValue(__ContextProperty); }
set { base.SetValue(__ContextProperty, value); }
}

public SPItemKey __ListItem
{
get { return (SPItemKey)base.GetValue(__ListItemProperty); }
set { base.SetValue(__ListItemProperty, value); }
}

public string __ListId
{
get { return (string)base.GetValue(__ListIdProperty); }
set { base.SetValue(__ListIdProperty, value); }
}

public string ULSMessage
{
get { return (string)base.GetValue(ULSMessageProperty); }
set { base.SetValue(ULSMessageProperty, value); }
}

Y en esta misma clase, sobrescribimos dos métodos, el Execute, que contiene la lógica de nuestra Custom Action, y el HandleFault, que será el que gestione qué ocurre cuando se produce un error:


protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
ActivityActivationReference.IsAllowed(this, __Context.Web);
NWWorkflowContext ctx = NWWorkflowContext.GetContext(
this.__Context,
new Guid(this.__ListId),
this.__ListItem.Id,
this.WorkflowInstanceId,
this);

base.LogProgressStart(ctx);

string resolvedProperty = ctx.AddContextDataToString(this.ULSMessage);
ULSLogger.Current.Trace(resolvedProperty);

base.LogProgressEnd(ctx, executionContext);
return ActivityExecutionStatus.Closed;
}

protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
{
Nintex.Workflow.Diagnostics.ActivityErrorHandler.HandleFault(executionContext, exception,
this.WorkflowInstanceId, "Error writing message in ULS Log", __ListItem.Id, __ListId, __Context);
return base.HandleFault(executionContext, exception);
}

Ahora vamos con la clase del adapter. En este caso heredará de GenericRenderingAction, y debe sobrescribir una serie de métodos. Esta es su estructura:


image


Debemos añadir al comienzo de esta clase las propiedades que creamos en el activity anterior, teniendo especial cuidado con que los nombres de las mismas coincidan, o sino luego tendremos problemas.


image


Y ahora vamos a ir implementando cada método de este adapter. Por un lado, el método GetDefaultConfig, que nos permitirá configurar los parámetros de entrada / salida que vamos a usar. En este caso tenemos uno de entrada, que es el mensaje que vamos a trazar en el ULS:


public override NWActionConfig GetDefaultConfig(GetDefaultConfigContext context)
{
NWActionConfig config = new NWActionConfig(this);
config.Parameters = new ActivityParameter[1];

config.Parameters[0] = new ActivityParameter();
config.Parameters[0].Name = ULSMessageProperty;
config.Parameters[0].PrimitiveValue = new PrimitiveValue();
config.Parameters[0].PrimitiveValue.Value = string.Empty;
config.Parameters[0].PrimitiveValue.ValueType = SPFieldType.Text.ToString();

config.TLabel = ActivityReferenceCollection.FindByAdapter(this).Name;
return config;
}

El siguiente, ValidateConfig, que gestiona la validación de nuestras propiedades. En este caso vamos a validar que el mensaje que se introduce no esté vacío:


public override bool ValidateConfig(ActivityContext context)
{
bool isValid = true;
Dictionary<string, ActivityParameterHelper> parameters = context
.Configuration.GetParameterHelpers();
if (!parameters[ULSMessageProperty].Validate(typeof(string), context))
{
isValid &= false;
validationSummary.AddError("ULS Message", ValidationSummaryErrorType.CannotBeBlank);
}
return isValid;
}

Ese mensaje de salida se verá en el área de trabajo de esta forma:


image


El siguiente, AddActivityToWorkflow, que será el que cree la instancia de nuestra activity cuando la insertamos en el área de trabajo.


public override CompositeActivity AddActivityToWorkflow(PublishContext context)
{
Dictionary<string, ActivityParameterHelper> parameters = context.Config.GetParameterHelpers();
NintexULSLogActivity activity = new NintexULSLogActivity();

parameters[ULSMessageProperty].AssignTo(activity,
NintexULSLogActivity.ULSMessageProperty, context);

activity.SetBinding(NintexULSLogActivity.__ContextProperty,
new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__context));
activity.SetBinding(NintexULSLogActivity.__ListItemProperty,
new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__item));
activity.SetBinding(NintexULSLogActivity.__ListIdProperty,
new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__list));

ActivityFlags f = new ActivityFlags();
f.AddLabelsFromConfig(context);
f.AssignTo(activity);

context.ParentActivity.Activities.Add(activity);
return null;
}

La siguiente, GetConfig, que recoge las propiedades del contexto de la actividad y las actualiza en el NWActionConfig.


public override NWActionConfig GetConfig(RetrieveConfigContext context)
{
NWActionConfig config = this.GetDefaultConfig(context);
Dictionary<string, ActivityParameterHelper> parameters = config.GetParameterHelpers();
parameters[ULSMessageProperty].RetrieveValue(context.Activity,
NintexULSLogActivity.ULSMessageProperty, context);

return config;
}

Y por último, BuildSummary, que muestra un mensaje de resumen cuando la actividad ha sido configurada en el área de trabajo.


public override ActionSummary BuildSummary(ActivityContext context)
{
Dictionary<string, ActivityParameterHelper> parameters =
context.Configuration.GetParameterHelpers();
return new ActionSummary("Write to ULS: {0}",
parameters[ULSMessageProperty].Value);
}

Este sería el resultado de este BuildSummary (al pasar el cursor sobre nuestra Custom Action ya configurada):


image


Una vez implementadas las dos clases, vamos a continuar con el formulario ASPX. Deberemos añadir las referencias a los componentes / controles de Nintex, así que registramos los siguientes:


<%@ Register TagPrefix="Nintex" Namespace="Nintex.Workflow.ServerControls" Assembly="Nintex.Workflow.ServerControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=913f6bae0ca5ae12" %> 
<%@ Register TagPrefix="Nintex" TagName="ConfigurationPropertySection" src="~/_layouts/15/NintexWorkflow/ConfigurationPropertySection.ascx" %>
<%@ Register TagPrefix="Nintex" TagName="ConfigurationProperty" src="~/_layouts/15/NintexWorkflow/ConfigurationProperty.ascx" %>
<%@ Register TagPrefix="Nintex" TagName="DialogLoad" Src="~/_layouts/15/NintexWorkflow/DialogLoad.ascx" %>
<%@ Register TagPrefix="Nintex" TagName="DialogBody" Src="~/_layouts/15/NintexWorkflow/DialogBody.ascx" %>
<%@ Register TagPrefix="Nintex" TagName="SingleLineInput" Src="~/_layouts/15/NintexWorkflow/SingleLineInput.ascx" %>

<%@ Register TagPrefix="Nintex" TagName="PlainTextWebControl" Src="~/_layouts/15/NintexWorkflow/PlainTextWebControl.ascx" %>

Y continuamos insertando en nuestro PlaceHolderAdditionalPageHeader las funciones cliente que necesitamos para leer y escribir los valores en nuestras propiedades:


<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<Nintex:DialogLoad runat="server" />
<script type="text/javascript" language="javascript">
function TPARetrieveConfig() {
setRTEValue('<%=ULSMessageProperty.ClientID%>', configXml.selectSingleNode("/NWActionConfig/Parameters/Parameter[@Name='ULSMessage']/PrimitiveValue/@Value").text);
}

function TPAWriteConfig() {
configXml.selectSingleNode("/NWActionConfig/Parameters/Parameter[@Name='ULSMessage']/PrimitiveValue/@Value").text = getRTEValue('<%=ULSMessageProperty.ClientID%>');
return true;
}

onLoadFunctions[onLoadFunctions.length] = function () {
dialogSectionsArray["<%= MainControls1.ClientID %>"] = true;
};
</script>
</asp:Content>

Y en el PlaceHolderMain metemos una sección para “pintar” los controles necesarios para rellenar los valores de nuestras propiedades. En este caso uso el control SingleLineInput, que muestra un botón para construir texto utilizando propiedades del contexto del flujo de trabajo.


<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<Nintex:ConfigurationPropertySection runat="server" Id="MainControls1">
<TemplateRowsArea>
<Nintex:ConfigurationProperty runat="server" FieldTitle="ULS Message" RequiredField="True">
<TemplateControlArea>
<Nintex:SingleLineInput clearFieldOnInsert="true" runat="server" id="ULSMessageProperty"></Nintex:SingleLineInput>
</TemplateControlArea>
</Nintex:ConfigurationProperty>
</TemplateRowsArea>
</Nintex:ConfigurationPropertySection>


<Nintex:DialogBody runat="server" id="DialogBody">
</Nintex:DialogBody>

</asp:Content>

Y por último, a la clase de este formulario ASPX le modificamos la clase de la que hereda por NintexLayoutBase:


image


Ahora vamos a configurar el fichero NWA, que será el que contenga la definición XML de la Custom Action. Lo primero será, en sus propiedades, cambiar la Build Action y otros aspectos. La configuración debe quedar así:


image


Y en ese fichero NWA definimos la Custom Action:


<NintexWorkflowActivity>
<Name>ULS Log</Name>
<Category>Custom Activities</Category>
<Description>Custom Log in ULS</Description>
<ActivityType>NintexLogAction.CustomActions.NintexULSLog.NintexULSLogActivity</ActivityType>
<ActivityAssembly>$SharePoint.Project.AssemblyFullName$</ActivityAssembly>
<AdapterType>NintexLogAction.CustomActions.NintexULSLog.NintexULSLogAdapter</AdapterType>
<AdapterAssembly>$SharePoint.Project.AssemblyFullName$</AdapterAssembly>
<HandlerUrl>ActivityServer.ashx</HandlerUrl>
<Icon>/_layouts/15/NintexWorkflow/CustomActions/NintexLogAction/Images/ULS49x49.png</Icon>
<ToolboxIcon>/_layouts/15/NintexWorkflow/CustomActions/NintexLogAction/Images/ULS30x30.png</ToolboxIcon>
<ConfigurationDialogUrl>/CustomActions/NintexLogAction/NintexULSLogDialog.aspx</ConfigurationDialogUrl>
<ShowInCommonActions>yes</ShowInCommonActions>
<DocumentLibrariesOnly>no</DocumentLibrariesOnly>
</NintexWorkflowActivity>

Como en algunas etiquetas hemos metido tokens para resolver el nombre del assembly completo, debemos añadir en nuestro proyecto una regla para que Visual Studio reemplace esos tokens al compilar nuestro proyecto. Para ello editamos el fichero .csproj:


image


image


Y justo antes de cerrar el primer PropertyGroup, añadimos la regla:



<TokenReplacementFileExtensions>nwa</TokenReplacementFileExtensions>


De forma que queda así:


image


image


Y por último el código de activación de la Feature. Básicamente se trata de registrar o de desregistrar nuestro fichero NWA que contiene la definición de Custom Action. El siguiente código realiza esas operaciones:


public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWebApplication parent = (SPWebApplication)properties.Feature.Parent;
RegisterNWA(parent, properties, "NintexULSLogAction.nwa");
}


public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication parent = (SPWebApplication)properties.Feature.Parent;
UnRegisterNWA(parent, properties,
"NintexLogAction.CustomActions.NintexULSLog.NintexULSLogAdapter",
Assembly.GetExecutingAssembly().FullName);
}


protected void RegisterNWA(SPWebApplication parent, SPFeatureReceiverProperties properties,
string pathToNWAFile)
{
XmlDocument nwaXml = GetNWADefinition(properties, pathToNWAFile);

ActivityReference newActivityReference = ActivityReference.ReadFromNWA(nwaXml);
ActivityReference action = ActivityReferenceCollection.FindByAdapter(newActivityReference.AdapterType,
newActivityReference.AdapterAssembly);

if (action != null)
{
// update the details if the adapter already exists
ActivityReferenceCollection.UpdateActivity(action.ActivityId, newActivityReference.Name,
newActivityReference.Description, newActivityReference.Category,
newActivityReference.ActivityAssembly, newActivityReference.ActivityType,
newActivityReference.AdapterAssembly, newActivityReference.AdapterType,
newActivityReference.HandlerUrl, newActivityReference.ConfigPage,
newActivityReference.RenderBehaviour, newActivityReference.Icon, newActivityReference.ToolboxIcon,
newActivityReference.WarningIcon, newActivityReference.QuickAccess,
newActivityReference.ListTypeFilter);
}
else
{
ActivityReferenceCollection.AddActivity(newActivityReference.Name, newActivityReference.Description,
newActivityReference.Category, newActivityReference.ActivityAssembly,
newActivityReference.ActivityType, newActivityReference.AdapterAssembly,
newActivityReference.AdapterType, newActivityReference.HandlerUrl, newActivityReference.ConfigPage,
newActivityReference.RenderBehaviour, newActivityReference.Icon, newActivityReference.ToolboxIcon,
newActivityReference.WarningIcon, newActivityReference.QuickAccess,
newActivityReference.ListTypeFilter);
action = ActivityReferenceCollection.FindByAdapter(newActivityReference.AdapterType,
newActivityReference.AdapterAssembly);
}

string activityTypeName = string.Empty;
string activityNamespace = string.Empty;

Utility.ExtractNamespaceAndClassName(action.ActivityType, out activityTypeName, out activityNamespace);
AuthorisedTypes.InstallAuthorizedWorkflowTypes(parent, action.ActivityAssembly, activityNamespace,
activityTypeName);

ActivityActivationReference reference = new ActivityActivationReference(action.ActivityId, Guid.Empty,
Guid.Empty);

reference.AddOrUpdateActivationReference();
}

protected void UnRegisterNWA(SPWebApplication parent, SPFeatureReceiverProperties properties,
string adapterType, string adapterAssembly)
{
ActivityReference action = ActivityReferenceCollection.FindByAdapter(adapterType, adapterAssembly);
if (action != null)
{
if (!IsFeatureActivatedInAnyWebApp(parent, properties.Definition.Id))
ActivityReferenceCollection.RemoveAction(action.ActivityId);
string activityTypeName = string.Empty;
string activityNamespace = string.Empty;
Utility.ExtractNamespaceAndClassName(action.ActivityType, out activityTypeName, out activityNamespace);

Collection<SPWebConfigModification> modifications = parent.WebConfigModifications;

foreach (SPWebConfigModification modification in modifications)
{
if (modification.Owner == AuthorisedTypes.OWNER_TOKEN)
{
if (IsAuthorizedTypeMatch(modification.Value, action.ActivityAssembly, activityTypeName,
activityNamespace))
{
modifications.Remove(modification);
parent.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
break;
}
}
}
}
}

private bool IsAuthorizedTypeMatch(string modification, string activityAssembly, string activityType,
string activityNamespace)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(modification);

if (doc.FirstChild.Name == "authorizedType")
{
return (doc.SelectSingleNode("//@TypeName").Value == activityType
&& doc.SelectSingleNode("//@Namespace").Value == activityNamespace
&& doc.SelectSingleNode("//@Assembly").Value == activityAssembly);
}
return false;
}

private bool IsFeatureActivatedInAnyWebApp(SPWebApplication thisWebApplication, Guid thisFeatureId)
{
SPWebService webService = SPWebService.ContentService;
if (webService == null)
throw new ApplicationException("Cannot access ContentService");
SPWebApplicationCollection webApps = webService.WebApplications;
foreach (SPWebApplication webApp in webApps)
{
if (webApp != thisWebApplication)
if (webApp.Features[thisFeatureId] != null)
return true;
}

return false;
}

private XmlDocument GetNWADefinition(SPFeatureReceiverProperties properties, string pathToNWAFile)
{
using (Stream stream = properties.Definition.GetFile(pathToNWAFile))
{
XmlDocument nwaXml = new XmlDocument();
nwaXml.Load(stream);
return nwaXml;
}

}

Y ya está. Desplegamos el proyecto, y comprobamos si nuestra Custom Action se ha creado y registrado correctamente. Para ello, desde la Administración Central vamos a las Manage allowed actions.


image


Y vemos que efectivamente está registrada:


image


Así que creamos un flujo y hacemos una prueba rápida:


image


Y nuestro diálogo ASPX de configuración:


image


Y el resultado al ejecutarlo sobre una lista cualquiera:


image


 


Espero que haya servido de ayuda.

Workflow de Nintex con llamada a Web Service

Ya vimos en otro post anterior cómo utilizar Nintex para crear un flujo de trabajo sencillo. En este otro ejemplo vamos a crear un flujo sencillo que se conecte a un Web Service para trabajar con su respuesta.

El ejemplo va a ser crear una lista donde guardar una dirección de email, y lanzar el flujo sobre ese elemento para hacer una llamada a un servicio y que nos indique si el email es válido o no.

La dirección del servicio que voy a usar es la siguiente: http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx?wsdl

Lo primero es construir la lista que vamos a usar para guardar los emails. En este caso vamos a usar el campo Title para guardar la dirección, y un campo multilinea de texto plano para guardar la respuesta del servicio (luego veremos cómo tratar la respuesta). Algo así por ejemplo:

image

Sobre la lista creada vamos a crear un flujo de trabajo con Nintex.

image

Y vamos a insertar en primer lugar un componente de tipo Call web service:

image

Y lo configuramos. Lo primero es introducir la dirección URL del servicio al que vamos a atacar, y posteriormente, hacemos clic sobre el botón de Refresh para comprobar la llamada y para que nos descubra el WSDL y seleccionemos el método del servicio al que queremos llamar, en este caso VerifyEmail:

image

Lo siguiente es configurar la salida. Para ello primero vamos a crear una variable, donde guardaremos ese valor. En esta misma ventana, en la zona superior podemos acceder a las variables y crear una:

image

Para guardar la respuesta creamos una variable de tipo multilinea.

image

Y una vez creada la variable, terminamos de configurar la salida del servicio. (Antes hemos indicado en los parámetros del método al que vamos a llamar que el campo email vendrá de la variable Title que leemos de la lista en cuestión.) En este caso como queremos guardarlo en una variable plana para ver qué tipo de salida nos da, lo dejamos por ejemplo como sigue:

image

Y metemos un componente más en el flujo para guardar el valor de la salida del servicio en nuestro campo de la lista (usamos el componente Set field value):

image

Y lo configuramos de la siguiente manera, con el fin de que la variable que recoge el resultado del servicio se guarde en la columna de la lista que hemos llamado WSResult (hacemos uso del icono de la derecha para acceder a los tokens disponibles del workflow):

image

Y por último configuramos el flujo para indicar que queremos un arranque manual y con opción desde su menú contextual:

image

Y ya sólo faltaría probarlo. Creamos un par de elementos, uno con email válido y otro inválido y lanzamos el flujo para ver qué resultado obtenemos. Algo así:

image

image

Como vemos, el XML de la respuesta tiene una etiqueta con nombre GoodEmail que indica el resultado final, verdadero o falso. Vamos a modificar la lista para guardar ese valor en lugar de guardar el XML entero.

Para ello, creamos un campo nuevo en la lista de tipo booleano:

image

Y modificamos el componente de Call web service de nuestro flujo, en concreto el output, en donde ahora vamos a cambiar y donde pone Default value, vamos a seleccionar Specify elements, de forma que podemos navegar por el árbol de etiquetas del XML de la respuesta para seleccionar el deseado:

image

Y lo dejamos configurado de esta forma:

image

Y ahora, lo que vamos a hacer es incorporar al flujo un poco de lógica, de modo que si la respuesta que obtenemos en un texto que indica true, vamos a grabar el valor True en la columna de la lista y viceversa. Añadimos los siguientes componentes:

image

Y en el Run if, vamos a configurar lo que hemos descrito:

image

Si la acción continúa en el If, vamos a establecer el valor de la columna con un True, así que añadimos el componente Set field value:

image

Y lo configuramos para que guarde un True, ya que siempre que entremos por esta rama del If es porque el valor es verdadero:

image

Ahora, para replicar esta rama a la del else, en vez de crear otra vez cada componente, podemos copiar y pegar la actual y cambiar las condiciones:

image

image

Cambiamos las condiciones de la rama nueva para que entre cuando el valor sea false, y a la vez en la columna se guarde un False también. El resultado es un flujo como el siguiente:

image

Y si ahora lanzamos de nuevo el flujo sobre los elementos que ya teníamos creados, vemos que el resultado es el que buscábamos (faltaría eliminar la columna antigua, ya que los valores que tenía no se eliminan solos):

image

Primeros pasos con Nintex Workflow en SharePoint 2013

Para el que no conozca Nintex Workflow, es una herramienta que permite diseñar y publicar flujos de trabajo dentro de SharePoint, sin necesidad de una herramienta externa como pueda ser Visual Studio o SharePoint Designer. Nintex cuenta con un gran número de componentes a través de los cuales podemos construir distintos tipos de flujos.

En este post vamos a ver cómo crear un flujo muy básico sobre una lista, omitiendo todo el proceso de instalación de la herramienta.

Lo primero de todo será activar las características necesarias dentro de nuestro portal, así como comprobar que la licencia es válida y la configuración de la base de datos de Nintex es correcta.

Vamos a crear un flujo que, cuando se cree un elemento en una lista, se produzca la creación de otro elemento en una lista “espejo”, usando además un par de trazas para poder monitorizar el resultado de la ejecución.

Vamos a crear en primer lugar una lista (NList), y vamos a crear un flujo de trabajo sobre ella, utilizando el Wizard de Nintex (Create a Workflow in Nintex Workflow):

image

Seleccionaremos una plantilla en blanco para construir nuestro flujo desde cero:

image

Podemos ver que nuestro área de trabajo se divide fundamentalmente en tres zonas, una superior (Ribbon) donde están las opciones de guardar, publicar y opciones del flujo entre otras, un panel izquierdo donde vamos a encontrar los componentes agrupados por categorías, y una zona central donde vamos a arrastrar esos componentes hasta formar nuestro flujo de trabajo:

image

Si echamos un vistazo a los ajustes generales del flujo, podremos acceder a opciones tales como establecer título y descripción al flujo, configurar el auto-start, configurar los criterios de arrancado, y otra serie de opciones:

image

Vamos a comenzar arrastrando un componente de tipo traza en el log para monitorizar el comienzo del flujo, seguido de otro componente que será el de crear el item en la lista “espejo” que hemos creado previamente:

image

Para configurar el componente de Create item, accedemos desde su menú contextual a las opciones de configuración, y podemos establecer algo como lo siguiente:

image

En donde hemos fijado la lista sobre la que vamos a trabajar, y el texto que queremos configurar en el campo Title de la misma, usando el campo Title de la lista origen, a través de un selector que nos permite coger ese tipo de propiedades.

Añadimos una traza al final y tenemos un flujo muy básico con el siguiente aspecto:

image

Lo publicamos y comprobamos que efectivamente nuestra lista NList tiene configurado el flujo que acabamos de crear:

image

Ahora podemos crear un elemento y ver si se crea su copia espejo. En este caso yo me he encontrado con dos problemas.

Por un lado, que no se puede realizar el auto-start de un flujo de trabajo si la cuenta que estamos usando para probar es la de System Account. Al ver que no se arrancaba de forma automática, he revisado las trazas y he visto el siguiente mensaje:

Declarative workflows cannot automatically start if the triggering action was performed by System Account. Canceling workflow auto-start. List Id: b7d9d101-f401-400a-bc6f-96ec0ada8df8, Item Id: 4, Workflow Association Id: 9fa0278e-6aeb-4632-97d6-95af76a379f8

Así que para probarlo, vamos a lanzarlo de manera manual. Una vez lanzado, veo que la ejecución ha fallado en el arranque del mismo. Reviso trazas de nuevo, y encuentro una excepción de este tipo:

RunWorkflow: Microsoft.SharePoint.SPException: <Error><CompilerError Line="0" Column="10" Text="The type or namespace name 'Nintex' could not be found …

Revisando la configuración, me doy cuenta de que he olvidado activar Nintex para la Web Application sobre la que estoy trabajando, así que lo activo (Web Application activation):

image

image

Vuelvo a probar, y ahora si ha funcionado. Compruebo que se ha creado su copia en la lista espejo:

imageimage

Y si ahora revisamos el histórico de ejecución del flujo de trabajo, podemos ver los mensajes de las dos trazas que habíamos configurado:

image

SharePoint Between Racks

SharePoint Between Racks © 2012
. Con la tecnología de Blogger.

¡Compártelo!


Estoy en LinkedIn!


Ve mi perfil en LinkedIn!