Escenario:

Supongamos que tenemos una lista, llamémosla “fuente”, de SharePoint 2010 y queremos que al insertar o actualizar un elementos, se registre un log o un histórico en otra lista, llamémosla “destino”, es decir, insertar un registro en la otra lista con los datos que necesitemos. Para realizar esta operación deberíamos programar un EventReceiver y asignar nuestro código a los eventos deseado, ItemAdding, ItemAdded, ItemUpdating o ItemUpdated según sea el caso de nuestro escenario.

Solución

Tal y como ya avancé, tenemos que programar un EventReceiver asociado a la lista “fuente” y el evento deseado (Para el ejemplo he escogido ItemAdded), que se produce al insertar un nuevo registro. Lo que vamos a hacer es hacer una copia del registro insertado en la lista origen e insertarlo dentro de la lista destino.

   1: public override void ItemUpdated(SPItemEventProperties properties)

   2: {

   3:     // Obtener lista destino desde las propiedades del evento

   4:     SPList targetList = properties.Web.Lists["TargetList"];

   5:  

   6:     // Antes que nada, debemos comrobar si hemos conseguido obtener la lista 

   7:     if (targetList != null)

   8:     {

   9:         // Obtenemos el elemento que acabamos de insertar

  10:         SPListItem currentItem = properties.ListItem;

  11:  

  12:         // Instanciamos un nuevo elemento de la lista de destino

  13:         SPListItem targetItem = targetList.Items.Add();

  14:  

  15:         // Recorremos la lista de campos del elemento insertado para hacer una copia

  16:         foreach (SPField field in currentItem.Fields)

  17:         {

  18:             // Si la lista destino contiene el campo actual y el campo no es de sólo lectura

  19:             if (targetItem.Fields.Contains(field.Id) && !targetItem.Fields[field.Id].ReadOnlyField)

  20:             {

  21:                 // Establecemos el valor del campo

  22:                 targetItem[field.Id] = currentItem[field.Id];

  23:             }

  24:         }

  25:         // Se actualiza el registro en la lista para que los cambios tengan efecto

  26:         checkInItem.Update();

  27:     }

  28: }

 

Posible problema

Hasta aquí todo debería ir bien pero, ¿qué ocurre si no tenemos permisos en la lista destino? Por lo general un usuario estándar no debería tener permisos en el “histórico” de un proceso y lo que ocurre es que este código se ejecuta en nombre del usuario que realiza la operación, es decir, el usuario logueado en el sistema y, más aún, el parámetro SPItemEventProperties properties que recibe el evento está compuesto por los elementos para los que tiene permisos el usuario actual, con lo que la lista destino no estaría presente, con lo que no sólo no se podría insertar el elemento, sino que ni siquiera se podría obtener la lista destino.

 

Solución

La solución pasa por ejecutar el código mediante RunWithElevatedPrivileges y dentro del código instanciar tanto la colección de sitios como el sitio en cuestión de forma tradicional

   1: public override void ItemUpdated(SPItemEventProperties properties)

   2: {

   3:     // Comenzamos el bloque de ejecución con privilegios elevados

   4:     SPSecurity.RunWithElevatedPrivileges(delegate()

   5:     {

   6:         // Instanciar la colección de sitios con el Id que tenemos en properties 

   7:         using (SPSite mySite = new SPSite(properties.SiteId))

   8:         {

   9:             // Instanciar el sitio donde se encuentra la lista origen gracias a la URL que tenemos en properties

  10:             using (SPWeb myWeb = mySite.OpenWeb(properties.RelativeWebUrl))

  11:             {

  12:                 // Obtenemos la lista desde la colección de listas del sitio

  13:                 SPList checkInList = myWeb.Lists["TargetList"]; 

  14:             

  15:                 // Antes que nada, debemos comrobar si hemos conseguido obtener la lista 

  16:                 if (targetList != null)

  17:                 {

  18:                     // Obtenemos el elemento que acabamos de insertar

  19:                     SPListItem currentItem = properties.ListItem;

  20:             

  21:                     // Instanciamos un nuevo elemento de la lista de destino

  22:                     SPListItem targetItem = targetList.Items.Add();

  23:             

  24:                     // Recorremos la lista de campos del elemento insertado para hacer una copia

  25:                     foreach (SPField field in currentItem.Fields)

  26:                     {

  27:                         // Si la lista destino contiene el campo actual y el campo no es de sólo lectura

  28:                         if (targetItem.Fields.Contains(field.Id) && !targetItem.Fields[field.Id].ReadOnlyField)

  29:                         {

  30:                             // Establecemos el valor del campo

  31:                             targetItem[field.Id] = currentItem[field.Id];

  32:                         }

  33:                     }

  34:                     // Se actualiza el registro en la lista para que los cambios tengan efecto

  35:                     checkInItem.Update();

  36:                 }

  37:             }

  38:         }

  39:     });

  40: }

 

Como podéis ver, se instancian de una forma tradicional tanto la colección de sitios como el sitio donde se encuentran las listas. De esta forma, obtendremos esos elementos y sus propiedades dentro del contexto de privilegios elevados gracias a que se encuentran dentro del bloque RunWithElevatedPrivileges y por lo tanto, sí tendremos acceso a la lista destino. Finalmente, el resto del código es exactamente igual al inicial.