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:
- Crear una clase que herede de la clase SPItemEventReceiver
- 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.
- 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.
- 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
- 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.
- 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
- 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
Comparte este post: