domingo, 19 de abril de 2009

[Infopath] Promoción de propiedades

Nos queda por ver como relacionar los formularios infopath con Sharepoint más allá de la visualización. Para ello vamos a ver la promoción de propiedades y como manejar el elemento generado mediante un EventHandler desde Sharepoint.

La promoción de propiedades consiste en extraer los valores de ciertos campos del formulario para utilizarlos en otro entorno como por ejemplo sharepoint, pudiendo manejarlos en vistas, búsquedas, workflows, etc.

Si continuamos con nuestro ejemplo de notas de gasto de los anteriores post, promocionarnos la propiedad del campo “estado” para controlarlo desde un EventReceiver y así controlar que no se pueda editar el documento una vez entregado.

Al guardar los datos de un formulario infopath podremos indicarle que lo haga sobre una biblioteca de documentos de Sharepoint, que llame a un servicio web, que lo envíe por correo o que lo envíe en modo post a a una páginas web. En sharepoint lo guardaremos en una biblioteca de formularios. Al guardar el formulario, infopath creará sobre la biblioteca indicada un fichero xml con los datos introducidos. Podemos entonces asociar a la biblioteca un EvenReceiver, un Workflow o promocionar propiedades.

Asociaremos entonces un EvenReceiver sobre la biblioteca que tenía asociada la plantilla xsn con nuestro formulario. Este EvenReceiver controlará el estado de la nota de gasto de forma que una vez la hayamos entregado no podamos volver a editarla. Hasta ahora no habíamos definido ningún campo “estado” en nuestra plantilla, por lo que ¿cómo lo agregamos para que podamos leer desde el formulario infopath y el EvenReceiver de sharepoint?, pues con la promoción de propiedades. Básicamente consiste en indicar tanto a sharepoint como a infopath que camos del origen de datos principal se va a compartir. Para infopath la propiedad promocionada será como un campo más del origen de datos principal, y para sharepoint corresponderá con una nueva columna en la biblioteca donde se aloje la plantilla xsn.

Abrimos entonces nuestro fichero EjemploNotasDeGasto.xsn en modo edición. Agregamos un campo al origen de datos principal un nuevo campo con el nombre “Estado” del tipo texto, además marcaremos que se pueda editar el campo desde una venta de propiedades.

A continuación publicamos nuestra plantilla sobre una biblioteca de documentos, en esta ocasión lo he publicado como plantilla de una biblioteca de formularios con compatibilidad de explorador. Es importante que antes de publicar indiquéis en las propiedades de la plantilla que determine el nivel de seguridad automáticamente.

Una vez indicada la biblioteca os aparecerá una pantalla con el siguiente aspecto, sobre la que agregaremos una nueva columna sobre la que indicaremos la relación entre la columna de la biblioteca con el campo del origen de datos del formulario infopath.




Una vez publicado nuestra plantilla xsn veremos en las propiedades de nuestra biblioteca una nueva columna:



Con VisualStudio creamos un EvenReceiver y lo asociaremos a la biblioteca de forma que cuando el usuario rellene el campo “Responsable” cambiemos el valor del campo “Estado”.

Pero fijaros que el responsable no es una propiedad promocionada, entonces ¿cómo leemos los datos xml generados por infopath para conocer los valores del Responsable?:

  • Promocionando la propiedad
  • Leyendo los datos guardados por infopath en modo xml

Si abrimos el fichero xml generados por la plantilla encontraremos algo como esto
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:Plantilla1-2:-myXSD-2009-03-20T16-27-26" solutionVersion="1.0.0.29" productVersion="12.0.0.0" PIVersion="1.0.0.0" href="http://w2k3r2:17092/FormServerTemplates/Plantilla1_2.xsn"?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:misCampos xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-03-20T16:27:26" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xml:lang="es-ES">
<my:txtNombre>Mario</my:txtNombre>
<my:txtEmail>mcortes@renacimiento.com</my:txtEmail>
<my:txtApellidos>Cortés flores</my:txtApellidos>
<my:txtNumeroEmpleado>338877</my:txtNumeroEmpleado>
<my:grupo1>
<my:grupo2>
<my:txtFecha>2009-04-02</my:txtFecha>
<my:txtImporte>11</my:txtImporte>
<my:tipoDeGasto>Taxi</my:tipoDeGasto>
</my:grupo2>
</my:grupo1>
<my:txtObservaciones></my:txtObservaciones>
<my:gpResponsable>
<my:Responsable>
<my:DisplayName></my:DisplayName>
<my:AccountId></my:AccountId>
<my:AccountType></my:AccountType>
</my:Responsable>
</my:gpResponsable>
</my:misCampos>

Si accedemos por código al objeto SPListItem que representa el elemento almacenado dispondremos de la propiedad “File” que podremos leer abriendo un stream de la siguiente forma:
XmlDocument xmlNotaDeGasto = new XmlDocument();
XmlTextReader
xmlTextReader = new XmlTextReader(properties.ListItem.File.OpenBinaryStream());
xmlNotaDeGasto .Load(xmlTextReader );
Una vez cargado los datos como un XmlDocument accederemos al campo "my:AccountId” para leer el valor. El problema vendrá cuando intentemos acceder por ejemplo con el método “SelectSingleNode”, obtendremos una excepción pidiéndonos un “XmlNamespaceManager”, si lo creamos y lo pasamos a SelectSingleNode obtendremos otra excepción del tipo “unrecognized ‘my’ prefix”. Para solucionarlo crearemos el XmlNamespaceManager de la siguiente forma:
public XmlNamespaceManager InitNamespaceManager(XmlDocument xmlDOMDoc)
{
XmlNamespaceManager xnmMan;

xnmMan = new XmlNamespaceManager(xmlDOMDoc.NameTable);

foreach (XmlAttribute nsAttr in
xmlDOMDoc.DocumentElement.Attributes)

{

if (nsAttr.Prefix=="xmlns")

xnmMan.AddNamespace(nsAttr.LocalName,nsAttr.Value);

}

return xnmMan;

}

Para crear el EvenReceiver he utilizado las extensiones VseWSS 1.3 y el código quedaría una cosa así:

public class NotasDeGastoItemEventReceiver : SPItemEventReceiver
{
public NotasDeGastoItemEventReceiver()
{
}

public override void ItemAdding(SPItemEventProperties properties)
{
ItemUpdating(properties);
}


public override void ItemUpdating(SPItemEventProperties properties)
{
try
{
if
(string.IsNullOrEmpty((string)properties.ListItem["Estado"]))
{
XmlDocument xmlNotaDeGasto = new XmlDocument();
XmlTextReader
xmlTextReader = new XmlTextReader(properties.ListItem.File.OpenBinaryStream());
xmlNotaDeGasto.Load(xmlTextReader);
XmlNode nodoAccountResponsable =
xmlNotaDeGasto.SelectSingleNode("//my:AccountId",
InitNamespaceManager(xmlNotaDeGasto));
string nuevoEstado;
if
(!string.IsNullOrEmpty(nodoAccountResponsable.InnerText))
nuevoEstado =
"Publicado";
else
nuevoEstado = "Borrador";


DisableEventFiring();
try
{
properties.ListItem["Estado"] = nuevoEstado;
}
finally
{
EnableEventFiring();
}
}
}
catch
{
properties.ErrorMessage = "Se ha producido un error que impide la
modificación";
properties.Status = SPEventReceiverStatus.CancelWithError;
}
}





public override void ItemDeleting(SPItemEventProperties properties)
{
if (!string.IsNullOrEmpty((string)properties.ListItem["Estado"])&&
(string)properties.ListItem["Estado"] != "No iniciado" &&
(string)properties.ListItem["Estado"] != "Borrador")
{
properties.ErrorMessage = "La nota de gasto no puede borrarse";
properties.Status = SPEventReceiverStatus.CancelWithError;
}
}





#region "Helper"
public XmlNamespaceManager
InitNamespaceManager(XmlDocument xmlDOMDoc)
{
XmlNamespaceManager
xnmMan;
xnmMan = new XmlNamespaceManager(xmlDOMDoc.NameTable);
foreach
(XmlAttribute nsAttr in xmlDOMDoc.DocumentElement.Attributes)
{
if
(nsAttr.Prefix == "xmlns")
xnmMan.AddNamespace(nsAttr.LocalName,
nsAttr.Value);
}
return xnmMan;
}
#endregion "Helper"

}

Por último nos queda controlar desde el formulario que solo se pueda abrir en modo edición cuando el valor del campo estado no sea “No entregado” o “Borrador, (en este caso no lo vamos a hacer por programación sobre Infopath), bloquearemos los controles usando los “formatos condicionales”. Desde las propiedades de cada uno de los controles desde la pestaña “Presentación” > formato condicional > agregar.

No hay comentarios: