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.