domingo, 1 de febrero de 2009

Capturando eventos en Sharepoint

Siguiendo con la temática acerca de dar una introducción al desarrollo en Sharepoint, veremos los mecanismos que disponemos para capturar algunos de lo eventos producidos en los elementos de sharpeoint.

Los desarrolladores disponemos de los Event Receivers, que consisten en artefactos configurados para capturar eventos acera de modificaciones producidas en listas (o bibliotecas), elementos de lista y features.

Todos los event receivers deben heredar de una clase base y sobreescribir los oventos a capturar. Para registrar estos eventos se tendrá que realizar o bien por programación o bien mediante una feature. Aunque podremos utilizar herramientas como “Event handler explorer” que nos permitirá asociar nuestros event receiver’s sobre librerías o documentos ya creadas sin tener que desplegar features ni escribir código.

SPItemEventReceiver

Será la clase base que utilizaremos para capturar los eventos relacionados con modificaciones sobre los elementos de una lista o biblioteca. Si utilizamos las extensiones VSEWSS podremos añadir un elemento del tipo “Event Receiver” a nuestro proyecto, esto nos creará los ficheros y definiciones para sobreescribir los eventos que necesitemos.

Para capturar los eventos sobreescribiremos los métodos que nos proporciona esta clase:

public virtual void ContextEvent(SPItemEventProperties properties);
public virtual void ItemAdded(SPItemEventProperties properties);
public virtual void ItemAdding(SPItemEventProperties properties);
public virtual void ItemAttachmentAdded(SPItemEventProperties properties);
public virtual void ItemAttachmentAdding(SPItemEventProperties properties);
public virtual void ItemAttachmentDeleted(SPItemEventProperties properties);
public virtual void ItemAttachmentDeleting(SPItemEventProperties properties);
public virtual void ItemCheckedIn(SPItemEventProperties properties);
public virtual void ItemCheckedOut(SPItemEventProperties properties);
public virtual void ItemCheckingIn(SPItemEventProperties properties);
public virtual void ItemCheckingOut(SPItemEventProperties properties);
public virtual void ItemDeleted(SPItemEventProperties properties);
public virtual void ItemDeleting(SPItemEventProperties properties);
public virtual void ItemFileConverted(SPItemEventProperties properties);
public virtual void ItemFileMoved(SPItemEventProperties properties);
public virtual void ItemFileMoving(SPItemEventProperties properties);
public virtual void ItemUncheckedOut(SPItemEventProperties properties);
public virtual void ItemUncheckingOut(SPItemEventProperties properties);
public virtual void ItemUpdated(SPItemEventProperties properties);
public virtual void ItemUpdating(SPItemEventProperties properties);

Los tres métodos básicos que utilizaremos serán: ItemAdding, ItemUpdating, ItemDeleting 

ItemAdding: Se llama antes de que el elemento se añada a la lista. Lo utilizaremos para validar la entrada. Aunque lo recomendable es utilizar Workflows para manejar la funcionalidad, es preferible manejar este evento para impedir entradas de datos no deseadas.

ItemUpdating: Lo utilizaremos para controlar los cambios producidos sobre un elemento. Podremos impedir el cambio de determinadas columnas con el uso de las propiedades BeginProperties y AfterProperties.

ItemDeleting: Podremos impedir el borrado de los elementos que no cumplan con las especificaciones funcionales.

 

En el siguiente ejemplo veremos como manejar los eventos sobre una lista con el contentype “Empleado” de ejemplo utilizado en el post “Implementación de Contentypes”:

public class EventReceiver1ItemEventReceiver : SPItemEventReceiver
{

        public override void ItemAdding(SPItemEventProperties properties)
        {
            using (SPWeb currentWeb = properties.OpenWeb())
            {
                string numeroEmpeladoInternalName = currentWeb.Lists[properties.ListId].Fields["Numero Empleado"].InternalName;
                if (!string.IsNullOrEmpty((string)properties.AfterProperties[numeroEmpeladoInternalName]))
                {
                    properties.ErrorMessage = "El código de empleado es autonumérico";
                    properties.Status = SPEventReceiverStatus.CancelWithError;
                }
            }
        }

        public override void  ItemAdded(SPItemEventProperties properties)
        {
            DisableEventFiring();
            try{
                properties.ListItem["Numero Empleado"] = DateTime.Now.ToString("yyyyMMddhhmmss");
                properties.ListItem.Update();
            }
            finally
            {
                EnableEventFiring();
            }
        }   

        public override void ItemUpdating(SPItemEventProperties properties)
        {
            string numeroEmpeladoInternalName = properties.ListItem.ParentList.Fields["Numero Empleado"].InternalName;

            if(string.IsNullOrEmpty((string)properties.AfterProperties[numeroEmpeladoInternalName]))
            {
                properties.ErrorMessage = "El código de empleado no es editable";
                properties.Status = SPEventReceiverStatus.CancelWithError;
            }

            if (properties.ListItem["Numero Empleado"].ToString() != properties.AfterProperties[numeroEmpeladoInternalName].ToString())
            {
                properties.ErrorMessage = "El código de empleado no es editable";
                properties.Status = SPEventReceiverStatus.CancelWithError;
            }
        }

        public override void ItemDeleting(SPItemEventProperties properties)
        {
            properties.ErrorMessage = "No se pueden eliminar empleados de la lista";
            properties.Status = SPEventReceiverStatus.CancelWithError;
        }  
    }

 

Destacamos del ejemplo al menos dos cosas:

  • Para acceder a las propiedades de la lista hemos tenido que utilizar el método “OpenWeb” de SPItemEventProperties, ya que aunque podríamos haber utilizado properties.ListItem.ParentList , en el evento ItemAdding la propiedad  es null ya que todavía no se ha creado el elemento.
  • Para cancelar el evento utilizaremos la propiedad “Status” y para mostrar un mensaje con el motivo “ErrorMessage”.
  • Para utilizar las propiedad AfterProperties tendremos que utilizar el “internal name” de la columna en la lista !!!, suele ser un error habitual el utilizar el display name.
  • El método DiableEventFiring para desactivar el event receiver al modificar el elemento. Para volverlo a activar hemos utilizado EnableEventFiring dentro de un bloque try-finally.

 

SPListEventReceiver

Podremos capturar los eventos producidos al realizar modificaciones sobre la estructura de una instancia de una lista o biblioteca. Es decir, podremos manejar los eventos producidos al añadir una nueva columna, …

Los métodos que podremos sobreescribir corresponden con:

public virtual void FieldAdded(SPListEventProperties properties);
public virtual void FieldAdding(SPListEventProperties properties);
public virtual void FieldDeleted(SPListEventProperties properties);
public virtual void FieldDeleting(SPListEventProperties properties);
public virtual void FieldUpdated(SPListEventProperties properties);
public virtual void FieldUpdating(SPListEventProperties properties);

 

Este tipo de eventos los utilizaremos sobre todo con CustomFields que necesiten de refrescar su definición al asignarse a una lista.

 

SPFeatureReceiver

Con esta clase capturaremos los eventos producidos sobre una feature. Es muy útil cuando queremos provisionar el site donde se haya activa la feature.

public abstract void FeatureActivated(SPFeatureReceiverProperties properties);
public abstract void FeatureDeactivating(SPFeatureReceiverProperties properties);
public abstract void FeatureInstalled(SPFeatureReceiverProperties properties);
public abstract void FeatureUninstalling(SPFeatureReceiverProperties properties);

 

Por ejemplo:

 

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
            try
            {
                SPWeb web = (SPWeb)(properties.Feature.Parent);

                // Provisión de seguridad
                ProvisionSecurity(web);

                // Provisión de formularios a la lista
                ProvisionForms(web);

                // Provisión de navegación
                UpdateQuickLaunch(web);

                // Enlaza el WF con la lista
                ProvisionWorkflow(web);
            }
            catch { }
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
            try
            {
                SPWeb web = (properties.Feature.Parent as SPSite).RootWeb;

                // Provisión de navegación
                DeleteQuickLaunch(web);
            }
            catch { }
}

No hay comentarios: