Archivo

Posts Tagged ‘SP Object Model’

Cómo guardar contenido cuando el usuario no tiene permisos en una lista de SharePoint


Cómo guardar contenido cuando el usuario no tiene permisos en una lista

He observado con mi equipo de trabajo, colegas y sharepointeros en general que una de los problemas que tenemos cuando comenzamos a usar el modelo de objetos de SharePoint es el tema de la seguridad. De hecho a mí mismo me pasaba que tenía que crear un WebPart, el cual obtendría datos de una lista o biblioteca, los renderiza, y al final presenta alguna acción que causa una actualización en dicha fuente. Por supuesto, en ambiente de desarrollo todo jala de pelos, porque usamos la cuenta del administrador. Pero tan pronto publicamos para que el cliente pruebe, ¡boom! Un SecurityException es nuestra recompensa.

Por supuesto, el problema en estos casos suele ser que el usuario que ha ingresado no tiene permisos para acceder a una biblioteca. El caso suele ser sencillo: darle permisos a dicho usuario, pues si no los tiene, su razón ha de tener. En otras palabras, si el usuario no tiene permiso es por la lógica de seguridad implementada para la lista o biblioteca en cuestión.

Sin embargo hay ocasiones en las que el cliente quiere compartir sólo cierta información. Por ejemplo, imagina una lista donde se guarda información sobre los proveedores de una empresa y la tarifa que se les paga a cada uno por unidad de servicio. Por otro lado, el jefe de compras decide que quiere exponer una forma para que los usuarios de otras áreas busquen información de los proveedores, pero no quiere que la tarifa quede expuesta. Además, si el proveedor no buscado no existe, el usuario debería poder darlo de alta (nuevamente, sin capturar la tarifa). ¿Qué podemos hacer?

No podemos simplemente dar permisos de lectura a los usuarios generales porque entonces podrían visualizar la página AllItems.aspx de la lista y les mostraría la tarifa. Luego, lo que tenemos que hacer es un WebPart que consulte la lista y haga la búsqueda ahí, aunque el usuario autenticado no tenga permisos de lectura en la misma. Y lo mismo ocurre con el alta de un proveedor: el usuario deberá ingresar la información sin tener permisos para hacerlo.

Afortunadamente, el modelo de objetos de SharePoint nos proporciona una forma de hacer el “by-pass” de seguridad. La clase SPSecurity (ubicada en el espacio de nombres Microsoft.SharePoint) contiene un método llamado RunWithElevatedPrivileges. Éste toma un sólo parámetro: un delegado de un método sin parámetros que regresa void.

protected override void CreateChildControls()
{ 
    SPGridView myView = new SPGridView();
    ...

    SPSecurity.RunWithElevatedPrvileges( () => {

        // aunque el usuario autenticado no tenga
        // permisos para leer o modificar la lista "Proveedores",
        // al ejecutarse este delegado anónimo sí que
        // los tiene. 
        SPList list = SPContext.Current.Lists["Proveedores"];
        myView.DataSource = list.Items;
        myView.DataBind();
    });

    Controls.Add(myView);
}

El código anterior muestra la forma básica de utilizar dicho método. Como ves, le pasamos un método anónimo. Si el usuario que carga el WebPart no tiene permisos para ingresar, éste código le garantizará el pase. Lo mismo para actualizar datos.

protected void OnOK(object sender, EventArgs args)
{
    SPSecurity.RunWithElevatedPrivileges(() => {

        SPList list = SPContext.Current.Lists["Proveedor"];
        SPListItem item = list.Items[0];
        item["Título"] = "Nuevo título";
        item.Update();
    });
}

Ahora bien, hay que aclarar un asunto. SPSecurity.RunWithElevatedPrivileges no es que de un paso libre así tal cual. Lo que hace en realidad es darle los privilegios del usuario que corre el proceso. Así, si corremos el código anterior dentro de un WebPart, éste se ejecutará bajo los permisos que tenga la cuenta asociada al Application Pool (y usualmente será el administrador de la granja). Pero por otro lado, si corriésemos un código similar en, digamos, un temporizador de SharePoint o un Workflow, la cuenta que tendrá será la asociada a estos procesos (digamos, al servicio SPTimerv4).

Sin embargo, por lo anterior, hay que tener cuidado ya que no siempre funcionará. Si por ejemplo, ejecutamos desde un temporizador y la cuenta asociada a ésta no tiene permiso en el recurso que queremos consultar (i.e. la lista “Proveedor” en nuestro ejemplo) tendremos un SPException como si no tuviésemos permiso. Lo mismo ocurre si ejecutamos dicho código desde una aplicación (de consola o Windows Forms): el usuario que ejecuta la aplicación será quien deba tener permisos en la lista.

Así que ya sabes, no siempre será garantía su empleo. Sin embargo, para WebParts, páginas de aplicación y controles, siempre será una muy buena opción.

Leyendo columnas de búsqueda (Lookup) en SharePoint


Buen inicio de año a todos, feliz 2012, feliz nuevo b’ak’tun… una disculpa por mi silencio de noviembre y diciembre, pero bueno, ya saben: miles de proyectos, todo urge… yada yada yada. Pero ya vine con un post pequeño pero que es útil en tu programación para SharePoint run-of-the-mill.

Una característica padre que tiene SharePoint es que te permite crear campos de búsqueda en una lista cualquiera. Estos campos hacen referencia a campos dentro de otra lista o biblioteca, de tal suerte que permiten crear una relación padre/hijo.

Veamos un ejemplo jocoso. Supongamos que estamos creando un SharePoint para el colegio inglés Hogwarts. Pensemos que estamos armando un catálogo que contiene información sobre los cursos que daría el famoso auror Alastor Moody. Cada curso tiene asociados diversos temas, y cada tema tiene asociadas las preguntas del examen que el profesor practicará a los estudiantes.

image

Creamos nuestra lista de preguntas: una lista personalizada sencilla. Por ejemplo:

image

Posteriormente, creamos nuestra lista de temas. Pero cada tema debe tener asociada una o más preguntas, por lo que podemos sacar provecho el campo búsqueda: añadimos uno, le decimos que busque en la lista “Preguntas” y que permita valores múltiples. La siguiente imagen muestra cómo luce la lista cuando estamos creando un nuevo tema, y cómo se ve la vista de tabla.

image

image

Y algo muy similar para los cursos: creamos un campo de búsqueda y lo asociamos a los temas.

image

Ahora bien, como administradores eso está padre, y todo, pero nada nuevo bajo el sol. Lo interesante ocurre cuando tenemos que crear algún control en el cual tengamos que leer dicha información.

Afortunadamente SharePoint nos provee con una clase que nos hace la vida más fácil: SPFieldLookupValueCollection. Las columnas de búsqueda con opción múltiple (como la de Temas en la imagen anterior) pueden convertirse en esta clase, y ahí podemos iterar para obtener los objetos SPFieldLookupValue, los cuales cuentan con dos propiedades importantes: LookupId y LookupValue. De esta forma podemos buscar relaciones fácilmente.

SPWeb web = SPContext.Current.Web;

SPList cursos = web.List["Cursos"];
SPList temas = web.List["Temas"];

foreach (SPListItem curso in cursos.Items)
{
    Console.WriteLine("Curso: {0}", curso.DisplayName);

    var temasEnCursos = curso["Temas"] as SPFieldLookupValueCollection;
    foreach (var temaLookup in temasEnCursos)
    {
        var tema = temas.Items.GetItemById(temaLookup.LookupId);
        Console.WriteLine("Tema: {0}", tema.DisplayName);
    }
    Console.WriteLine("=====\n");
}

Dado que SPFieldLookupValueCollection hereda de List<SPFieldLookupValue>, tenemos disponible toda la funcionalidad de List<T>. Por ejemplo, podemos usar el método Find para obtener todos los elementos que comienzan con A:

SPWeb web = SPContext.Current.Web;

SPList cursos = web.List["Cursos"];
SPList temas = web.List["Temas"];

foreach (SPListItem curso in cursos.Items)
{
    Console.WriteLine("Curso: {0}", curso.DisplayName);

    var temasEnCursos = curso["Temas"] as SPFieldLookupValueCollection;
    var cursosFiltrados = temasEnCursos.Find(x => x.StartsWith("A"));

    foreach (var temaLookup in cursosFiltrados)
    {
        Console.WriteLine("Tema: {0}", temaLookup.LookupValue);
    }
}

Por otra parte, SPFieldLookupValueCollection tiene el método ToString sobrecargado. Al invocarlo, nos regresa una cadena de texto con el formato: ID;#Texto;ID;#Texto. Por ejemplo:

var temasEnCursos = curso["Temas"] as SPFieldLookupValueCollection;
Console.WriteLine(temasEnCursos.ToString());

Nos imprime algo como:

2;#Cruciatus;#3;#Imperio;#1;#Avada Kedavra

 

Si quisiéramos crear un nuevo objeto, podemos usar el constructor que toma un parámetro: una cadena con el formato anterior. Por ejemplo:

var texto = "2;#Cruciatus;#3;#Imperio;#1;#Avada Kedavra";
var nuevoTema = new SPFieldLookupValueCollection(texto);
curso["Temas"] = nuevoTema;
curso.Update();

Como podemos ver, es muy sencillo utilizar este tipo de campos en nuestros desarrollos para SharePoint. Y esto gana en la vida, porque usar este tipo de columnas nos permite establecer relaciones maestro-hijo de forma muy sencilla. La alternativa sería usar una tercera lista que enlace ambas entidades. Pero eso es muy de base de datos, y no tanto de SharePoint.

Tschüss!

Leer los valores de un campo de opción múltiple


SharePoint cuenta con los campos de opción múltiple, los cuales nos permiten seleccionar un valor a partir de una lista de valores predeterminados.

Cuando hacemos algún desarrollo, como –digamos- un WebPart, quizás querramos mostrar las opciones que presenta uno de estos campos. Es algo sencillo, pero pensé que sería bueno publicarlo de todas formas. Un “quickie”.

El campo de opción múltiple es representado por la clase SPFieldChoice. Y esta clase tiene una propiedad, Choices, la cual es una colección de cadena de texto. Soooo, easy peasy, sólo tenemos que conseguir una referencia a dicha clase.

SPWeb web = SPContext.Web;
SPList list = web.Lists["Mi Lista"];
SPFieldChoice field = list.Fields["Mi Campo"] as SPFieldChoice;

DropDownList list = new DropDownList();
list.DataSource = field.Choices;
list.DataBind();

El ejemplo anterior muestra cómo obtener dicha referencia. Si el campo Mi Campo es de opción múltiple, la conversión será un éxito. Luego, simplemente hacemos un enlazado de datos contra una lista desplegable de ASP.NET.

Easy peasy.