SharePoint 2010. Validación en encuestas

Una de las mejoras que SharePoint 2010 nos ofrece a nivel de listas, es la posibilidad de incluir validaciones de la información que los usuarios introducen. Esta lógica de validación se podría realizar a nivel de elemento de lista, por ejemplo, validar que una fecha siempre es superior a otra, o a nivel de columna, por ejemplo, que el valor de una fecha sea superior al día actual.

Las listas de encuestas, son un tipo especial de lista que permite crear preguntas y añadir lógica de bifurcación en función de las respuestas de los usuarios.

sharepoint2010_surveys_1

Aunque me parece un gran olvido, estas listas no permiten la validación de la listas, sólo nos muestra los campos Creado y Modificado para crear la fórmula de validación, y no el resto de campos de la encuesta. En las columnas pasaría más de lo mismo, no tenemos la opción de configurar la fórmula de validación.

sharepoint2010_suvery_2

¿Y qué opciones de validación tenemos para este tipo de lista?

Las opciones que tenemos son las mismas que teníamos con SharePoint 2010, desarrollar un Event Receiver que, desde el lado del servidor, valide la información de los campos del elemento de la lista, o modificar los formularios de la encuesta e incluir validación del lado del cliente utilizando JavaScript.

Event Receiver

Para crearnos un Event Receiver tenemos un proyecto de Visual Studio 2010 que nos permite implementar la clase que sobrescribe los métodos necesarios para la lista. Cuidado con esto porque si no configuramos bien el receiver, podemos incluir esta funcionalidad a todas las listas de una plantilla determinada. Por ejemplo, vamos a sobrescribir el método ItemAdding (cuando se está agregando un elemento a la encuesta) para comprobar que la suma de todos los campos de la encuesta es 35.

/// <summary>

/// List Item Events

/// </summary>

public class LimitEventReceiver : SPItemEventReceiver

{

   /// <summary>

   /// An item is being added.

   /// </summary>

   public override void ItemAdding(SPItemEventProperties properties)

   {

       int score = 0;

       SPList MySurvey = properties.Web.Lists[properties.ListId];

 

       foreach (SPField item in MySurvey.Fields)

       {

           if (item.TypeAsString == "GridChoice")

           {

               score += ResponceScore(properties.AfterProperties[item.InternalName].ToString());

           }

       }

 

       if (score != 35)

       {

           StringBuilder body = new StringBuilder();

           body.Append("La valoración de la encuesta no llega a 35. El valor actual es ");

           body.Append(score.ToString());

           body.Append("nn");

           body.Append("Por favor, revise las respuestas de su encuesta para que llegue al valor solicitado.");

 

           properties.ErrorMessage = body.ToString();

           properties.Cancel = true;

       }

   }

 

   private int ResponceScore(string expr)

   {

       String[] numbers = System.Text.RegularExpressions.Regex.Split(expr, @"[#+#]");

       int total = 0;

       foreach (String number in numbers)

       {

           if (number != "")

           {

               int num = 0;

               int.TryParse(number, out num);

 

               total += num;

           }

       }

       return total;

   }

 

}

El problema de este tipo de validación es que se realizan en el lado del servidor, y la experiencia del usuario que recibe un error no es muy buena, ya que parece que se ha producido una excepción grave en el sistema y tampoco le permite volver atrás y corregir los errores en el formulario de alta.

JavaScript

La mejor opción, aunque no sea muy partidario de incluir código JavaScript en los formularios de SharePoint, es modificar el formulario de alta (NewForm.aspx) con el SharePoint Designer (acordaros de editarlo en modo avanzado) y incluir esa misma validación pero en el lado del navegador, sin necesidad de realizar un postback al servidor. Con esta validación mejoramos sustancialmente la experiencia del usuario, que, en el mismo formulario, le avisamos que no ha rellanado correctamente la encuesta y que tiene la posibilidad de corregirla.

<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">

    <SharePoint:UIVersionedContent UIVersion="4" runat="server"><ContentTemplate>

        <SharePoint:CssRegistration Name="forms.css" runat="server"/>

    </ContentTemplate></SharePoint:UIVersionedContent>

    <script type="text/javascript" src="/Style Library/jQuery/jquery-1.4.2.min.js"></script>
   1:  

   2:     <script type="text/javascript">

   3:         var cuenta = 0;

   4:         function PreSaveAction() {

   5:             SP.UI.Status.removeAllStatus(true);

   6:             $("#mensajeError").html("");

   7:             $("#mensajeError").hide();

   8:             

   9:             cuenta = 0;

  10:             $("form input[type='radio']:checked").each(function() {

  11:                 if ($(this).val() == "ctl00")

  12:                     cuenta = cuenta + 1;    

  13:                 if ($(this).val() == "ctl01")

  14:                     cuenta = cuenta + 2;    

  15:                 if ($(this).val() == "ctl02")

  16:                     cuenta = cuenta + 3;    

  17:                 if ($(this).val() == "ctl03")

  18:                     cuenta = cuenta + 4;    

  19:                 if ($(this).val() == "ctl04")

  20:                     cuenta = cuenta + 5;    

  21:             });

  22:             

  23:             if (cuenta == 35) 

  24:                 return true;

  25:             else {

  26:                 var mensaje = "La valoración de la encuesta no llega a 35. El valor actual es: " + cuenta;

  27:                 mensaje = mensaje + ". Por favor, revise las respuestas de su encuesta para que llegue al valor solicitado.";

  28:                 

  29:                 var sid = SP.UI.Status.addStatus('Valoración no válida:', mensaje); 

  30:                 SP.UI.Status.setStatusPriColor(sid, 'red'); 

  31:  

  32:                 return false;

  33:             }

  34:         }    

  35:     

</script>

</asp:Content>

En el contenedor “PlaceHolderAdditionalPageHead”, añadimos un enlace al core de jQuery y sobrescribimos una función llamada PreSaveAction que es llamada por el código de formulario en SharePoint antes de hacer el summit. En esa función implementamos nuestra validación y utilizamos el objeto SP.UI.Status para mostrar el mensaje de validación al usuario usando la barra de estado que utiliza SharePoint 2010.

Y hasta aquí las posibilidades de validación en listas de tipo encuestas, es una pena que no se hayan incluido en la nueva funcionalidad de validación, aún así SharePoint nos sigue demostrando día a día que es una plataforma extensible y que con un poquito de experiencia se pueden hacer muy buenas cosas.

 

Saludos a todos…

TenerifeDev. Grupo de usuarios de .NET en Tenerife

Logo

Después de un tiempo dándole forma, hemos creado un grupo de usuarios de desarrolladores en .NET en Canarias. La semana pasada realizamos nuestra primera reunión en la que presentamos el grupo, junto con sus objetivos, y hablamos sobre Windows Azure y Windows Phone 7.

La agenda de las sesiones sufrió un cambio de última hora (David Rodríguez se apuntó a la sesión práctico con Windows Azure y yo me encargué de dinamizar todas las sesiones, incordiando un poco con algunos comentarios y ayudando a los ponentes) y quedó como sigue:

  • Presentación de las sesiones y del grupo TenerifeDev. Yo mismo
  • Cloud Computing con Windows Azure. José Fortes
  • Desarrolla con Windows Azure. David Rodríguez
  • Desarrolla con Windows Phone 7. Yeray Julián

Os podéis descargar las presentaciones en el SkyDrive del grupo.

También tuvimos la oportunidad de grabar un video resumen de las charlas. Muchas gracias al cámara Sonrisa

Resumen 1. Charlas Windows Azure y Windows Phone 7
Resumen 2. Charlas Windows Azure y Windows Phone 7

También algunas fotos, muchas gracias a la fotógrafa.

Gracias a todos por asistir y a los que no pudieron, comentarles que vienen más. Ya estamos planificando las siguientes y necesitamos de vuestra participación en el grupo, esto no es sólo nuestro si no de todos.

En lo que tenemos un portal para poder compartir experiencias, hemos creado un grupo en LinkedIn para que os vayáis apuntando, ofreciendo vuestra ayuda o dando sugerencias y temas para las próximas reuniones. También tenemos una cuenta de Twitter @TenerifeDev, en la que os iremos publicamos noticias sobre el grupo, y una email TenerifeDev@live.com.

Sin más, invitaros a participar, ya sea de oyente o de una forma más activa como organizador, esperamos que nos veamos en las siguientes reuniones del grupo.

 

Saludos a todos…

SharePoint 2010. Mensaje de error para depuración

Cuando desarrollamos con SharePoint, lo normal es que configuremos nuestra aplicación web de desarrollo para que nos muestra los mensajes de error y la pila de llamadas del mismo, y no la página de errores personalizada de SharePoint.

sharepoint2010_error_idataserviceupdateprovider

En SharePoint 2007 se configura editando el web.config de nuestra aplicación web, que por norma se encuentra en “c:inetpubwwwrootVirtualDirectories<puerto o nombre de aplicación>” y cambiando las siguientes directivas de ASP.NET:

  1. Ponemos el Debug a true
  2. Ponemos el CallStack a true
  3. Desactivamos el CustomErrors poniéndolo a Off

Cuando seguimos estos pasos en un servidor SharePoint 2010, nos encontramos con que no termina de funcionar y seguimos sin obtener todo el mensaje de error, nos aparece lo siguiente:

RunTimeErrorAgain

Al parecer, en SharePoint 2010 tenemos otro web.config (“..14TEMPLATELAYOUTSweb.config”) en el que tenemos que desactivar el CustomErrors:

customErrors

Y con esto tendremos nuestro SharePoint de desarrollo preparado para mostrarnos algunos errores (jaja, algunos dice, no nos queda nada) que generemos con nuestro código.

 

Saludos a todos…

Evento sobre Windows Azure y Windows Phone 7 en Tenerife

Lo sé, os aviso con poco tiempo, pero es lo que hay. MAÑANA, 22 de diciembre de 2010, tenemos unas sesiones sobre lo último de lo último de Windows Azure y Windows Phone 7.

No te quedes rezagado y vente a conocer las últimas novedades en desarrollo sobre Windows Phone 7 y Windows Azure, en el Salón de Grados de la Escuela de Informática de la Universidad de La Laguna.

Me lo van a preguntar y por eso me adelanto, ¿Por qué tan poco tiempo? Tenemos la oportunidad de que la sesión de Windows Phone 7 nos la cuente José Yeray Julián Ferreiro, antiguo compañero y que ahora desarrolla para Plain Concepts. Nos aprovechamos de él y, en sus vacaciones, nos mostrará lo sencillo y divertido que es Desarrollar para Windows Phone 7.

Sin menospreciar al resto de ponente (uno de ellos voy a ser yo), José Fortes nos hablará de Cloud Computing y las capacidades de Windows Azure y yo haré de su becario, intentando dar una sesión práctica de Desarrollo con Windows Azure.

Os podéis registrar, aunque no será obligatorio, en https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032473610&Culture=es-ES . Si os registráis mejor que mejor.

Día: 22 de diciembre de 2010

Hora comienzo/fin: 17:30-20:15

Lugar: Salón de Grados, Escuela Técnica Superior de Ingeniería Informática

Universidad de La Laguna, Camino San Francisco de Paula s/n
38271, La Laguna, Tenerife, SPAIN

La agenda es la siguiente:

  • 17:30 Bienvenida y presentación de las sesiones
  • 17:30 Cloud Computing con Windows Azure (José Fortes)
  • 18:15 Desarrolla con Windows Azure (Alberto Díaz)
  • 19:00 Descanso
  • 19.15 Desarrolla con Windows Phone 7

No os lo perdáis, es el primero de una larga serie de eventos que haremos en Tenerife.

Saludos a todos…

SharePoint 2010. Personalización de pestañas en Mi Perfil

Si tenemos configurado los perfiles de usuarios, hemos tenido que crear una colección de sitios que hospeda los sitios personales de los usuarios. Una de las funciones de estos sitios es mostrar la información del perfil de un usuario (Mi Perfil).

ms-sharepoint-2010-communities-consultants

En la página del perfil, nos encontramos con una serie de pestañas que nos muestran la siguiente información:

  • Información General (Overview). Muestra un resumen del perfil con las áreas de Preguntarme acerca de, Actividades recientes, Panel de notas, organigrama y en común con el usuario actual.
  • Organización (Organization). Un visualizar en silverlight (o en html) de la jerarquía de la organización.
  • Contenido (Content). El contenido de mi sitio al que tienes permisos del perfil.
  • Etiquetas y notas (Tags and Notes). La información que ha sido etiquetada y las anotaciones del perfil.
  • Compañeros (Colleagues). Los compañeros del perfil.
  • Pertenencia a grupos (Memberships). Los grupos y sitios de SharePoint al que pertenece el perfil.

Si necesitamos personalizar estas pestañas, cambiar el orden, crear una nueva, podemos hacerlo (con los permisos adecuados) en la administración del host de mi sitio, en la administración del Inicio rápido.

image

Esto nos permitiría crear una nueva página que, manteniendo la misma funcionalidad de las actuales, añada nuevas capacidades al sitio de los perfiles. Por ejemplo, podríamos crearnos una nueva pestaña que tuviera un Web Part que consultara todos los documentos del usuario de una colección de sitios.

Primero creamos la nueva página, por ejemplo, utilizamos el SharePoint Designer y en All Files creamos una copia de person.aspx que es la página de la pestaña de Información General, la llamamos alldocs.aspx.

sharepoint2010_myprofile_newtab_1

Nos creamos un encabezado, en el Inicio rápido, que apunte a la nueva página.

sharepoint2010_myprofile_newtab_2

Podemos personalizar la página utilizando SharePoint Designer o editándola desde el navegador y agregamos los Web Parts que nos hagan falta para la consulta de todos los documentos,

Si utilizamos el Designer deberíamos mantener el divisor ms-profilepageheader que tiene toda la funcionalidad de carga de los datos del perfil y de las pestañas, utilizando un parámetro de página con la cuenta del usuario a cargar (/my/alldocs.aspx?accountname=cuentadelusuario). Editando la página es tan sencillo como cualquier página de Web Parts.

sharepoint2010_myprofile_newtab_3

Con poca o mucha dificultad, SharePoint siempre nos permite personalizar y mejorar la experiencia del usuario. En esta ocasión, sin necesidad de escribir código, hemos ampliado las funcionalidades del sitio de perfiles.

 

Saludos a todos…

LINQ. Enigma recursivo

Lo tengo que reconocer, soy un viciado de las consultas SQL. Me recuerdan a los problemas de lógica en los que tenías que averiguar quién hace qué y cómo, y resolver un enigma.

logic_tapalogicextra_tapa

Pues así veo las consultas SQL, como un enigma a resolver. Me he pasado horas dándole vueltas a las consultas para hacer joins imposibles y subconsultas buscando obtener los datos en una sola llamada y con el mejor rendimiento posible. La verdad es que, a veces, echo de menos esas consultas de varias páginas tiradas en el Administrador de consultas del SQL Server 6.5 o 2000.

70-228-6-5

Con el tiempo vas evolucionando y te vas pasando a los ORM (Hibernate, LINQ to SQL, Entity Framework, LLBLGEN), y dejas de lanzar esas consultas y simplificas la lógica para que se adapten a las entidades, aún así seguías queriendo meter código en procedimientos almacenados para no olvidar aquella parte de ti, que controlas y que te gusta.

Desde que nació LINQ me pareció el gran avance de la programación actual. Cuando lo empiezas a usar, te das cuenta que vas a dejar de echar de menos a aquellas consultas y empiezas a olvidarte de los procedimiento almacenados. Pasas a otro nivel.

En ese nivel, empiezas a lanzar consultas sobre listas, enumerados y colecciones (y a todo lo que te echen por delante, que si no es consultable buscas la forma de que lo sea), y empieza a aparecer, de nuevo, los enigmas de aquellas consultas SQL, ahora en C#.

No lo puedo evitar y busco siempre la mejor consulta y la más eficiente, incluso me pongo a calcular el rendimiento de hacerla con LINQ y sin LINQ.

El último reto fue hacer una búsqueda recursiva, a través de listas de elementos enlazados con listas de esos elementos, utilizando LINQ.  Partimos de la siguiente clase que contiene una lista de elementos hijos de su misma clase.

public class Organizacion

{

    public Guid ID { get; set; }

    public OrganizationProfile Profile { get; set; }

    public string DisplayName { get; set; }

    public Organizacion Parent { get; set; }

    public List<Organizacion> Childs { get; set; }

    public UserProfile Leader { get; set; }

    public List<UserProfile> Members { get; set; }

}

Necesitábamos realizar una búsqueda recurrente en la lista principal que a su vez hiciera la misma búsqueda en los hijos (List<Organizacion> Childs) que a su vez buscara en los hijos de los hijos de los hijos… y así sucesivamente.

Este problema lo solucionaríamos con una búsqueda recursiva que fuera recorriendo todos los elementos, bajando y realizando la búsqueda en cada lista de hijos que fuera encontrando.

Cómo no me puedo estar quieto, me puse a buscar una solución que me permitiera hacer esa búsqueda en una consulta LINQ y ya llegué a la solución de crearme un método extensor que utiliza el combinador Y para realizar lambdas recursivas. Este método quedó tal como sigue:

private delegate Func<A, R> Recursive<A, R>(Recursive<A, R> r);

private static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f)

{

    Recursive<A, R> rec = r => a => f(r(r))(a); return rec(rec);

}

 

public static IEnumerable<Organizacion> Recorrer(this IEnumerable<Organizacion> source, Func<Organizacion, bool> function)

{

    var traverse = Extensions.Y<IEnumerable<Organizacion>, IEnumerable<Organizacion>>(

        f => items =>

        {

            var r = new List<Organizacion>(items.Where(function));

            r.AddRange(items.SelectMany(i => f(i.Childs)));

            return r;

        });

 

    return traverse(source);

}

Nos creamos un delegado de la función recursiva (Y) y la función que recorre todos los elementos navegando por la lista de Childs y devolviendo un enumerable de todos los elementos de todas las listas.

Con lo que es muy sencillo y limpio (además de bonito y satisfactorio Sonrisa ) hacer la consulta para encontrar un elemento de todas las colecciones, utilizando el método extensor.

 

Organizacion parentOrg = OrganizacionList.Recorrer(o => o.DisplayName == manager["Department"].ToString()).FirstOrDefault();

 

Lo sé, debería de pasar más tiempo haciendo otras cosas más útiles, pero, y lo bien que me lo he pasado….

 

Saludos a todos…

SharePoint 2010. Ver Web Analytics

Una de las mejoras de versión 2010 de SharePoint se centra sobre su capacidad analítica sobre el uso de la granja. El servicio Web Analytics se encarga de recopilar y analizar el uso del contenido de SharePoint para que podamos obtener los siguientes informes:

  • Informes de Tráfico. Estos nos proveen métricas sobre el número de visitas, las páginas visitadas y los visitantes.

sharepoint_webanalytics_view_5

  • Informes de Búsqueda. Estos ofrecen métricas sobre qué buscan los usuarios y cómo lo buscan.

sharepoint_webanalytics_view_6

  • Informes de Inventario. Que nos provee información sobre el uso del almacenamiento y los sitios de nuestros servidores.

sharepoint_webanalytics_view_7

Para acceder a estos informes existe una acción de menú, en la configuración del sitio, para Informes de Web Analytics del sitio o Informes de Web Analytics de la colección de sitios. Aunque también podemos ver estos informes a nivel de Aplicación Web o Servicio de Búsqueda.

Por definición, estos informes están al alcance de los Administradores de cada nivel, por ejemplo, los administradores de un sitio podrán ver los informes del sitio y los Administradores de la colección de sitios podrán ver los informes a un nivel superior de todos los sitios.

Entonces, para que un usuario acceda a estos informes tendremos que darle permiso de Administrador . ¿Seguro? Bueno, existe otra posibilidad bastante más adecuada que nos permite crear un nivel de permisos para los visualizadores de informes.

Para esto, nos vamos a los permisos del sitio y en el ribbon tenemos una opción para administrar los Niveles de permisos.

sharepoint_webanalytics_view_1

Desde está página de administración de niveles podemos modificar los niveles que vienen en SharePoint, que nos es muy recomendable ya que afectaría a todos los permisos de la colección de sitios, o crear un nuevo nivel que nos permita establecer los permisos de visualización de informes de Web Analytics.

sharepoint_webanalytics_view_2

Cuando añadimos un nuevo nivel de permiso, tenemos una lista de permisos para la configuración del alcance del nivel, por ejemplo, podemos dar permisos de lista para administrar una lista y no permitir crear elementos. Nosotros lo que necesitamos es dar permisos de Ver datos de Web Analytics.

sharepoint_webanalytics_view_3

Una vez creado el nivel de permisos, ya podemos ir a un grupo de usuarios o a un usuario, en los permisos del sitio, y asociarle ese permiso que le permitiría visualizar los informes de Web Analytics.

sharepoint_webanalytics_view_4

 

Y hasta aquí este pequeño tutorial para que los usuarios que no sean administradores del sitio puedan ver los informes de análisis de los datos de uso de SharePoint 2010.

 

Saludos a todos…