sábado, 21 de enero de 2017

Azure, Office 365 y servicios personalizados

Hace algunas semanas uno de nuestros clientes nos llamó por un desarrollo express, de aquellos que te erizan la piel por el reto que implican.

Inicialmente nosotros ayudamos a desarrollar su Intranet sobre SharePoint Online y nos encontrábamos cerrando dicho proyecto cuando nos solicitaron un desarrollo especial. Su complicación fue que, al migrar una de sus soluciones core, varios de sus servicios se cayeron entre ellos, un módulo que les permite saber cuando una persona se encuentra en su puesto de trabajo; es decir, un módulo para hacer Check In.

Fue así como nos solicitaron de manera urgente, desarrollar una pequeña solución sobre SharePoint Online, que permitiera a una persona indicar cuando estuviera en su posición de trabajo, esto coordinado con un itinerario que nos enviarían desde otro sistema y usando archivos de texto sincronizados cada 5 minutos en un servidor SFTP.

Con estas premisas en frente aceptamos el reto, claro está con ciertas condiciones de infraestructura que pusimos sobre la mesa para poder hacer factible la sincronización de los sistemas sin la acción de alguna persona que manualmente realizara alguna actividad.

Lo primero que hicimos fue aceptar que no era suficiente con Office 365 para poder cumplir con el requerimiento. 

Lo segundo que hicimos fue generar una infraestructura factible que nos permitiera:

  1. Comunicarnos con un servidor SFTP para descargar los archivos de texto con los itinerarios.
  2. Procesar los archivos de tal forma que nos permitiera realizar consultas de información y responder en cuestión de instantes.
  3. Gestionar los accesos desde Office 365 para conocer al usuario que busca hacer Check In y permitirle o informarlo de la hora adecuada para registrar su acceso (un nice to have en la aplicación es conocer las coordenadas en las que se encuentra la persona para poder hacer Check In)
  4. Registrar el Check In y enviar la información al sistema origen en el mismo formato de texto.
  5. La información entre el sistema origen y Office 365 debe sincronizarse cada 5 minutos.
Para cumplir con toda esta infraestructura nos valimos del servicio Azure y toda la gama de opciones para ofrecer una aplicación disponible las 24 horas, completamente elástica y con los niveles de seguridad que nos solicita el cliente.

El resultado final se puede ver a grandes rasgos en la siguiente imagen.

En ella se muestra que:
  1. En Office 365 se generó un App para SharePoint que funciona como la cara al usuario y en donde se le ofrecen todos los controles e información necesaria para hacer Check In.
  2. En Azure se montó una aplicación Web que se encarga de intercambiar la información entre todos los actores que intervienen. Esta aplicación se encarga de conectarse al servidor SFTP y procesar la información en los documentos de texto. La información procesada se envía a una Base de Datos, también en Azure. Finalmente, también procesa las peticiones de itinerario y Check In que realizan los usuarios.
  3. Se generó una Base de datos en SQL Azure que almacena toda la información  procesada por la aplicación Web. Además de que almacena los Check Ins con fecha, hora y posición geográfica.
  4. Se montó un Web Job que cada cinco minutos realiza una petición a la aplicación Web para sincronizar la información del servidor SFTP.
Como ustedes pueden ver, este pequeño monstruo nos puso a prueba en nuestros conocimientos sobre la plataforma y, gracias a la oferta de servicios actual sobre distintos tipos de tecnología, fue que se pudo resolver el reto. Después de esta pequeña nota me gustaría conocer, ¿Ustedes cómo lo habrían solucionado?

¡Les agradezco de antemano sus comentarios y pasen un excelente reto!

martes, 18 de octubre de 2016

Webpart para auditar documentos en SharePoint Online

En varias ocasiones nos han preguntado si SharePoint cuenta con alguna capacidad de auditar quién ha visto, descargado, modificado o borrado algún documento dentro de la plataforma; e incluso, hay personas mucho más pro-activas que se dieron a la tarea de leer los post que ofrece Microsoft y dan por sentado que la plataforma lo permite. 

Ahora, esto llega a generar cierta confusión en ellos cuando se adentran al detalle de la nota en la que pone las diferencias entre la plataforma en la nube (es decir SharePoint Online) y la plataforma On-Premise (dentro de nuestra infraestructura de servidores); y es completamente normal; como normal es que yo no conozca a profundidad de leyes, contabilidad, marketing, microbiología, etc. Para esos casos en los que necesitas entender o resolver esos pequeños-grandes detalles, siempre lo recomendable es acudir con un experto.

Pues bien, en esta ocasión les comparto mi experiencia con este requerimiento que a mi parecer es muy normal, sobre todo, saber quién ha leído un documento dentro de SharePoint Online (que al momento de escribir este post, no permite auditar las descargas o las lecturas de documentos en la plataforma de manera natural). Para exponer el caso tomemos en cuenta lo siguiente:

  1. SharePoint es una plataforma flexible que permite crear tantos sitios web como bibliotecas de documentos tengamos en mente. El orden o estructura se lo podemos dejar a la imaginación, por lo que la solución que demos, debe ser tan flexible como bibliotecas tengamos.
  2. Los usuarios que gestionan el contenido en los sitios quieren saber qué usuarios acceden al contenido que ellos publican en los diversos portales.
  3. Se espera que los documentos sean compartidos en diferentes formas como:
    1. Vínculos mediante correo electrónico.
    2. Colocando los vínculos en los menús o portales como accesos directos.
Con estos puntos en mente, pensemos en un sistema mailing, de estadísticos de twitter e incluso, de las búsquedas en Google. Si observamos un poco sobre el comportamiento del navegador al momento de presionar los vínculos, antes de enviarnos a la página que esperamos ver nos envía a una página en blanco y posteriormente nos redirecciona a la liga que seleccionamos, es justa en esa página en blanco en la que estamos entregando información sobre nuestro comportamiento sin darnos cuenta de ello.


Trasladando esta observación a nuestro escenario, ¿podríamos nosotros proponer una pequeña App que modifique los vínculos de los documentos y que, éstos vínculos nos permitan enviar a los usuarios que los presionen a una página intermedia que registre el comportamiento? 
Claro que sí y para ello, habría que crear un proyecto en Visual Studio con la plantilla de SharePoint Add-In. 
Para ello:


1. Ya con nuestro proyecto creado, agregué una página web llamada URLAuditable.aspx
2. Agregué una página web llamada Analytics.aspx

3. Agregué un nuevo ítem del estilo Menu Item Custom Action al que llamé GetURLAudit y seleccioné la página creada en el punto 1.
Agregando el Control para el menú

4. Este ítem te permite asociarlo a tipos de contenido Biblioteca de documentos e indicar que se presentará en las bibliotecas de documentos del sitio Host. 
Asignando en dónde se mostrará el nuevo control
Indicando la página que se mostrará al presionar el botón.
Si observamos un poco el XML creado por el ítem, éste nos indica que proporcionará como parámetro en la URL el ID de la lista y el ID del ítem con lo que podremos recuperar (usando un poco de programación con JavaScript) toda la información del documento.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="8c10803a-8293-427b-84b1-250036b46c18.GetURLAudit"
                RegistrationType="List"
                RegistrationId="101"
                Location="EditControlBlock"
                Sequence="10001"
                Title="Obtener URL Auditable">
    
    <UrlAction Url="~appWebUrl/Pages/URLAuditable.aspx?{StandardTokens}&amp;SPListItemId={ItemId}&amp;SPListId={ListId}" />
  </CustomAction>
</Elements>

5. En la página a la que nos referencía construiremos la URL que nos ayudará a capturar el comportamiento de los visitantes. Para ello, agregué en la página un campo de texto.

Campo de texto para construir una URL
6. Cree una lista de SharePoint llamada Registro en la que capturaré todos los eventos de carga de la página Analytics.aspx.
Agregando la lista para registrar las visitas.
7. En la página ULAuditable.aspx coloqué el siguiente código jQuery para generar la nueva liga:

var hostweburl;
var appweburl;
var idLista;

$(document).ready(function () {
    hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
    appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
    
    var scriptbase = hostweburl + "/_layouts/15/";

    $.getScript(scriptbase + "SP.RequestExecutor.js", getListName);
});

function getListName() {
    var executor = new SP.RequestExecutor(appweburl);
    idLista = decodeURIComponent(getQueryStringParameter("SPListId")).replace("{", "").replace("}", "");
    
    executor.executeAsync(
        {
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists(guid'" + idLista + "')?@target='" + hostweburl + "'",
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                var jsonObject = JSON.parse(data.body);
                var tituloBiblioteca = jsonObject.d.Title;

                getDocumentName(tituloBiblioteca);
            },
            error: onQueryFailed
        }
    );
}

function getDocumentName(tituloBiblioteca) {
    var executor = new SP.RequestExecutor(appweburl);
    var iditem = decodeURIComponent(getQueryStringParameter("SPListItemId")).replace("{", "").replace("}", "");
    
    executor.executeAsync(
        {
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists/GetByTitle('" + tituloBiblioteca + "')/items(" + iditem + ")/File?@target='" + hostweburl + "'",
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                var jsonObject = JSON.parse(data.body);

                $("#txtURLEditable").val(appweburl + "/Pages/Analytics.aspx?SPHostUrl=" + encodeURIComponent(hostweburl) + "&SPAppWebUrl=" + encodeURIComponent(appweburl) + "&SPListId=" + encodeURIComponent("{" + idLista + "}") + "&SPListItemId=" + iditem);
                
            },
            error: onQueryFailed
        }
    );
}

8. En la página Analytics.aspx coloqué el siguiente código para capturar el evento de acceso:

var hostweburl;
var appweburl;
var idLista;
var context, web;

$(document).ready(function () {
    context = SP.ClientContext.get_current();
    web = context.get_web();

    context.load(web);
    context.executeQueryAsync((this, function (sender, args) {
        getListName();
    }),
    Function.createDelegate(this, onQueryFailed));
});

function getListName() {
    var executor = new SP.RequestExecutor(appweburl);
    idLista = decodeURIComponent(getQueryStringParameter("SPListId")).replace("{", "").replace("}", "");

    executor.executeAsync(
        {
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists(guid'" + idLista + "')?@target='" + hostweburl + "'",
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                var jsonObject = JSON.parse(data.body);
                var tituloBiblioteca = jsonObject.d.Title;

                getDocumentName(tituloBiblioteca);
            },
            error: onQueryFailed
        }
    );
}

function getDocumentName(tituloBiblioteca) {
    var executor = new SP.RequestExecutor(appweburl);
    var iditem = decodeURIComponent(getQueryStringParameter("SPListItemId")).replace("{", "").replace("}", "");

    executor.executeAsync(
        {
            url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists/GetByTitle('" + tituloBiblioteca + "')/items(" + iditem + ")/File?@target='" + hostweburl + "'",
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: function (data) {
                var jsonObject = JSON.parse(data.body);

                registrarEvento(jsonObject.d.Name, tituloBiblioteca, jsonObject.d.LinkingUrl);
            },
            error: onQueryFailed
        }
    );
}

function registrarEvento(nombreArchivo, tituloBiblioteca, url) {
    var oList = context.get_web().get_lists().getByTitle('Registro');
    var itemCreateInfo = new SP.ListItemCreationInformation();
    var oListItem = oList.addItem(itemCreateInfo);

    oListItem.set_item('Title', nombreArchivo);
    oListItem.set_item('Biblioteca', tituloBiblioteca);
    oListItem.update();

    context.load(oListItem);

    context.executeQueryAsync(
        Function.createDelegate(this, function (sender, args) {
            console.log("Evento registrado");
            window.location = url;
        }),
        Function.createDelegate(this, onQueryFailed));
}

Si observan el código, es muy similar al del punto 7 y puede haber algunas cosas que no sean necesarias debido a que es un ejemplo recortado del requerimiento original, en el que nos pidieron leer propiedades adicionales del usuarios como su número de Empleado y su Puesto. Sin embargo, creo que funciona bien para los propósitos del post.

9. Le di permisos de lectura al App desde el archivo Manifest.xml a la colección de sitios y los perfiles de usuarios (este último por si haces lectura desde los perfiles de usuario para traer información adicional).


10. ¡Finalmente, a probar!

Una vez corriendo la aplicación, todas las bibliotecas de documentos en el sitio Host tendrán un nuevo control que nos permitirá crear la URL, compartirla y al abrirla, registrar el evento de lectura.

Control en biblioteca de SharePoint Online
Registro de auditoría en la lista personalizada dentro del App.

¡Espero que este post les sea de utilidad y de ser así, por favor compartan y comenten que estamos para escucharlos!

También están invitados a conocer Chimalli Apps en donde colocamos soluciones empresariales para ustedes.

martes, 11 de octubre de 2016

Flujos de trabajo en Apps Parte1

Desde tiempo atrás, los flujos de trabajo han sido una parte importante de varios proyectos de colaboración y automatización de procesos empresariales.


Aunque es cierto que en cada organización, los procesos se ajustan de acuerdo a sus capacidades, necesidades, madurez, etc.; lo cierto es que una herramienta de desarrollo de soluciones como los flujos de trabajo no pueden quedar fuera del mapa de complementos de SharePoint y SharePoint Online.

Diagrama del flujo de trabajo
Al momento de este post, la documentación sobre los flujos de trabajo es limitada, sin embargo, esperemos que con este escrito puedas comprender un poco de sus capacidades y limitantes.

Para nuestro ejemplo, generaremos un flujo de aprobación sencillo en dos pasos. El flujo estará asociado a una lista dentro de nuestra App y se iniciará automáticamente al crear un nuevo ítem.

En cuanto inicie el flujo, esperará a que el ítem alcance el estatus Enviado con lo que generará una tarea de aprobación.
Antes de finalizar el flujo, éste notificará al autor del ítem del resultado del proceso de aprobación mediante un correo electrónico.

En la imagen de la derecha puedes ver el flujo que se busca alcanzar.

Lo primero a realizar es crear es nuestra estructura de datos, es decir, nuestras columnas de sitio, tipos de contenido y listas/biblioteca (Si requieres más información de cómo crear Columnas, Tipos de contenido y listas/bibliotecas, por favor, revisa nuestro post anterior). Nosotros ocuparemos:

Proyecto en Visual Studio
  1. Columna: Estatus
  2. Tipo de contenido: Solicitud
  3. Lista: Solicitud
En la imagen puedes ver cómo queda configurado nuestro proyecto en Visual Studio. El orden o nombre de las carpetas no es necesario sin embargo, lo recomiendo mientras se trabaja con proyectos grandes.

El siguiente paso es crear nuestro flujo de trabajo. Para el ejemplo nos enfocaremos en un flujo secuencial; y al igual que con la estructura de datos, comenzaré colocando una carpeta en la que agregue todos los flujos derivados del proyecto. Sólo como nota personal, sean pacientes cuando trabajan con flujos en su lista, esto debido a que el tamaño del proyecto crece considerablemente y generar pruebas será cada vez más lento.

Para agregar nuestro primer flujo, presiona con el botón derecho del mouse sobre la carpeta Workflow y selecciona las opciones Add > New Item del menú contextual. Esta actividad lanzará el Wizard con el que podemos elegir el objeto Workflow. Nombra el flujo de acuerdo a tus estándares y presiona el botón Add.
Agregando un objeto de tipo Workflow al proyecto
Una vez presionado el botón Add, se dispara un nuevo Wizard en el que colocamos el Nombre del Workflow y el Tipo de flujo a generar (sitio o lista). Nosotros ocuparemos el tipo Lista.
Seleccionando el tipo de flujo de trabajo
Presiona el botón Next para continuar. En la siguiente pantalla, el Wizard solicitará seleccionar la lista o biblioteca a la que se vinculará, así como la opción de seleccionar las lista de tareas e historial.
Configurando los insumos para el flujo e trabajo
Presiona el botón Next. En la siguiente pantalla puedes indicar en qué momento se inicia la ejecución del flujo de trabajo (Manualmente, al crear un ítem o, al modificar un ítem). Para este ejemplo seleccionaré: manualmente y al crear un ítem.
Configurando la ejecución del flujo de trabajo
Al presionar Finish, Visual Studio creará el flujo de trabajo y las listas de Tareas e Historial requeridas. Tienes la opción de mover las listas a las carpetas para mantener un orden en tu proyecto.
Proyecto con el flujo de trabajo
En el área central de Visual Studio se tiene el lienzo en el que puedes comenzar a arrastrar las actividades que te permiten los flujos de trabajo. Existen varias herramientas y, a diferencia de los flujos de trabajo de SharePoint 2010 y 2013, éstos te limitan el uso de código como C#; es decir, sólo podrás colocar código en determinadas partes de tu proyecto y, no podemos hacer uso de todo el abanico de opciones que tiene C#, sin embargo, la extensión de herramientas es bastante buena para los fines del ejercicio.
En este punto del proyecto, te sugiero que vayas guardando periódicamente porque llega a ocurrir que Visual Studio se congele y se cierre automáticamente sin poder recuperar tus ajustes y configuraciones del flujo de trabajo.

Comencemos por identificar las áreas de herramientas y configuración en Visual Studio. En la siguiente imagen se muestra en rojo cuatro secciones principales:


Áreas de trabajo en Visual Studio
Del lado izquierdo tenemos todas las herramientas de las que disponemos, te recomiendo que le des un buen vistazo a cada una de las opciones y sus propiedades para que te vayas dando una idea de qué parámetros requiere de entrada y cuál es su salida. En el área central arriba tenemos el lienzo en el que iremos acomodando cada una de nuestras herramientas y formando el flujo de trabajo. En el área central abajo, tenemos tres espacios, el primero es para definir las variables de nuestro flujo de trabajo (así como su ámbito o alcance, es decir, en dónde estarán vivas nuestras variables); el segundo espacio es para definir argumentos de entrada y salida; finalmente, el último espacio permite agregar librerías de C# estándar. Finalmente, del lado derecho está la ventana de propiedades que nos permite configurar cada uno de los controles que coloquemos sobre el lienzo. Si en algún momento tienes dudas de qué tipo de información se requiere como entrada para algún componente, coloca el mouse sobre el nombre del Input y éste te dará una idea de la entrada requerida; no es mucho pero es algo jejeje.

Cuando nuestro flujo de trabajo inicia, coloco un control del tipo LookupSPListItem, con el que puedo recuperar la información del ítem actual. Para ello simplemente configuro sus propiedades tal como lo muestra la imagen siguiente.
Obteniendo el ítem actual
Una vez configurado el ítem actual, presiona el vínculo Get Properties; con ello se agregará automáticamente el control GetDynamicValueProperties con la variable dv_0 como fuente. Si prestas atención a la imagen anterior, el resultado de nuestro control LookupSPListItem es dv_0. Al presionar el vínculo View properties..., éste nos dará un cuadro de diálogo con el que podremos:

  1. Seleccionar la lista de la que se obtienen los datos con el control Entity Type.
  2. Seleccionar los campos que nos interesa recuperar con el cuadro de selección en la columna Path.
  3. Generar las variables y asignarles el valor que obtienen del ítem de la lista al presionar el vínculo Populate Variables.

Al configurar los campos y presionar el botón OK, nuestra recuperación y asignación de información quedará completa. El siguiente control que usamos es WaitForFieldChange, el cual espera a que ocurra un evento en el ítem actual y valida si un campo determinado cuenta con un valor específico. En la siguiente imagen se muestra la configuración que valida si el campo Estatus cuenta con el valor "Enviada". Si observas la manera en la que se escribe el valor del campo, te darás cuenta de en qué momento se usa código C#.

Para nuestro siguiente post, mostraremos cómo generar el proceso de aprobación mediante tareas y la personalización de los correos electrónicos usando HTML; así como ciertas consideraciones sobre los límites al desarrollar los flujos.
Espero que este post les de un buen inicio a sus proyectos y recuerden comentar sobre lo que les pareció el artículo y los temas que les gustaría que tocáramos.

¡Mucha suerte y felices trazos!

martes, 22 de marzo de 2016

Property Bag - Buenas prácticas al desarrollar software

Es de lo más común desarrollar software en el que determinadas piezas tienen que ser configurables. Un ejemplo de estas piezas serían aquellas que conectan a base de datos y requieren cadenas de conexión en los que intervienen el nombre del servidor, puerto, instancia de base de datos, etc.
¿En dónde colocan ustedes esa información necesaria para que su aplicación funcione? Hay varias técnicas y todas ellas intentan mantener el mayor equilibrio posible entre Facilidad para gestionar la información y Seguridad de la información. Algunas de ellas pueden ir desde escribir ésta información directamente en el código (muy mala idea); usar archivos XML como el web.config (no recomendable); hasta colocarla en una base de datos diferente (lo cual trae de nuevo el mismo problema). 

En SharePoint existe el mismo reto cuando desarrollamos soluciones para dicha plataforma pero, con un abanico más amplio de herramientas; por ejemplo, crear una lista con dos columnas (Clave y Valor). Su función es la misma que un diccionario en el que la clave nos permite vincular la información con alguna variable en nuestro código y el valor nos da la información que necesitamos. La solución es sencilla; no necesitamos de cadenas de conexión gracias al API de desarrollo; útil y nos provee de una interfaz de usuario con la que podemos gestionar la información. El problema está en la seguridad y es que las listas son rastreables por el motor de búsqueda o bien, se puede acceder a la información desde: Todo el contenido del sitio.
Ciertamente hay trucos para evitar estos inconvenientes pero, probemos una solución mas "elegante". 

Existe una herramienta conocida como Property Bag que nos permite gestionar información y que no es rastreable por el motor de búsqueda ni está disponible por alguna interfaz de usuario en SharePoint. Su funcionamiento es igual que el de un diccionario y la forma en la que lo trabajamos es la misma que con un Ítem de una lista en SharePoint; lo mejor es que está disponible en las versiones más recientes de SharePoint, incluso en el Online.

En el siguiente ejemplo ocupo el API de desarrollo de SharePoint con JavaScript para almacenar una variable llamada _Propiedad_Conexion en el Property Bag.


function crearInformacion() {
    var context = SP.ClientContext.get_current();
    var web = context.get_web();
    var informacion = "Información necesaria para conexión";

    context.load(web);
    var webProperties = web.get_allProperties();
    var propiedad = webProperties.set_item("_Propiedad_Conexion", informacion);
    web.update();

    context.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
        alert("Información creada/actualizada correctamente");
    }),
        Function.createDelegate(this, this.onQueryFailed)
    );
}

En la siguiente función recuperaremos la misma variable del Property Bag. Recuerden ocupar las secciones try/catch para evitar interrupciones de código en el caso de que no exista la variable en el Property Bag.


function cargarInformacionMapa() {
    var context = SP.ClientContext.get_current();
    var web = context.get_web();
    var webProperties = web.get_allProperties();

    context.load(webProperties);

    context.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
        try {
            var propiedad = webProperties.get_item("_Propiedad_Conexion");
            //TODO: Hacer algo adicional.
        } catch (error) {
            console.log("No existe la variable en el Property Bag");
        }
    }),
        Function.createDelegate(this, this.onQueryFailed)
    );
}


Espero que este post les ayude a construir mejores soluciones y recuerden, felices trazos jejeje.

viernes, 26 de febrero de 2016

RemoteEndpoint SharePoint POST

¿No les ha pasado que justo la información que ustedes necesitan para desarrollar un método está incompleta?
Seguramente sí y creo que es una práctica común en el medio de la Tecnología de la Información.
Hace un par de semanas comencé un pequeño desarrollo sobre SharePoint Online en el que necesitaba enviar información capturada en un Add-In de SharePoint hacia un servicio web alojado en otro servidor. Para ello se necesita hacer uso de una herramienta conocida como RemoteEndPoint que proporciona SharePoint para estos casos pero ¿Cómo funciona?
Sencillo (o por lo menos eso es lo que te hace ver la documentación jejeje). Lo primero es seguir la documentación oficial. Ahí nos indican cómo funciona por medio de un diagrama.
Funcionamiento del Remote Endpoint en SharePoint
La idea es simple, nuestro navegador interpretará los archivos JavaScript del Add-In, cuando quiera hacer uso de recursos externos como los de un Web Service ocupará a SharePoint como el intermediario, es decir: le indicará a dónde conectarse, el método, los datos y la respuesta que espera recibir de él entre otras cosas. 
SharePoint tomará la información e intentará realizar la petición; si ésta se lleva a cabo correctamente, el recurso externo enviará la respuesta a SharePoint y éste la enviará al Add-In.
En la documentación ustedes encontrarán cómo crear el proyecto en Visual Studio, cómo formar la solicitud para consultar datos de un Web Service desde JavaScript y finalmente, les indica que registren el RemoteEndPoint en el AppManifest.xml pero, ¿qué ocurre en el caso en el que quieres enviar información al Web Service? Bueno, lamentablemente esa información no está disponible ahí... así que, en este caso nos toca a nosotros encontrar el cómo sí hacer la llamada.
El siguiente ejemplo es muy sencillo pero ejemplifica cómo realizar la actividad.
Pensemos en un pequeño formulario dentro del Add-In que ayude a los desarrolladores a obtener Feedback de sus aplicaciones y, necesitan que la información esté almacenada fuera de Office 365. Para ello se desarrolló un Web Service en Azure que reciba las llamadas y envíe la información a base de datos.
La siguiente función muestra todas las consideraciones para realizar la llamada desde JavaScript y enviar la información:
Los puntos interesantes se encuentran en las líneas 45 y 53 a 58.
En la línea 45 definimos los datos que queremos enviar al Web Service, los cuales son transformados a una cadena de texto correctamente-formada para el envío. Si esto no sucede la llamada al servicio web fallará...
En la línea 53 se define el método en POST que es el ocupado para enviar información (aunque también funciona bien para obtener datos).
En la línea 54 se define el tipo de contenido, esta línea varía ligeramente de la documentación oficial.
Finalmente la línea 58 define el cuerpo de la solicitud, es decir, los datos que serán enviados.
El resto de las líneas son similares al ejemplo en el sitio de Microsoft.

Espero que este pequeño post les haya servido, prueben y comenten porque su retro es muy importante.

Saludos y felices trazos jejeje.

lunes, 14 de diciembre de 2015

Formularios de listas personalizados

En un post anterior escribimos sobre la estructura de datos. En él mostramos cómo definir listas usando archivos XML que están empaquetados en nuestras soluciones para SharePoint sin embargo, los formularios con los que se implementa la solución son los estandar y cuentan con las validaciones básicas como la de campos requeridos.
Para poderle dar un poco más de vida a los formularios que ofrece SharePoint nosotros podemos personalizarlos tanto como queramos (claramente el esfuerzo dependerá de cuánta personalización desees). En este ejemplo les mostraré cómo definir un formulario personalizado al que se le agrega una referencia javascript/jQuery.

Agregando el formulario

El primer paso es expandir la serie de archivos en el que se define la lista en nuestro proyecto de Visual Studio y agregar un ítem al mismo nivel que el archivo Schema.xml.
En el cuadro de diálogo seleccione la opción Page que nos permite agregar un aspx a nuestra App. Tome nota del nombre que le di al archivo (DisplaySolicitud.aspx). Ésto es sólo una convención ya que se pueden agregar tres páginas diferentes por lista: una para Insertar un ítem, otra para Editar el ítem y la última para Visualizar el ítem. El procedimiento para cualquiera de éstos formularios es exactamente el mismo.
Al presionar el botón Add se creará el archivo. Es de notar que sólo se crea el aspx, esto debido a que en mi caso, es un proyecto para SharePoint Online pero, para aquellos que realizan soluciones para On premise, la página se creará con los archivos cs.
Un punto importante es cambiar la propiedad Deployment Type de los archivos aspx a ElementFile. En el caso de omitir este punto, ocurrirá un error al intentar implementar la solución.
El siguiente paso es modificar el archivo aspx agregando las referencias a los archivos CSS y JavaScript a utilizar. Todas las referencias las coloqué en el bloque Content que hace referencia al ContentPlaceHolderId="PlaceHolderAdditionalPageHead". Ten especial atención con la ruta relativa de los archivos que llamarás debido a que no concuerda con la estructura de carpetas del proyecto, en su lugar, concuerda con la ruta de implementación de los archivos en SharePoint.
En el bloque Content que hace referencia al ContentPlaceHolderId="PlaceHolderMain" es necesario agregar el WebPartZone con ID="Main". En ésta zona se crearán los controles que requiere la lista para capturar o mostrar los valores de cada uno de sus campos.
El archivo DisplaySolicitud.js contiene una función sencilla que envía un mensaje cuando se carga la página, por ello no entraré en detalle de ese archivo.
El último paso de configuración se da en el archivo Schema.xml de la lista que estamos trabajando. Es necesario expandir la sección Forms y editar la opción del formulario que nos interesa. Para el caso de éste ejercicio edité el nodo marcado con Type="DisplayForm"

  • Lo primer es cambiar la Url del formulario, para ello sólo coloco el nombre del archivo aspx que agregué (DisplaySolicitud.aspx). 
  • El siguiente paso es cambiar SetupPath por Path y colocar el nuevamente el nombre del formulario. 
  • Finalmente, agregué el atributo UseLegacyForm="FALSE".

Ya con todos los puntos cubiertos podemos pasar a probar el formulario utilizando F5 para implementar la solución. Una vez cargada la solución, me dirigí a la lista y agregué un ítem que posteriormente abrí para que SharePoint mostrara el formulario DisplaySolicitud.aspx que fue el que agregamos en este ejemplo.

Conclusión y siguientes pasos

Este ejemplo aunque sencillo, demuestra una forma de editar los formularios de SharePoint de tal manera que se pueden empaquetar y son implementados sin un esfuerzo adicional de configuración. El mayor provecho se obtiene al usar el API de SharePoint basada en JavaScript del cual platicaremos más adelante; pero no sólo eso, el manejo de los estándares web HTML, CSS y Javascript ayudará a darle una mayor funcionalidad con menor esfuerzo dado a que varias de las validaciones las puede controlar SharePoint y el resto se pueden hacer mediante Javascript.
Nuevamente recuerden que todos sus comentarios son bienvenidos. Saludos y la mejor de las suertes en sus proyectos.

Primero lo primero, la estructura de datos

La primer vez que me dediqué a desarrollar una solución sobre SharePoint recuerdo sentarme frente a la computadora, poner las manos en el teclado y decir: ¿por dónde comienzo? Fue ahí que me di cuenta que el primer paso que siempre hacía (definir la estructura de la base de datos) cambiaba de enfoque debido a que en lugar de tener tablas y vistas ahora tenía listas y bibliotecas (además de columnas de sitio y tipos de contenido que en ese momento no sabía a ciencia cierta cómo usar). Mi primer paso fue apoyarme de la interfaz gráfica para crear las listas a ocupar. Aprendí cuándo usar una lista para un catálogo y cuándo una columna de tipo Elección, etc.
Después de un tiempo desarrollando, la primer pregunta sigue siendo la misma: ¿cómo almacenaremos los datos? y, ya pensando un poco a futuro y, sobre todo en colocar la solución en un ambiente de pruebas y el productivo; uno busca la forma de ahorrarse el tiempo que pasaría en el navegador definiendo las listas o resolviendo problemas de compatibilidad con plantillas, entre otras curiosidades que se pueden encontrar al migrar la solución de una granja a otra o bien, entre versiones de las granjas.
La solución es sencilla (requiere su esfuerzo pero es sencilla jeje). Define tu modelo de datos en el proyecto de Visual Studio, empaquétalo y cuando lo implementes en otras granjas éste se creará completo ahorrándote todo el tiempo que se invierte en pulir esos detalles del pase a producción.

Pongamos un ejemplo. 

Ejemplo de modelo Entidad Relación del escenario propuesto
Pensemos en un sistema en el que un usuario registra un rango de fechas para solicitar licencia. Dicha solicitud almacena el usuario, la fecha de inicio y fin del periodo, el nombre de la persona que tomará sus responsabilidades en ese periodo y una categoría para la licencia (vacaciones, seminario/capacitación, razón médica).
Al leer el ejemplo y pensar en un modelo de base de datos tradicional, uno vislumbraría una tabla en la que se almacene el catálogo de "Categorías de licencia" y una tabla en la que se almacenan las solicitudes.
Ejemplo simplificado en SharePoint

En cambio, si ocupamos las opciones que nos provee SharePoint al momento de diseñar las listas, podríamos ahorrarnos una de las tablas si en lugar de usar una lista adicional ocupamos un campo de tipo Elección. Claro está que la elección de la solución depende del requerimiento sin embargo, no nos compliquemos la vida y pensemos en que el campo de tipo Elección es adecuado al requerimiento.

Esto nos indica que tendremos dos campos de tipo Fecha, un campo de tipo persona y un campo de tipo elección.
Podemos dispensar del campo ID y Solicitante ya que SharePoint nos provee con estos de forma automática.

El proyecto de Visual Studio

Una vez identificada nuestra estructura de datos abramos Visual Studio y creamos un proyecto de SharePoint. Las siguientes opciones funcionan tanto para Soluciones On Premise como On Line por lo que siéntete con la seguridad de que funcionará (por favor revisa la sección probando jejeje). En este caso estamos usando Visual Studio Community 2015. En la siguiente liga coloco un post de cómo configurar Visual Studio para poder usar las plantillas de proyectos de Office/SharePoint. Nosotros usaremos la opción de App's para SharePoint Online.
En Visual Studio selecciona la opción File > New Project y selecciona Templates > Visual C# > Office/SharePoint y selecciona SharePoint Add-in.
Creando un proyecto para SharePoint Online
Al presionar el botón OK, Visual Studio solicitará la URL de tu sitio de desarrollo, en mi caso, coloco la URL de mi sitio en Office 365 con la opción SharePoint-hosted y proveo mis credenciales de acceso. 

También solicitará la versión del sitio de SharePoint, en mi caso selecciono SharePoint Online.


Al presionar el botón Finish, Visual Studio creará un proyecto con la plantilla adecuada. Para dar un poco de orden a los siguientes puntos, coloqué algunas carpetas anidadas en las que definiré las columnas de sitio y la lista.

Las columnas de sitio

Una columna de sitio es la definición de un tipo de dato que puede ser usado varias veces en un sitio de SharePoint y ahí es donde radica su utilidad. Por otro lado, tienes un control mucho mayor que apreciarás en su momento como, la posibilidad de ocupar una clave como título y otra para descripción y, de esta forma, controlar el idioma del nombre y descripción de la columna que se muestra al usuario. En otro momento hablaremos de este tema.
Lo primero que se necesita para agregar una columna de sitio es, presionar con el botón derecho sobre el folder Columnas y seleccionar las opciones Add > Add New Item. Esto abrirá un cuadro de diálogo en el que la opción Site Column estará disponible, selecciónala y dale un nombre a la columna. Te recomiendo que al definir nombres internos o de referencia nunca ocupes espacios o caracteres especiales ya que estos valores se codifican de otra forma y te harán un poco difícil el desarrollo.
Al realizar este procedimiento se generará un archivo xml en el que podrás definir el resto de las propiedades de las columnas por ejemplo, en la siguiente ilustración coloco la definición de la columna TipoLicencia que es de tipo Choice y Requerida. Para que puedas construir el resto de las columnas (de tipo fecha y persona) te recomiendo la definición de Microsoft que contiene un par de ejemplos adicionales.

La lista

Una vez creadas las columnas, es necesario crear la plantilla de la lista en la que se hará referencia a las columnas recientemente creadas. Para ello de click derecho a la carpeta Listas y seleccione las opciones Add > Add New Item.
En el cuadro de diálogo que aparece, seleccione la opción List y dele un nombre a la lista.
Visual Studio le presentará un Wizard con el que podrá elegir la plantilla de la lista, revise las opciones para que se dé una idea de las posibilidades para futuros proyectos. En nuestro caso dejamos la selección original y presionamos el botón OK.
Al presionar Finish el Wizard creará varios archivos xml con los que podremos definir varias opciones de la lista incluso, los formularios para visualizar, dar de alta y actualizar la información de ítem. 
Archivos XML de configuración de la lista
El archivo que contiene el grueso de la información es el llamado Schema.xml. Éste archivo está compuesto de cuatro secciones principales:

    1. Content Types
    2. Fields
    3. Views
    4. Forms
Los Content Types son plantillas de información bastante útiles y que veremos en otro post debido a su extensión. Por el momento basta con decir que en dicha sección se colocarán referencias a las columnas que creaste en la sección Las columnas de sitio, y que agregas en el editor que aparece justo después de haber finalizado el Wizard de la creación de la lista (La imagen de abajo muestra el editor y las columnas que hemos agregado, dichas columnas son las que creamos en la sección anterior). 
En la sección Fields se encuentra toda la definición de las columnas que creamos anteriormente. Esta redundancia se debe a motivos de seguridad y es que, ¿qué ocurriría si su proyecto llegara a productivo y por algún error, un administrador borrara la definición de una columna de sitio que sea ocupada por la lista?, por ejemplo Suplente. Para evitar que se genere un error, la lista tiene su propia definición de la columna de sitio.
Estructura del archivo Schema.xml
Tanto la sección Views como Forms merecen cada uno su propio post debido a que cada uno tiene configuraciones especiales. En el caso de Views, ésta sección del XML administra la estructura de la vista; desde su nombre, el número de elementos a mostrar, el orden de las columnas, los datos a mostrar, los criterios de filtrado y los archivos con los que se manejará la apariencia de la lista. Para la sección de Forms está destinado el archivo que manejará el formulario para Edición, Inserción y Visualización de datos. En éstos archivos podrás agregar referencias a elementos JavaScript y CSS, lo que te dará un mejor manejo y presentación del formulario.
Agregando columnas de sitio a la lista.














Finalmente agregaremos un vínculo a la lista para que puedas probar su funcionamiento. Para ello, abre el archivo Default.aspx que está dentro de la carpeta Pages. Dentro coloca el código HTML para dirigirte a la lista, por ejemplo yo ocupé el siguiente fragmento de código:
Agregando vínculos a nuestra lista

Probando

Es el momento de probar todo el trabajo, para ello ejecuta el depurador con la tecla F5.
Una vez que termine de implementar la solución verás una página similar a la que se muestra en el lado izquierdo, en ella verás el vínculo que colocamos; al seguir el vínculo te mostrará la lista que definimos con todos los XML.
Te recomiendo que hagas avances pequeños cuando definas tu estructura de datos y los pruebes; es decir, crea las columnas que necesites para tu primer lista y también crea dicha lista, posterior a eso prueba que funcione correctamente. Si todo sale bien, crea las siguientes columnas que necesites, su lista y vuelve a probar. 
¿A qué se debe mi sugerencia?
Lamentablemente existen algunos problemas al momento de implementar las soluciones durante el desarrollo, relacionados con SharePoint. Entre los problemas más comunes están, que no puedas ocupar la lista debido a que una de las columnas no fue creada de forma correcta (ocurre seguido con las columnas de tipo Choice y Lookup). La solución a este problema es eliminar la columna problema y volverla a crear (esto a nivel del Visual Studio), lo cual es un problema si tienes poco tiempo para realizar el desarrollo. En ambientes productivos no he tenido dicho problema pero, si a alguno de ustedes le ha ocurrido, ojalá puedan compartir su experiencia.

Los siguientes pasos

Con esto concluimos nuestro primer post, esperamos que les sea de utilidad en sus proyectos y siéntanse libres de expresar todos sus comentarios. En nuestros siguientes artículos le daremos un poco más de funcionalidad a nuestra lista con:
  • Formularios de listas personalizados
  • Usando JSOM

Enlaces de interés