Sharepoint-search: un componente Polymer para usar la búsqueda de SharePoint On-Line

Muy buenas a todos.

En una entrada anterior, os contaba cómo usar la búsqueda de SharePoint a través de su servicio API REST. El enlace ha dicha entrada es la siguiente:

Search Driven Development. Usando la API REST de SharePoint para el servicio de búsqueda

Al final de esta entrada os contaba que como objetivo, tenía el desarrollo de un componente Polymer que permitiera usar este servicio, algo que me resultaba muy útil e interesante. Después de varios días de trabajo, quería compartir con vosotros una primera “versión” de dicho componente. Aún queda trabajo por delante, pero ya hay un punto de partida funcional a partir del cual seguir trabajando.

El componente debía cumplir con los siguientes requisitos:

  • Permitir la posibilidad de usar refinadores.
  • Permitir la posibilidad de establecer opciones de ordenamiento.
  • Controlar la paginación y el número de resultados por página en los resultados de búsqueda.
  • Dar la posibilidad de estilar el contenido del componente en función de como se desee.

El componente “sharepoint-search” nos permite cubrir estos requisitos. Para usarlo, necesitaremos tener cargados los ficheros de Polymer y los propios del componente y utilizaremos la etiqueta de la siguiente manera:

<sharepoint-search tenant="organizer" clientid="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" refinement="author,size,created" ordination="size,created" pageElement="15">
</sharepoint-search>

Para que funcione correctamente, necesitaremos el clientID de una aplicación de Azure AD con los permisos para el servicio de búsqueda de SharePoint concedidos, y obviamente estar logado en nuestra aplicación contra esa aplicación de Azure AD, para ello podemos usar el componente para la autenticación que creé en una entrada anterior y seguir los correspondientes pasos para obtener el clientid:

Registrando una aplicación en Azure Active Directory

login-azuread: Un Componente Web con Polymer y Javascript ADAL

Además deberemos de configurar la aplicación de nuestra Azure AD para permitir el OAuth 2.0 implicit grant flow, lo haremos de la siguiente forma:

  • Acceder a la configuración de la aplicación de Azure AD correspondiente
  • Descargamos el manifiesto
  • Modificamos el parámetro oauth2AllowImplicitFlow dándole el valor true en lugar de false que tiene establecido inicialmente
  • Cargamos de nuevo el manifiesto modificado

El componente “sharepoint-search” permite configurar los siguientes parámetros:

  • tenant: Indicar el tenant al que nos queremos conectar
  • clientid: Clientid de la aplicación con los permisos adecuados
  • refinement: Propiedades para las cuales queremos obtener refinamiento
  • ordination: Propiedades por las que queremos hacer ordenación
  • pageElement: Número de elementos por página a mostrar

Una vez que lo añadimos, funciona como vemos a continuación.

sharepointsearchinit

sharepointsearchsearched

Por defecto, el componente nos muestra los resultados en una lista con el título del documento y la posibilidad de descargar el mismo. Aunque como se indica a continuación, en futuras mejoras, se trabajará en flexibilizar este aspecto del componente.

Os dejo el enlace a mi cuenta de GitHub donde podéis descargar el código del componente web y donde podréis encontrar información sobre cómo usarlo.

https://github.com/jcroav/sharepoint-search

Y esto es todo por hoy, este componente aún tiene áreas de mejora, a las que intentaré dedicarle tiempo en las próximas semanas. Os dejo algunas de las ideas:

  • El componente está pensado para que los resultados de búsqueda usen a su vez un subcomponente para ser mostrados, queda pendiente de desarrollar esa parte.
  • Completar el componente con la funcionalidad de sugerencias
  • Mejorar toda la parte de estilado
  • Refactorizar y trabajar sobre la optimiazación del código para hacer un componente más robusto

Como siempre, todo aquel que quiera colaborar, o quiera usar el componente que os he mostrado hoy para mejorarlo, es libre de hacerlo. El objetivo de estos desarrollos, a parte de para aprender a usar las funcionalidades de Office 365 y SharePoint On-Line, es contribuir a la comunidad.

Un saludo y espero que os sea útil

Anuncios

login-azuread: Un Componente Web con Polymer y Javascript ADAL

Hola a todos,

En la entrada anterior os contaba como se podía usar la librería Javascript ADAL para autenticar una aplicación contra Azure AD usando Javascript y terminaba diciendo que os contaría sobre el componente Polymer que había desarrollado para la autenticación.

Autenticación de nuestra Aplicación usando Javascript ADAL

Lo que he hecho ha sido crear un componente web que permite de una forma sencilla hacer la autenticación y que añadiéndolo a cualquiera de nuestras aplicaciones, podamos gestionar el sign in contra Azure AD de la misma. El código del componente web lo podéis encontrar en mi cuenta de GitHub.

Azuread-login en GitHub

El modo de empleo es muy sencillo, solo tendremos que insertar la siguiente línea en donde queramos tener nuestro modulo de login.

<login-azuread domain="<tenantdomainhere>" clientid="<clientidhere>"
</login-azuread>

Antes, deberemos haber registrado la aplicación en Azure AD, lo que nos proporcionará un client ID. Para que el componente funcione correctamente, deberemos indicar en el atributo domain, el dominio de nuestro tenant y en el atributo clientid el dato obtenido tras el registro de la aplicación en Azure AD.

¿Cuál es el resultado?

Vamos a ver como funciona el componente web una vez lo usamos en un ejemplo.

Cuando un usuario no se ha autenticado.

azureadlogin1

azureadlogin2

Cuando un usuario se ha autenticado
azureadlogin3

Además el componente se ha diseñado de manera que los elementos que lo forman, no se encuentran dentro del Shadow DOM que usa Polymer, sino que están renderizados por el DOM de la página, de manera que podemos estilarlos por medio del CSS de nuestra aplicación. Para ello, solo tenemos que redefinir las clases loginad-name, loginad-a, logoutad-a.

¿Vemos algo de código?

El código es muy sencillo, y aunque lo podéis ver en GitHub os lo pongo aquí y explico algunos detalles. El componente lo forman dos ficheros, uno .html y otro .js

<script src="../../Scripts/polymer.min.js"></script>
<script src="../../Scripts/adal.js"></script>

<polymer-element name="login-azuread" attributes="domain clientID" />
    <template>
        <content select="span"></content>
        <content select="a"></content>
        <content select="a"></content>
    </template>
    <script src="login.js"></script>
</polymer-element>

El fichero .html contiene la definición del componente web. Como véis el template utiliza la etiqueta content, que nos va a permitir que el contenido del componente no esté renderizado en el Shadow DOM y si en el DOM de la página.

window.config = {
    tenant: '',
    clientId: '',
    postLogoutRedirectUri: window.location.origin,
    cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
};

var authContext;

Polymer({
    userName: '',
    domain: '',
    clientID: '',
    ready: function () {
        window.config.tenant = this.domain;
        window.config.clientId = this.clientID;

        authContext = new AuthenticationContext(config);

        var isCallback = authContext.isCallback(window.location.hash);
        authContext.handleWindowCallback();

        if (isCallback &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; !authContext.getLoginError()) {
            window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
        }

        // Check Login Status, Update UI
        var user = authContext.getCachedUser();

        if (user)
            this.userName = user.userName;
        else
            this.userName = '';

    },
    attached: function () {
        var name = document.createElement("span");
        name.innerHTML = this.userName;
        name.className = "loginad-name";

        var login = document.createElement("a");
        login.href = "javascript:;";
        login.innerText = "Login";
        login.className = "loginad-a";
        login.addEventListener("click", this.login, true);

        if (this.userName != '')
            login.style.display = "none";
        else
            login.style.display = "inline-block";

        var logout = document.createElement("a");
        logout.href = "javascript:;";
        logout.innerText = "Logout";
        logout.className = "logoutad-a";
        logout.addEventListener("click", this.logout, true);

        if (this.userName != '')
            logout.style.display = "inline-block";
        else
            logout.style.display = "none";

        this.appendChild(name);
        this.appendChild(login);
        this.appendChild(logout);
    },
    login: function () {
        authContext.login();
    },
    logout: function () {
        authContext.logOut();
    }
});

La particularidad de este código es que, como podemos ver, utiliza la función ready de Polymer para cargar el objeto de configuración y comprobar si está autenticado el usuario y la función attached para inyectar todos los elementos que forman el componente, con la funcionalidad de cada uno.

¿Es mejorable?, como siempre, claro que lo es. Y me encantaría recibir feedback de qué más podríamos hacer para que este componente fuera realmente útil y pudiéramos usarlo en nuestras aplicaciones como una alternativa más.

Saludos, espero que lo hayáis encontrado interesante.

Autenticación de nuestra Aplicación usando Javascript ADAL

Muy buenas a todos,

En una entrada anterior, vimos como podíamos desarrollar una aplicación web con MVC .NET que nos permitiera acceder a la información guardada en nuestro Office 365. Una parte importante de ese desarrollo era la autenticación contra Azure AD, lo que se hacía por medio del paquete Nugget ADAL (Active Directory Authentication Library).

Hace unos días tuve conocimiento de una librería de Javascript que nos permitía hacer la autenticación contra Azure AD (conocida como Javascript ADAL) y que se podía utilizar en el desarrollo de Single Page Applications de una forma muy sencilla. Así que decidí probarlo para seguir abriendo y conociendo el abanico de oportunidades para trabajar contra Azure AD y luego con la API de Office 365. El primer paso obviamente será el login para después acceder y consultar la API usando CORS.

Así que en la entrada de hoy, os voy a mostrar cómo hacer el login usando esta librería de Javascript. Para el ejemplo he creado un proyecto de MVC vacío en el que he hecho todo el desarrollo. Antes de nada, os dejo, como siempre, los enlaces que me han servido de guía y donde creo que hay información muy valiosa sobre esta librería.

http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/

https://github.com/AzureADSamples/SinglePageApp-jQuery-DotNet

Disponemos de una versión de la librería integrada con Angularjs y otra versión que podremos usar con JQuery y otros frameworks que será la versión que voy a usar en mi ejemplo.

Empezando con el ejemplo

Obviamente el primer paso en cualquier caso, es registrar nuestra aplicación en el Azure Active Directory de nuestro Tenant. Tras esto, tenemos que descargar el fichero adal.js.

El siguiente paso que he seguido es crear la vista del ejemplo, que es tal y como veréis a continuación

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Scripts/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <script src="~/Scripts/adal.js"></script>
    <script src="~/Scripts/App/app.js"></script>
</head>
<body>
    <div>
        <h2>Index</h2>

        <a href="javascript:;" class="app-login">Login</a>
        <a href="javascript:;" class="app-logout">Logout</a>
    </div>

</body>
</html>

Esta vista tiene dos botones, uno para hacer login y otro para hacer logout, nada más. El fichero que tiene todo el código es el fichero app.js que podéis ver ahora.

(function () {

    //1.- Initial configuration
    window.config = {
        tenant: '<tentantdomain>',
        clientId: '<clientid>',
        postLogoutRedirectUri: window.location.origin,
        cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
    };
    var authContext = new AuthenticationContext(config);

    //2.- Handle redirect after login
    var isCallback = authContext.isCallback(window.location.hash);
    authContext.handleWindowCallback();

    if (isCallback && !authContext.getLoginError()) {
        window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
    }

    //3.- Check for authenticated information
    var user = authContext.getCachedUser();
    if (user) {
        console.log(user);
    } else {
        console.log("error");
    }

    //4.- Handle events for login & logout
    var $signInButton = $(".app-login");

    $signInButton.click(function () {
        authContext.login();
    });

    var $signOutButton = $(".app-logout");

    $signOutButton.click(function () {
        authContext.logOut();
    });
}());

El código de este ejemplo es una simplificación del ejemplo de Github que os he puesto un poco más arriba y que he usado para entender el proceso que seguía. Lo único que hace este código es mostrar en la consola de depuración los datos del usuario si está logueado o mostrar un texto de error si no lo está. Los puntos más importantes del código son los siguiente:

  1. El primer paso es crear el objeto de configuración, con los datos del tenant y client id que hemos obtenido al registrar la aplicación
  2. A continuación se indica el callback que maneja la redirección después del login
  3. Se comprueba y se muestra la información de logueo del usuario
  4. Por último se crean los eventos que dispararán el login y el logout asociados al click en los enlaces

Y esto es todo, si lo probamos comprobaremos que el resultado es el esperado, cuando no estamos logueados, nos muestra la pantalla de signin de windows para indicar nuestro usuario y contraseña del Azure AD de nuestro tenant.

Lo que os voy a enseñar en próximas entradas es un componente web de Polymer que he creado que utiliza este código para implementar la funcionalidad de signin y signout de una manera muy sencilla y que creo que puede ser de utilidad. Este componente, de hecho ya está en mi GitHub

Un saludo a todos y buen finde.

Usando Bower y Grunt en Visual Studio 2013

Muy buenas a todos,

Hoy os quiero contar los pasos que he dado para usar en mis proyectos de Visual Studio 2013 bower, usándolo como gestor de paquetes en el lado del cliente, y grunt, como task runner. Ambos están siendo muy comúnmente usados en el mundo del desarrollo de aplicaciones web y si bien, la versión de Visual Studio 2015 los incluye de forma nativa, podemos configurar nuestros proyectos web de Visual Studio 2013 para usar estas dos herramientas tan útiles.

Para todo aquel que le pueda interesar, dejo la entrada del blog en la que me he basado para hacer toda la configuración, aunque si accedéis veréis que en el post, se usa otro gestor de tareas llamado gulp en lugar de grunt.

http://blogs.msdn.com/b/cdndevs/archive/2015/02/17/using-bower-with-visual-studio-2013.aspx

Pero antes de entrar en detalle, vamos a ver qué son exactamente cada una de estas herramientas.

¿Qué es Bower?

Bower works by fetching and installing packages from all over, taking care of hunting, finding, downloading, and saving the stuff you’re looking for.

La primera pregunta que se nos plantea viendo lo que hace Bower es ¿Y Nugget?. La respuesta a esta pregunta es muy sencilla. Desde su aparición Nugget se ha descubierto como una herramienta muy eficaz para la gestión de paquetes de servidor, pero no así para paquetes de cliente. Bower se ha convertido en el estándar de facto en el desarrollo web. Es poco probable, que un desarrollador que crea una nueva librería en Javascript la ponga disponible como paquete Nugget, sin embargo es frecuente que esté disponible en Bower en todas sus versiones. Esto provoca desactualización o imposibilidad de usar determinadas librerías a través de Nugget, lo que desaconseja su uso como herramientas de gestión de paquetes del lado del cliente.

¿Qué es Grunt?

In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes.

En resumen, con Grunt vamos a poder automatizar determinadas tareas que van a ayudar a tener nuestra aplicación lista para poner en producción con acciones como: minimizar y agrupar los estilos, minimizar y agrupar los javascript, etc. En la entrada veremos una tarea que he creado que puede ser necesaria y recomendable cuando usamos Bower.

Configurando y usando Bower

Para la configuración de Bower, podéis fácilmente seguir la entrada del blog de la msdn sin ningún problema, es muy sencilla de seguir y está muy detallada, de hecho yo la he seguido y es lo que de forma simplificada explicaré por aquí, solo hay una cosa que cambia, porque a mi al menos, o no me quedaba claro en la entrada inicial o no estaba debidamente reflejado. Vamos a ello.

    1. El primer paso es instalar node.js, ya que tanto Bower como Grunt lo utilizan. Enlace de descarga de node.js
    2. A continuación, abrimos la línea de comandos e instalamos bower
npm install bower -g
    1. Tras esto, necesitaremos tener instalado Git, Bower utiliza Git para descargar los paquetes. Concretamente instalaremos msysgit. Durante la instalación, tendremos que seleccionar la opción que vemos en la imagen a continuación para que se realice toda la configuración correctamente

gitinstall

    1. Ya tenemos listo Bower para ser utilizado. Si lo vamos a usar dentro de un proyecto de MVC, antes tendremos que tener en cuenta que por defecto, la plantilla de este tipo de proyectos en Visual Studio incluye una serie de paquetes, entre ellos: bootstrap, jquery, modernizr, etc. Por lo que antes de comenzar a usar bower en nuestro proyecto, tenemos que desinstalarlos. Para ello accedemos a la consola de Nuget y ejecutamos el siguiente comando para cada uno de los paquetes
Uninstall-Package <package>

Ahora que ya tenemos listo Bower vamos a usarlo dentro de nuestro proyecto. Esto lo haremos a través de la línea de comandos. En mi caso, no he usado la interfaz de línea de comandos propia de Windows (cmd), ya que me daba errores al usarla con Bower y Git, y buscando por internet vi que recomendaban usar una interfaz de línea de comandos que se instala con msysgit. Esta es Git Bash.

Vamos ahora a entrar al directorio del proyecto, y comenzaremos con el uso de Bower. Lo primero que haremos será iniciar el fichero de configuración de Bower. Para ello ejecutamos:

bower init

Una vez creado el fichero bower.json, podemos comenzar a instalar paquetes, esto lo haremos de la siguiente forma.

bower install jquery --save
bower install polymer --save

Una Buena Práctica recomendada para el uso de los paquetes que se obtienen con Bower, es no agregar directamente todos los archivos descargados con Bower a nuestro proyecto, ya que los paquetes en muchas ocasiones contienen más archivos de los que realmente necesitaremos, por lo que se recomienda copiar al proyecto solo aquellos archivos que realmente sean necesarios. ¿Cómo hacemos esto, manualmente?. La respuesta evidentemente es no, para esto tenemos nuestro Task Runner, Grunt 😉

Configurando y usando Grunt

En primer lugar, para añadir el soporte a Grunt para nuestro proyecto vamos a crear dos ficheros que añadiremos a la raíz del mismo package.json y gruntfile.js. El fichero package.json inicialmente contendrá la siguiente información.

{
"name": "WebApplication1",
"version": "1.0.0",
"description": "WebApplication package project",
"main": "gruntfile.js",
"license": "ISC";
}

A continuación y como ejemplo, la tarea que vamos a crear va a copiar los ficheros necesarios de los paquetes que hemos obtenido mediante Bower a la carpeta Scripts, donde se encuentran los ficheros js de nuestro proyecto. El código del fichero gruntfile.js es el siguiente:

module.exports = function (grunt) {

    grunt.initConfig({
        copy: {
            main: {
                files: [
                  {
                      expand: true,
                      cwd: '../bower_components/webcomponentsjs/',
                      src: ['webcomponents.js'],
                      dest: 'Scripts'
                  },
                  {
                      expand: true,
                      cwd: '../bower_components/jquery/dist',
                      src: ['jquery.min.js'],
                      dest: 'Scripts'
                  }
                ]
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.registerTask('build', ['copy']);
};

Obviamente, aún podríamos hacer algunas tareas de minimización y agrupamiento de ficheros con grunt y que serían necesarias, aunque eso lo dejo para más adelante, es posible que en alguna entrada futura os cuente como hacerlo.

Para que funcione correctamente, necesitamos un plugin de Grunt, “grunt-contrib-copy”. Cuando necesitemos añadir determinados plugins para nuestras tareas de Grunt, lo haremos desde la línea de comandos usando la utilidad npm de node.js. Nos vamos al directorio del proyecto y ejecutamos el siguiente comando.

npm install grunt-contrib-copy --save-dev

Una vez que ya tenemos instalado el plugin, desde la línea de comandos en el directorio en el que está nuestro archivo gruntfile.js (la raíz del proyecto), ejecutamos grunt. Esto llevará a cabo la tarea que hemos creado. La primera vez que ejecutamos grunt, tendremos que añadir manualmente los archivos que se han añadido a la tarea al proyecto, aunque esta acción no tendremos que volver a hacerla.

Como hemos visto, hasta ahora estamos usando la línea de comando para ejecutar Grunt. Pero, ¿Y si queremos que se ejecute automáticamente Grunt cuando hacemos el deploy del proyecto, esto es posible?, la respuesta es si. Para esto tenemos varias extensiones de Visual Studio que nos lo permiten. En mi caso he utilizado Task Runner Explorer.

La descargamos y la instalamos y ya podremos usarla en nuestro Visual Studio 2013. Vamos a View->Other Windows->Task Runner Explorer y cuando se abre la nueva ventana, lo primero que hacemos es pulsar sobre el botón actualizar para que capture todas las tareas creadas si aún no lo habia hecho.

taskrunnerexplorer

Aquí vemos que podemos configurar si queremos que una determinada tarea se ejecute antes del deploy de la solución, con lo que podremos automatizar correctamente la ejecucion de Grunt y no tendremos que hacerlo manualmente a través de la línea de comandos.

run

binding

Os dejo para terminar, algunos enlaces más de utilidad relacionado con lo que hemos visto en la entrada:

http://gruntjs.com/
http://bower.io/
https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708

Y esto es todo por hoy, yo a partir de ahora voy a utilizar estas dos herramientas en todos mis proyectos, por las ventajas que ofrecen, espero que todos podáis hacerlo.

Un saludo y hasta la próxima

Campos de tipo DateTime y API REST en SharePoint

Hola a todos,

Como os comentaba ayer, para poner en práctica los últimos avances que he tenido con las aplicaciones de SharePoint, he desarrollado una SharePoint Hosted App que utiliza, para la comunicación y las operaciones con SharePoint, API REST.

RoomReservation: Mi primera SharePoint Hosted App

Código en GitHub

Lo que hoy os quiero contar, es una de las cosas que más me ha entretenido en el desarrollo de la aplicación y tiene que ver con la manipulación de campos de tipo DateTime y la API REST. En sí, no tiene nada especialmente complejo, aunque a continuación veremos las particularidades que hay que tener en cuenta.

Filtrando campos de tipo DateTime en una petición API REST

El filtrado de campos de tipo fecha a través de API REST es muy sencillo, tan solo hay que pasar la fecha en el formato correcto:

https:///_api/web/lists/getbytitle(list)/items?$filter=DateFieldName eq datetime’yyyy-mm-ddThh:mm:ss’

El formato de la fecha como podéis ver es el siguiente “yyyy-mm-ddThh:mm:ss”

Datos devueltos en una consulta de API REST de tipo DateTime

Con los datos de tipo DateTime que nos devuelve una consulta API REST, empieza a haber algunas cosas a tener en cuenta. SharePoint, a través de API REST, nos devuelve todos los campos de tipo DateTime formateados como UTC. Por tanto, si mostramos los datos tal cual nos los devuelve API REST y nuestra configuración regional es distinta de la UTC (por ejemplo, en la península es UTC +1), no veremos los resultados que esperábamos. Por ejemplo, si hemos almacenado la siguiente fecha 2015-03-03T10:30:00 en el calendario de SharePoint y estamos en UTC+1, nos devolverá 2015-03-03T09:30:00.

Para solventar esto tenemos que aplicar la corrección correspondiente al TimeOffSet con respecto a la hora en formato UTC. Os pongo el código que he usado yo en mi aplicación y os cuento lo más importante. Para manipular las fechas he utilizado una librería de javascript llamada moment.js que añade algunas funcionalidades extra al objeto de tipo Date básico de Javascript, y que permitirá hacer sumas y restas a fechas.


var offset;

function GetTimeOffset(){
        var deferred = $.Deferred();

        var appweburl = MyApp.AppSPUtils.GetQueryStringParameter("SPAppWebUrl");
        var hostweburl = MyApp.AppSPUtils.GetQueryStringParameter("SPHostUrl");

        executor = new SP.RequestExecutor(appweburl);

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/regionalsettings/timezone?@target='" + hostweburl + "'",
            headers: {
                "accept": "application/json;odata=verbose",
                "content-type": "application/json;odata=verbose"
            },
            success: function (data) {
                var jsonObject = JSON.parse(data.body);
                offset = jsonObject.d.Information.Bias;

                deferred.resolve();
            },
            error: function (data) {
                deferred.reject();
            }
        });

        return deferred.promise();
    }
function ConvertToGetLocalTime(UTCdate) {

        var date = moment(UTCdate);

        if (date.utcOffset() > 0)
            date = date.subtract(offset, 'm');
        else
            date = date.add(((-1) * offset), 'm');

        return date.toISOString();
    }

El primer código que vemos, nos permite obtener por medio de API REST, el TimeOffSet de la configuración regional que tenemos en nuestro sitio de SharePoint con respecto a UTC. Si por ejemplo, estamos en UTC+1, el valor que obtendremos será -60, que son los minutos que habría que restar a la hora, para convertirla a formato UTC.

Por tanto, una vez obtenido el offset de nuestra configuración regional de sitio, aplicamos la conversión que vemos en la función ConvertToGetLocalTime que recibirá un string con el formato de fecha obtenido de la consulta por API REST (yyyy-mm-ddThh:mm:ss) y devuelve una fecha con el mismo formato y adaptado a la hora local configurada en nuestro sitio.

Añadiendo elementos a una lista con campos de tipo DateTime con API REST

Cuando lo que queremos es usar POST para añadir a través de API REST un elemento que tiene campos de tipo DateTime, el problema es el contrario. Lo que la API REST considera que recibe es una fecha en formato UTC y al almacenarla, la convertirá en el formato correspondiente a la configuración regional establecida. Por ejemplo, si pasamos como campo 2015-03-03T10:30:00 y tenemos una configuración UTC+1, lo que se almacenará será 2015-03-03T11:30:00.

Para solucionar esto, aplicaremos la configuración inversa que en el caso anterior por medio de la siguiente función:

function ConvertToPostLocalTime(UTCdate) {

        var date = moment(UTCdate);

        if (date.utcOffset() > 0)
            date = date.add(offset, 'm');
        else
            date = date.subtract(((-1) * offset), 'm');

        return date.toISOString();
    }

Y esto es todo por hoy, como podéis ver, trabajar con fechas y API REST requiere tener en cuenta la configuración regional y aplicar las conversiones a las fechas que correspondan, ya que ésta siempre trabaja con UTC tanto a la hora de hacer POST como GET.

Espero que os sea útil y os ahorréis ese tiempo de más que me ha llevado a la hora de trabajar con esto.

Saludos a todos.

RoomReservation: Mi primera SharePoint Hosted App

Muy buenas a todos,

Llevaba algunos días sin publicar nada en el blog, y es que he estado algo ocupado. Durante los meses de enero y febrero he estado trabajando en conocer el nuevo modelo de desarrollo de SharePoint, en qué consiste el nuevo modelo de Apps y que son y como se usan las API Cliente. He escrito algunos artículos sobre estos temas, relacionados con la API REST, CSOM y cómo crear SharePoint Hosted Apps. Pero para poder profundizar más en su conocimiento decidí crear una aplicación de SharePoint que tuviera en cuenta todo lo que había estado aprendiendo.

Por esto surge principalmente RoomReservation. RoomReservation es una SharePoint Hosted App que nos proporciona un sistema muy sencillo de reserva de salas. La aplicación, consiste en un formulario y un calendario. El formulario nos permite rellenar los campos básicos para hacer una reserva de salas, nos muestra la fecha, las horas y las salas disponibles. La utilidad de la aplicación, es que a medida que vamos completando los datos de fecha y hora las salas disponibles se van actualizando, permitiéndonos solo reservar aquellas salas que realmente están libres en el horario que estamos solicitando.

roomreservationapplication

Esta es la interfaz de la aplicación. Para que funcione correctamente, necesita dos lista, una lista personalizada con los recursos o salas disponibles y otra lista de tipo calendario donde se almacenarán las reservas realizadas.

El modelo de desarrollo que he seguido para la App ha sido el siguiente:

  • Todo el acceso a datos de SharePoint se ha hecho a través de API REST.
  • Las listas que son necesarias, son provisionadas remotamente por la propia aplicación, si tras instalarla, no existían ya. Para ello se ha usado también API REST, la aplicación evalúa si existen las listas, si no, automáticamente te redirige a una página donde tienes la opción de crear las listas.

install

Todo el código de la aplicación está en GitHub en el siguiente enlace:

https://github.com/jcroav/room-reservation

A partir de ahora comenzaré a utilizar más a menudo GitHub para subir mis desarrollos, e incluso ejemplos y demos que vaya publicando en mi blog.

Os animo a todos a que ayudéis RoomReservation o propongáis aspectos de mejora, serán siempre bienvenidos. Yo también intentaré aprovechar todo lo que he aprendido y lo que vaya a aprender próximamente para seguir mejorando esta App y desarrollando algunas nuevas.

¿Cuáles son los aspectos de mejora?

Como por supuesto no creo que sea perfecta, ni mucho menos, tengo ya previstas algunas áreas de mejora. Próximamente espero seguir trabajando en mejorar la interfaz gráfica, hacerla más adaptativa, añadir alguna funcionalidad extra y trabajar en los idiomas de la App, ya que ahora mismo solo está en inglés.

Pues nada más de momento, lo dicho, espero que os sirva como siempre, y que lo encontréis útil.

Un saludo y buena semana.

Usando Promises con JQuery y API REST de SharePoint

Hola de nuevo a todos,

Empezando a trabajar con Javascript de forma intensiva, me he topado con la necesidad de usar un concepto del que había oído hablar hace tiempo, pero que hasta ahora no me había hecho falta. Este concepto es lo que se conoce como Promesas o Promises.

Cuando estamos trabajando con varias APIs a través de peticiones asíncronas o con una API pero realizamos varias peticiones a la vez, al final te puedes encontrar con el problema de que, peticiones que necesitas que se ejecuten después que otras lo están haciendo antes, por la propia naturaleza de las llamadas asíncronas. Puede ocurrir también que, funciones que necesitas que se ejecuten al finalizar las llamadas asíncronas lo estén haciendo antes. Para evitar esto puedes pensar en un primer momento en una montón de llamadas asíncronas anidadas en el callback de éxito de las llamadas asíncronas o por el contrario, usar las promesas.

¿Que son las promesas?

Para explicar lo que son las promesas, voy a tomar prestado la explicación de la siguiente entrada de un blog que he encontrado:

http://www.funcion13.com/2012/03/26/comprendiendo-promesas-y-deferreds-en-jquery/

La promesa (promise) en sí, es un objeto que representa un evento en el tiempo, como la llegada de datos de una petición asíncrona o el fin del procesamiento de todos los datos obtenidos en una función asíncrona. Un objeto promise es una versión de solo lectura de un objeto deferred.

Los deferred comienzan en estado pendiente, y es lógico si lo pensáis, pudiendo pasar a estado resuelto (la tarea se completó) o rechazado (si es que falló). En el momento en que un deferred cambie su estado a resuelto o rechazado no podrá cambiar nunca más.

Vamos a usar las promesas para manejar peticiones asíncronas, y controlar que se reciba una respuesta sea correcta o no. Además, con JQuery, podremos controlar, la respuesta de varias peticiones asíncronas por medio de promesas que se ejecutan simultáneamente. El código javascript, además, quedará mucho más ordenado.

Os dejo algunos enlaces que he usado yo para entender bien el concepto de promises.

http://www.danieldemmel.me/blog/2013/03/22/an-introduction-to-jquery-deferred-slash-promise/

http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

http://api.jquery.com/deferred.promise/

¿Cómo se usan las promesas?

En el ejemplo que os voy a mostrar a continuación, he usado las promesas para trabajar con la API REST de SharePoint. En mi caso, en una aplicación que estoy desarrollando, necesitaba hacer varias peticiones API REST para saber si varias listas existían para, en función de si lo hacían o no indicarlo al usuario para darle la oportunidad de crearlas.

El problema me surgía por la dificultad de controlar el resultado de las peticiones, ya que al ser asíncronas, las evaluaciones de su resultado se hacían antes de que la petición se hubiera realizado realmente, por lo que no funcionaba como quería que lo hiciera.

Para ello he usado el concepto de promesas, por medio de ellas, puedo controlar que todas las peticiones se hagan y den un resultado, y esperar hasta que se obtenga la respuesta de todas para llevar la acción que corresponda. Voy a mostraros el código y posteriormente explicarlo, en mi caso he usado la librería JQuery y el objeto Deferred de la misma:

function ExistList(listname) {

        var deferred = $.Deferred();

        var appweburl = GetQueryStringParameter("SPAppWebUrl");
        var hostweburl = GetQueryStringParameter("SPHostUrl");

        var executor = new SP.RequestExecutor(appweburl);

        executor.executeAsync({
            method: "GET",
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/Lists/GetByTitle('" + listname + "')?@target='" + hostweburl + "'",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                deferred.resolve();
            },
            error: function (data) {
                console.log(data);
                deferred.reject();
            }
        });

        return deferred.promise();
    }

function Validation(){

            var list1 = "List1";
            var list2 = "List2";

            $.when(ExistList(list1), ExistList(list2))
             .done(function () {

             })
             .fail(function () {
                 RedirectApp("Install.aspx");
             });
                
        }
  • El primer aspecto importante se encuentra en la función ExistList y es el objeto deferred, se crea al inicio de la función y al final de la función se devuelve la promesa. Dentro de la función se hace la petición asíncrona y en el callback de éxito y de error de la misma llamada, se indica si la promesa se ha cumplido (por medio de deferred.resolve()) o no (por medio de deferred.reject()) respectivamente.
  • En la función de validación el primer elemento importante es la llamada $.when() a la que le indicaremos como parámetro (tal y como podemos ver), todas las promesas que queremos que espere a obtener resultado. Los callback no se ejecutarán hasta que no se haya recibido la respuesta de todas las promesas indicadas como parámetro de la llamada.
  • En el caso de que todas las promesas hayan respondido positivamente se utiliza el callback de la llamada done.
  • En el caso de que alguna de las promesas haya respondido negativamente se ejecuta el callbak de la llamada fail

En ocasiones también podremos encontrarnos que se usa la llamada then que lleva como parámetros los dos callback de éxito y error, pero a mi me ha resultado más claro usar la llamada done y fail.

Cuando trabajamos con JQuery y usamos la llamada $.ajax(), ya de por sí devuelve una promesa por lo que no debemos encapsularla en una función para crear el objeto deferred.

Y con esto espero que si alguien no sabía o no había trabajado habitualmente con promesas, encuentre aquí como trabajar con ellas. Aunque el trabajo con promesas no es una novedad, hasta ahora no había necesitado trabajar con ellas, me ha resultado muy fácil, además de que el código queda muchísimo más limpio y organizado.

Un saludo a todos.