Modificar el comportamiento de redirección de los formularios NewForm.aspx y EditForm.aspx (II)

En este segundo artículo de la serie, vamos a estudiar con ejemplos de código la forma de realizar la redirección de los formularios NewForm.aspx y EditForm.aspx.

La situación actual es la siguiente:



  • NewForm.aspx  => Al guardar los datos => Va a la página de AllItems.aspx
  • EditForm.aspx => Al guardar los datos => Va a la página de AllItems.aspx

Mientras que la situación que deseamos alcanzar es la siguiente:


  • NewForm.aspx  => Al guardar los datos queremos que vaya al => DispForm.aspx?ID=xx del elemento que se acaba de crear
  • EditForm.aspx?ID=xx => Al guardar los datos queremos que vaya al =>DispForm.aspx? ID=xx del elemento que se acaba de editar

La solución que utilizaremos será implementar un manejador de eventos, que capture el evento síncrono de ItemAdding /ItemUpdating, realice la adición/actualización de este ítem en la lista y por último realice la redirección a una determinada dirección, pero conociendo ya el ID que se le ha asignado a este ítem.


Para ello será necesario:


  1. Crear una clase que herede de la clase SPItemEventReceiver
  2. Sobreescribir los métodos ItemAdding y ItemUpdating de dicha clase.

    • Obtener la referencia a la lista y al SPListItem con el que se va a trabajar
    • Deshabilitar el disparo de nuevos eventos
    • Añadir/actualizar el item en la lista
    • Volver a habilitar el disparo de eventos
    • Realizar la redirección
    • Tratar las excepciones.

  3. Para el tratamiento de la excepción se ha creado una clase nueva, TraceProvider, siguiendo las indicaciones proporcionadas por este enlace y este otro enlace, para poder escribir las correspondientes entradas en el archivo de Log de SharePoint.

Debido a que la redirección implica abortar el hilo (Thread) en el que se esta ejecutando el código y pasar el control a una nueva página, es necesario poner en el último lugar el código de la redirección, ya que el código que se ponga posteriormente a la llamada a la función de redirección (SPUtility.Redirect()), no se ejecutará nunca, así como tratar la excepción ThreadAbortException que se producirá al ser abortado el hilo (Thread) como consecuencia de la llamada a esta función.


El ejemplo de código que vamos a utilizar, está parcialmente basado en el excelente post de Eric Bartels


Crear una clase que herede de la clase SPItemEventReceiver


Es necesario crear nuestro propio manejador de eventos que herede de la clase SPItemEventReceiver, para poder ser capaces de insertar nuestro código y realizar la redirección en el evento síncrono ItemAdding y en el evento síncrono ItemUpdating.






using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using VSeWSS;
using System.Web;
using Microsoft.SharePoint.Utilities;
using System.Threading;
using System.Reflection;

namespace Ciin.Demo.EventReceivers
{
  public class RedirectFormItemEventReceiver : SPItemEventReceiver
  {
  }
}


Obtener el contexto


Necesitamos conocer el contexto de la petición HTTP, para poder realizar la redirección correctamente.





        private HttpContext _currentcontext = null;

        public RedirectFormItemEventReceiver():base()
        {
            if (null != HttpContext.Current)
            {
                _currentcontext = HttpContext.Current;
            }
        }


Sobreescribir el método ItemAdding


Al sobreescribir este método, lograremos insertar la lógica que deseamos que se dispare cuando un usuario esta añadiendo (ItemAdding) un nuevo item a la lista, pero antes de que haya sido definitivamente añadido (ItemAdded)


Para ello:



  • obtenemos a partir de las propiedades del evento, cual es la lista que ha disparado el evento

    • SPList list = site.List[properties.ListId];

  • deshabilitamos el disparo de eventos para poder añadir un nuevo elemento sin que se vuelva a disparar el manejador.

    • DisableEventFiring();

  • obtenemos una instancia del objeto SPListItem que deseamos añadir a la lista

    • SPListItem itemToAdd = list.Items.Add();

  • realizamos distintas comprobaciones y en el caso de que sea un campo de tipo fecha comprobamos si el valor es nulo o esta vacio antes de añadir para evitar que nos dispare una excepción de tipo FormatException.
  • añadimos el item a la lista

    • itemToAdd[field.InternalName] = properties.AfterProperties[field.Internal.Name];
    • itemToAdd.Update();

  • un caso importante que hay que tratar es cuando se quiere añadir ficheros adjuntos a la lista.

    • Primero obtenemos los ficheros del objeto petición (Request)

      • HttpFileCollection miHFileCol = _currentcontext.Request.Files;

    • Obtenemos el stream de bytes de cada fichero y lo almacenamos en un array de bytes

      • Stream miStream = miHFileCol[key].InputStream;
      • miStream.Seek(0, SeekOrigin.Begin);  //Muy importante esta linea para inicializar el stream correctamente
      • int n = miStream.Read(input, numBytesRead, numBytesToRead);

    • Añadimos el fichero como un fichero adjunto al item

      • itemToAdd.Attachments.Add(fileName, input);

  • volvemos a habilitar el disparo de eventos

    • EnableEventFiring();

  • obtenemos la dirección del formulario de DisplayForm al que queremos redirigir

    • targetUrlOfNewItem = site.Url + list.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + “?ID=” + itemToAdd.ID;

  • hacer limpieza de las instancias de SPSite y SPWeb utilizadas
  • realizamos la redirección

    • SPUtility.Redirect(targetUrlOfNewItem, SPRedirectFlags.Default, _currentcontext);

  • realizamos el tratamiento de excepciones, en especial de la ThreadAbortException que se nos va a disparar.

CODIGO FUENTE:






     public override void ItemAdding(SPItemEventProperties properties)
        {
            SPListItem itemToAdd = null;
            SPSite siteColl = null;
            SPWeb site = null;
            bool bDoRedirection = true;


            try
            {
                //Obtener una referencia de la lista
                siteColl = new SPSite(properties.SiteId);
                site = siteColl.OpenWeb(properties.RelativeWebUrl);
                SPList list = site.Lists[properties.ListId];

                //Deshabilitar el disparo de eventos para poder añadir un nuevo elemento sin disparar el manejador
                DisableEventFiring();


                //Añadir el item y rellenarlo con los valores de las propiedades
                itemToAdd = list.Items.Add();
                foreach (SPField field in itemToAdd.Fields)
                {
                    // Comprobar que el campo no esta oculto, es de solo lectura o es un adjunto
                    if (!field.Hidden && !field.ReadOnlyField && field != null && field.InternalName != “Attachments”)
                    {
                        // Comprobar si el campo es de tipo fecha o no
                        if (field.Type != SPFieldType.DateTime)
                        {
                            itemToAdd[field.InternalName] = properties.AfterProperties[field.InternalName];
                        }
                        else
                        {
                            string valorCampo = (string)properties.AfterProperties[field.InternalName];
                            if (!String.IsNullOrEmpty(valorCampo))
                            {
                                itemToAdd[field.InternalName] = properties.AfterProperties[field.InternalName];
                            }
                        }
                    }


                    else    // Para el caso de que tengamos ficheros adjuntos
                    {
                        if (field.InternalName == “Attachments”)
                        {


                             //Obtenemos la coleccion con los ficheros adjutnos
                             HttpFileCollection miHFileCol = _currentcontext.Request.Files;
                             if (miHFileCol.Count != 0)
                             {
                                 foreach (String key in miHFileCol.AllKeys)
                                 {


                                      // Leemos cada fichero adjunto
                                      int numBytesToRead = miHFileCol[key].ContentLength;
                                      if (numBytesToRead > 0 && (!String.IsNullOrEmpty(miHFileCol[key].FileName)))
                                      {
                                          string fileName = Path.GetFileName(miHFileCol[key].FileName);
                                          byte[] input = new byte[numBytesToRead];
                                          Stream miStream = miHFileCol[key].InputStream;
                                          miStream.Seek(0, SeekOrigin.Begin);    //Muy importante esta linea
                                          int numBytesRead = 0;
                                          while (numBytesToRead > 0)
                                          {
                                               int n = miStream.Read(input, numBytesRead, numBytesToRead);
                                               if (n == 0)
                                                    break;
                                               numBytesRead += n;
                                               numBytesToRead -= n;
                                          }
                                          miStream.Close();
                                          // Añadir fichero como adjunto al item
                                          itemToAdd.Attachments.Add(fileName, input);
                                      }
                                 } // cierre del foreach de los ficheros
                            }
                        }
                    } // cierre del else para los attachments
                } // cierre del foreach de los campos itemToAdd.Fields


                //Añadir el item a la lista
                itemToAdd.Update();


                // Volver a habilitar el disparo de eventos
                EnableEventFiring();


                //Obtener la direccion de la pagina DispForm.aspx
                string targetUrlOfNewItem;
                targetUrlOfNewItem = site.Url + list.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + “?ID=” + itemToAdd.ID;

                if (bDoRedirection)
                {
                    //Hacer limpieza
                    if (site != null)
                        site.Dispose();
                    if (siteColl != null)
                        siteColl.Dispose();
                }

                //Redirigir a la pagina DispForm.aspx
                SPUtility.Redirect(targetUrlOfNewItem, SPRedirectFlags.Default, _currentcontext);
            }


            //Tratar las excepciones
            catch (ThreadAbortException ex)
            {
                //Escribir en Log de errores de SharePoint
                TraceProvider.RegisterTraceProvider();
                TraceProvider.WriteTrace(TraceProvider.TagFromString(“CIIN”), TraceProvider.TraceSeverity.Exception, Guid.NewGuid(), Assembly.GetExecutingAssembly().FullName, “RedirectFormHandler”, “TreadAbortException”, ex.Message + ex.StackTrace);
                TraceProvider.UnregisterTraceProvider();
            }
            catch (Exception ex)
            {
                //Cancelar la operacion
                if (itemToAdd != null && itemToAdd.ID != 0)
                {
                    itemToAdd.Delete();
                }
                properties.ErrorMessage = “Accion cancelada debido a una excepcion – Para obtener mas informacion mirar en archivo de LOG de SharePoint”;
                properties.Status = SPEventReceiverStatus.CancelWithError;
                properties.Cancel = true;


                //Escribir en Log de errores de SharePoint
                TraceProvider.RegisterTraceProvider();
                TraceProvider.WriteTrace(TraceProvider.TagFromString(“CIIN”), TraceProvider.TraceSeverity.Exception, Guid.NewGuid(), Assembly.GetExecutingAssembly().FullName, “RedirectFormHandler”, “Exception”, ex.Message + ex.StackTrace);
                TraceProvider.UnregisterTraceProvider();

            }
            finally
            {
                //Hacer limpieza de las instancias de SPSite y SPWeb utilizadas
                if (site != null)
                    site.Dispose();
                if (siteColl != null)
                    siteColl.Dispose();
            }
        }


Sobreescribir el método ItemUpdating


Al sobreescribir este método, lograremos insertar la lógica que deseamos que se dispare cuando un usuario esta modificando (ItemUpdating) un item de la lista, pero antes de que haya sido definitivamente modificado (ItemUpdated)

Para ello:


  • obtenemos a partir de las propiedades del evento, cual es la lista que ha disparado el evento

    • SPList list = site.List[properties.ListId];

  • deshabilitamos el disparo de eventos para poder añadir un nuevo elemento sin que se vuelva a disparar el manejador.

    • DisableEventFiring();

  • obtenemos una instancia del objeto SPListItem que deseamos actualizar

    • SPListItem itemToUpdate = list.Items.GetItemById(properties.ListItemId);

  • realizamos distintas comprobaciones y en el caso de que sea un campo de tipo fecha comprobamos si el valor es nulo o esta vacio antes de actualizarlo para evitar que nos dispare una excepción de tipo FormatException.
  • actualizamos el item en la lista

    • itemToUpdate[field.InternalName] = properties.AfterProperties[field.Internal.Name];
    • itemToUpdate.Update();

  • un caso importante que hay que tratar es cuando se quiere añadir ficheros adjuntos a la lista.

    • Primero obtenemos los ficheros del objeto petición (Request)

      • HttpFileCollection miHFileCol = _currentcontext.Request.Files;

    • Obtenemos el stream de bytes de cada fichero y lo almacenamos en un array de bytes

      • Stream miStream = miHFileCol[key].InputStream;
      • miStream.Seek(0, SeekOrigin.Begin);  //Muy importante esta linea para inicializar el stream correctamente
      • int n = miStream.Read(input, numBytesRead, numBytesToRead);

    • Añadimos el fichero como un fichero adjunto al item

      • itemToUpdate.Attachments.Add(fileName, input);

  • volvemos a habilitar el disparo de eventos

    • EnableEventFiring();

  • obtenemos la dirección del formulario de DisplayForm al que queremos redirigir

    • targetUrlOfNewItem = site.Url + list.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + “?ID=” + itemToUpdate.ID;

  • hacer limpieza de las instancias SPSite y SPWeb utilizadas
  • realizamos la redirección

    • SPUtility.Redirect(targetUrlOfNewItem, SPRedirectFlags.Default, _currentcontext);

  • realizamos el tratamiento de excepciones, en especial de la ThreadAbortException que se nos va a disparar.

CODIGO FUENTE:






       public override void ItemUpdating(SPItemEventProperties properties)
        {
            SPListItem itemToUpdate = null;
            SPSite siteColl = null;
            SPWeb site = null;
            bool bDoRedirection = true;

            try
            {
                //Obtener una referencia a la lista
                siteColl = new SPSite(properties.SiteId);
                site = siteColl.OpenWeb(properties.RelativeWebUrl);
                SPList list = site.Lists[properties.ListId];

                //Añadir el item y rellenarlo con los valores de las propiedades
                DisableEventFiring();
                itemToUpdate = list.Items.GetItemById(properties.ListItemId);

                foreach (SPField field in itemToUpdate.Fields)
                {
                    //Comprobar que el campo no esta oculto, es de solo lectura o es un adjunto
                    if (!field.Hidden && !field.ReadOnlyField && field != null && field.InternalName != “Attachments”)
                    {
                        //Comprobar si el campo es de tipo fecha o no
                        if (field.Type != SPFieldType.DateTime)
                        {
                            itemToUpdate[field.InternalName] = properties.AfterProperties[field.InternalName];
                        }
                        else
                        {
                            string valorCampo = (string)properties.AfterProperties[field.InternalName];
                            if (!String.IsNullOrEmpty(valorCampo))
                            {
                                itemToUpdate[field.InternalName] = properties.AfterProperties[field.InternalName];
                            }
                        }
                    }


                    else    // Para el caso de que tengamos ficheros adjuntos
                    {
                        if (field.InternalName == “Attachments”)
                        {

                             //Obtenemos la coleccion con los ficheros adjutnos
                             HttpFileCollection miHFileCol = _currentcontext.Request.Files;
                             if (miHFileCol.Count != 0)
                             {
                                 foreach (String key in miHFileCol.AllKeys)
                                 {

                                      // Leemos cada fichero adjunto
                                      int numBytesToRead = miHFileCol[key].ContentLength;
                                      if (numBytesToRead > 0 && (!String.IsNullOrEmpty(miHFileCol[key].FileName)))
                                      {
                                          string fileName = Path.GetFileName(miHFileCol[key].FileName);
                                          byte[] input = new byte[numBytesToRead];
                                          Stream miStream = miHFileCol[key].InputStream;
                                          miStream.Seek(0, SeekOrigin.Begin);    //Muy importante esta linea
                                          int numBytesRead = 0;
                                          while (numBytesToRead > 0)
                                          {
                                               int n = miStream.Read(input, numBytesRead, numBytesToRead);
                                               if (n == 0)
                                                    break;
                                               numBytesRead += n;
                                               numBytesToRead -= n;
                                          }
                                          miStream.Close();
                                          // Añadir fichero como adjunto al item
                                          itemToUpdate.Attachments.Add(fileName, input);
                                      }
                                 } // cierre del foreach de los ficheros
                            }
                        }
                    } // cierre del else para los attachments
                } // Cierre del foreach de los campos itemToUpdate.Fields
                itemToUpdate.Update();
                EnableEventFiring();

                //Obtener direccion de la pagina DispForm.aspx
                string targetUrlOfNewItem;
                targetUrlOfNewItem = site.Url + list.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + “?ID=” + itemToUpdate.ID;

                if (bDoRedirection)
                {
                    //Hacer limpieza
                    if (site != null)
                        site.Dispose();
                    if (siteColl != null)
                        siteColl.Dispose();
                }

                //Redirigir a la pagina DispForm.aspx
                SPUtility.Redirect(targetUrlOfNewItem, SPRedirectFlags.Default, _currentcontext);
            }
            catch (ThreadAbortException ex)
            {
                //Escribir en Log de errores de SharePoint
                TraceProvider.RegisterTraceProvider();
                TraceProvider.WriteTrace(TraceProvider.TagFromString(“CIIN”), TraceProvider.TraceSeverity.Exception, Guid.NewGuid(), Assembly.GetExecutingAssembly().FullName, “RedirectFormHandler”, “TreadAbortException”, ex.Message + ex.StackTrace);
                TraceProvider.UnregisterTraceProvider();
            }
            catch (Exception ex)
            {
                //Cancelar la operacion
                properties.ErrorMessage = “Accion cancelada debido a una excepcion – Para obtener mas informacion mirar en archivo de LOG de SharePoint”;
                properties.Status = SPEventReceiverStatus.CancelWithError;
                properties.Cancel = true;

                //Escribir en Log de errores de SharePoint
                TraceProvider.RegisterTraceProvider();
                TraceProvider.WriteTrace(TraceProvider.TagFromString(“CIIN”), TraceProvider.TraceSeverity.Exception, Guid.NewGuid(), Assembly.GetExecutingAssembly().FullName, “RedirectFormHandler”, “Exception”, ex.Message + ex.StackTrace);
                TraceProvider.UnregisterTraceProvider();

            }
            finally
            {
                //Hacer limpieza
                if (site != null)
                    site.Dispose();
                if (siteColl != null)
                    siteColl.Dispose();
            }
        }


 


En el siguiente artículo de esta serie mostraremos como asociar este manejador de eventos con una lista concreta.


Enlaces recomendados y sobre los que se ha basado parcialmente este ejemplo:

http://www.entwicklungsgedanken.de/2008/03/27/redirecting-from-newformaspx-to-dispformaspx-after-creating-a-new-item/


http://scothillier.spaces.live.com/blog/cns!8F5DEA8AEA9E6FBB!236.entry

Un comentario en “Modificar el comportamiento de redirección de los formularios NewForm.aspx y EditForm.aspx (II)”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *