miércoles, 18 de junio de 2014

SPD 2013: Utilizar una columna de “metadatos administrados” en un Workflow.

Cuando nos enseñan a crear metadatos administrados (Managed Metadata) en SharePoint 2010 y 2013, vemos que es un servicio muy “cool” y tendemos a recibirlos con gran entusiasmo y a querer incorporarlos asiduamente a nuestra “familia” de tipos de columnas que nos gusta administrar en nuestro SharePoint.

Sin embargo, el tiempo y la experiencia nos demostrará que este tipo de columnas no son tan amigables como nos gustaría, y tiene una serie de limitaciones importantes (alguna ya resuelta en 2013, y otras que faltarían en esa lista).

En concreto hoy voy a centrarme en cómo utilizar este tipo de campos en los flujos de trabajo que utilizaremos en SharePoint Designer, pues no es tan sencillo como debiera ser o pudiera parecer (¡Esto es SharePoint, amigos!).

Vamos a partir del siguiente ejemplo/requerimiento: Tenemos una biblioteca de SharePoint donde existe una columna de tipo “metadatos administrados” de dos niveles. El primer nivel nos indica la provincia. El segundo nivel el municipio. La función de este campo es que el usuario pueda marcar la relación directa de un Municipio con su Provincia, de igual forma que haríamos con un desplegable en cascada. El campo almacena toda la ruta en sí mismo (ambos niveles), para que sea visible la relación existente a simple vista y el workflow pueda tratarlo.

El campo tendría el siguiente aspecto:
image
Ahora vamos a desarrollar un flujo de trabajo que permita leer ese valor y utilizarlo para separarlo en 2 metadatos diferentes (Provincia y Municipio), ya que eso permitirá al usuario filtrar u ordenar por ambos conceptos.

Lo primero que alguien normal intentaría sería realizar algo del tipo: Crear una variable tipo String y guardar en ella el valor de la columna.

image

Si hacemos esto tal cual, veremos que obtenemos un error en el flujo donde nos indica que no puede convertir en string este tipo de dato:

System.InvalidCastException: The value 'd/results(0)/Localidad' cannot be read as type 'String'.
Lo siguiente sería darnos cuenta de que al crear el managed metadata “Localidad”, automáticamente se ha creado un campo “Localidad_0” que invocado desde el flujo permite modificar lo que retorna a “Texto plano”.

image

Si lo intentamos de esta forma no nos dará un error, pero el valor retornado será un galimatías indescifrable del tipo “Calella|685929bb-f430-4179-9302-5c7fb646f5ac”. Si únicamente es un nivel, podemos hacer un “substring” del resultado, y quedarnos con la primera parte del “churro que retorna” (a partir del separador “|”). En el caso de que estemos trabajando con varios niveles de anidación, como en nuestro ejemplo, la estructura retornada del tipo “valor del último nivel de tag|GUID”  ha perdido todos los valores de los primeros niveles del árbol de metadatos, y no nos permite recuperar el string deseado, del tipo "Barcelona:Calella".

¡Maldito bicho! ¿Cómo podemos tratarlo entonces? Pues si nuestro flujo está en modalidad 2013 podremos hacerlo mediante la invocación y tratamiento del WebService que nos va a devolver el valor deseado del ítem tratado en la lista. (aquí es cuando comienzas a arrepentirte de no haber utilizado un “cascading dropdown” eh! Guiño). Tranquilo… respira… Aquí la solución:

El primer paso será crear una variable de tipo diccionario, mediante la acción “Build Dictionary” que encontraremos en la sección “Core Actions”, donde añadiremos 2 campos, uno llamado “Accept” y otro llamado “Content-Type”, ambos de tipo String, y ambos con el mismo valor: “application/json;odata=verbose”
image

Una vez inicializada esta variable, procederemos a invocar el WebService. Hay que tener en cuenta que el WebService a utilizar será del tipo:

Current Site URL/_api/Web/Lists('List GUID')/Items(Current ID)/ManagedMetadataName/Label
Para ello recurriremos a la acción “Call HTTP Web Service” que encontraremos en la sección “Core Actions”. Una vez insertada, modificaremos el parámetro “this” por la siguiente cadena (contiene 3 referencias a propiedades dinámicas)

[%Workflow Context:Current Site URL%]/_api/Web/Lists(guid'[%Workflow Context:List ID%]')/Items([%Current Item:ID%])/Localidad/Label

Donde modificaremos "Localidad" por el nombre de nuestro metadato administrado.

También modificaremos el parámetro “response” por una nueva variable de tipo “Dictionary”. Esta variable almacenará el resultado de la llamada al WebService.
image
Una vez realizada la invocación al WS, es un momento estupendo para insertar un log de control en el flujo y ver qué ha respondido el servicio. Para ello utilizaremos la acción “Log to History List” que encontraremos en el grupo de “Core Actions”.
image
Ahora vamos a extraer del xml retornado el valor en formato string del campo de managed metadata.

Para ello utilizamos la acción “Get an item from dictionary” en el grupo de “Core Actions”. Aquí sustituiremos los 3 parámetros:
  • item by name or path: Directamente por “d/Label”
  • dictionary: La variable de tipo Dictionary donde hemos guardado el resultado de la invocación al HTTP Web Service.
  • item: Nueva variable de tipo String donde almacenaremos el valor literal del campo.
image
Opcionalmente podemos insertar en este punto otro “Log to History List” para comprobar el valor convertido en String.

image
Llegados a este punto ya tendríamos el valor del campo de metadatos administrados en formato String, conservando todo su path de niveles de anidación.

A partir de aquí haremos el proceso de datos que queramos con este string. En nuestro ejemplo queríamos almacenar cada parte del mismo en un campo distinto, así que vamos a ello.

Para saber en qué posición se encuentra el separador de nivel del árbol de metadatos (en SharePoint 2013 son los dos puntos ‘:’) utilizaremos “Find substring in string” de la sección “Utility Actions”.
  • substring: Poner directamente los 2 puntos ‘:’
  • string: indicar la variable donde almacenamos el resultado del “Get an Item from dictionary”
  • Variable: index:  Podemos dejarla la que inserta SPD por defecto, es una nueva variable de tipo integer que indicará en qué posición se encuentra el separador.
image
Ahora utilizaremos la función “Extract Substring from Start of String” de la sección de “Utility Actions” para obtener el primer valor del campo de metadatos administrados, contenido entre el inicio del string y los dos puntos.
  • 0: Substituir por la variable de tipo integer que obtuvimos como resultado de la búsqueda del patrón en “Find substring in string”
  • string: Indicar la variable donde almacenamos el resultado del “Get an Item from dictionary”
  • Variable: substring: Nueva variable de tipo string que almacenará el primer nivel del arbol del campo de metadatos administrados. En este ejemplo, contendrá el nombre de la provincia.image
Ahora deberemos incrementar el índice en una unidad para saltarnos el separador (‘:’) y posicionarnos en el primer carácter del segundo string. Para ello utilizaremos la acción “Do calculation” que se encuentra en el grupo “Core Actions”.
  • value:Substituir por la variable de tipo integer que obtuvimos como resultado de la búsqueda del patrón en “Find substring in string”
  • plus: Dejarlo en “plus” exactamente igual como nos aparece inicialmente. Hará una suma.
  • value: Indicar una unidad, que es lo que queremos sumarle al índice. ‘1’.
  • Variable: calc: Nueva variable de tipo Number que se genera como resultado del cálculo. Podemos dejarla tal cual aparece por defecto
image
Ahora utilizaremos la acción “Extract Substring from Index” del grupo “Utility Actions” para obtener el segundo valor contenido entre el carácter posterior a los dos puntos y el final del string.
  • string: Indicar la variable donde almacenamos el resultado del “Get an Item from dictionary”
  • 0: Substituir por la variable de tipo number que obtuvimos en el resultado de “Do Calculation”
  • Variable: substring: Nueva variable de tipo String que almacenará el segundo nivel del metadato administrado. En nuestro ejemplo será el Municipio.
image
En este punto ya tenemos separados ambos valores del campo de metadatos administrados (Provincia y Municipio). De forma opcional, podríamos indicar estos valores en el “history list”. Ahora solo quedaría introducir esos valores en las columnas correspondientes de la biblioteca. Para ello usaremos la acción “Update List Item” que encontraremos en grupo de “List Actions”.
image
Hecho esto nuestro flujo ya debería funcionar perfectamente. Tendría un aspecto similar al siguiente:
image
Ya solo quedaría publicar y listo. Una vez lo hayamos hecho ya solo quedará montarnos las vistas en la biblioteca según nuestro criterio para mostrar los datos requeridos. A pesar de que en el NewForm.aspx el campo que aparece es el de “Localidad” de tipo Managed Metadata, en las vistas podremos jugar con los campos “Provincia” y “Municipio” de tipo string que nos alimentará el workflow:
image
Notad que para este ejemplo he usado un metadato administrado de 2 niveles, pero podrían ser muchos más igualmente. La clave está en usar la llamada http al WebServices para obtener el String resultante.

Como veis, es mucho más simple un cascading dropdown, que francamente, os lo recomiendo antes que realizar todo este proceso. Sin embargo, los requerimientos del cliente vienen como vienen, y no siempre tenemos la opción de plantear la mejor arquitectura técnica.

¡¡Saludos SharePointeros!!

No hay comentarios: