martes, 21 de julio de 2009

Establecer permisos de Item en un receptor de eventos programaticamente parte 1

Supongamos que es necesario que cada item de una lista tenga permisos distintos dependiendo de campos especificos que el usuario llena a la hora de crear un item, por ejemplo en una lista de tareas donde queramos que las tareas sean visibles para todos si no estan asignadas a nadie, per que solo un grupo en especifico pueda editarlas, para esto podemos ayudarnos de un receptor de eventos que lea esta informacion y modifique los permisos del item de acuerdo al requerimiento.


El primer paso es el crear un receptor de eventos, puedes ayudarte con las extensiones para Visual Studio que cuentan con una plantilla para la generación de receptores de eventos, esta plantilla no es visible en la creacion de una solucion, solo esta disponible cuando agregas un elemento nuevo a la solucion ya existente. No entrare en detalles en este tema a menos que les interese en cuyo caso dejen sus comentarios.

Sea cual sea el metodo por el que creas el receptor de eventos este debe atrapar el evento ItemAdded, si utilizaste las extensiones solo debes de quitar los tags de comentarios ("//"), ojo, no quites los tags de la descripcion del evento ("///").

Ahora debemos ejecutar el codigo con privilegios elevados, ya explicare la razon de esto mas adelante:

SPSecurity.RunWithElevatedPrivileges(delegate() { using (SPSite miSitio = new SPSite(properties.SiteId)) { using (SPWeb miWeb = miSitio.OpenWeb(properties.WebUrl.Replace(miSitio.Url,""))) {

Con esto lo que hacemos es delegar todo nuestro codigo para ejecutarse con privilegios elevados, en base a estos privilegios obtenemos los objetos SPSite y SPWeb correspondientes al contexto en el que se ejecuto el evento, es muy importante que al crear el sitio lo hagan haciendo referencia al ID del sitio (properties.SiteId) y no lo tomen el sitio del contexto o el sitio como tal, ya que estos incluyen las credenciales del usuario actual; en caso de el SPWeb si solo utilizamos el metodo OpenWeb sin mandarle parametros tambien tendremos problemas con el tipo de credenciales que manejan, es por esto que se le envia la direccion relativa del subsitio actual (por eso se elimina el inicio de WebUrl ya que este incluye la direccion completa del subsitio).

Ahora que tenemos los componentes principales, podemos emepzar a obtener los datos que necesitamos:

miWeb.AllowUnsafeUpdates = true;
SPRoleDefinition RoleDefinition = miWeb.RoleDefinitions.GetByType(SPRoleType.Contributor);
SPListItem miItemActual = miWeb.Lists[properties.ListId].GetItemById(properties.ListItemId); miItemActual.BreakRoleInheritance(true);
miWeb.AllowUnsafeUpdates = true;
while (miItemActual.RoleAssignments.Count > 0)
{
miItemActual.RoleAssignments.Remove(0);
}

Explicando el codigo anterior lo que hacemos es permitir las actualizaciones en la web que obtuvimos (que es un requisito para ejecutar BreakRoleInheritance), despues obtenemos la definicion de rol de Contribuir, puedes cambiar este usando el enum SPRoleType, despues obtenemos el item asociado al evento (ojo aqui vuelvo a hacer referencia al ID de la lista y el item para obtenerlo, si haces referencia directa tendras problemas en el codigo).

En la siguiente parte explicare como crear definiciones de rol de uso.

Error con Flujos de Trabajo recursivos con SharePoint Designer

Microsoft ha dado una explicación oficial a lo que muchos ya sabíamos, a partir del Service Pack 2 para Windows SharePoint Services y Microsoft Office SharePoint Server los flujos recursivos ya no son permitidos.

Impacto

Principalmente los flujos de trabajo que se ven a
  • Si creas un flujo de trabajo que envíe una alerta por correo electrónico periódicamente para tareas comunes (respaldos manuales de listas, depuración, informes de uso, etc). Estos flujos no tienen un fin definido, pero pude que el requerimiento este justificado.
  • Flujos de trabajo que se ejecutan mientras se cumpla una condición especifica (por ejemplo siempre que un elemento muestre un rendimiento debajo del 70%).
  • Flujos que simulen un State Machine Workflow, ya que para mover el flujo entre cada estado se ejecuta una nueva instancia del mismo. (pueden ver un ejemplo de este en un post de Adnan Farooq Hashmi en ingles).
Justificación

La razón de ser de esta restricción según el anuncio de Microsoft es el problema que al crear un flujo de trabajo que se dispare cuando se modifique un elemento y este mismo flujo realize una molificación al elemento creara un ciclo infinito que muchas veces no es la intención del usuario, pero si admiten que puede ser un requerimiento valido realizar dicho flujo, por lo que proponen una solución a este tipo de requerimientos.

Solución

Existen dos soluciones, o crear el flujo de trabajo por medio de Visual Studio.

La segunda opción es utilizar 2 flujos de trabajo para evitar la restricción impuesta, esto es algo que si bien no complicado se vislumbra laborioso, especialmente para aquellos que tienen muchos flujos de este tipo, demasiado largos o complejos, puesto que necesitan recrear el mismo flujo, así cada uno de ellos se quedara "estancado" en un ciclo, pero el flujo espejo podrá avanzar en su lugar. Para implementar esto se puede copiar el flujo de trabajo pero al intentar implementar el mismo flujo dos veces entran en conflicto, aunque no he realizado pruebas exhaustivas en cuanto a este punto y tampoco he encontrado una herramienta que exista actualmente que te permita recrear el flujo cambiando los IDs y GUIDS asociados.

La ultima forma es un poco mas compleja apunta a que al terminar un flujo de trabajo inicializes el siguiente y viceversa, esto lo puedes hacer con ayuda de las custom activities que estan en codeplex, terminas un flujo "A" e inicializas el flujo "B" que a su vez volverá a iniciar "A".

Conclusiones

Las pruebas que he realizado hasta ahora indican que la segunda solución es la mas viable pero menos recomendada en términos de rendimiento y mejores practicas, la solución de visual studio esta siempre sujeta a los tiempos y complejidad del requerimiento y la ultima opción significa modificar el comportamiento del flujo, tal vez inclusive la estructura.

A mi parecer lo que hace Microsoft es volver SPD un producto mas seguro para usuarios finales o personas que no estén relacionadas muy a fondo con conceptos del desarrollo básicos como la recursividad. Este cambio responde al hecho de que un flujo de trabajo infinito puede llegar a consumir grandes recursos en el servidor, y al parecer es mucho mas común equivocarte y crear un flujo de este tipo sin querer a hacerlo con plena conciencia de lo que pretendes, lo cual representa un problema para aquellos que utilizábamos SPD para este tipo de trabajos debido a la rapidez con la que se genera un flujo.