viernes, 18 de enero de 2013

Pruebas unitarias con SharePoint Emulator Package

No si ustedes hayan notado el mes pasado (con todas las fiestas que había) que el equipo de Visual Studio anuncio el paquete de SharePoint Emulators de Visual Studio 2012 para SharePoint 2010.

De entrada este paquete solo aplica para SharePoint 2010 y utilizándolo en Visual Studio 2012. Es comprensible que este paquete solo aplique para SharePoint 2010, dado el poco tiempo que lleva SharePoint 2013 liberado, además de que la finalidad del emulador es poder probar código de servidor, mismo que Microsoft desalienta escribir para SharePoint 2013.

Este paquete se puede instalar a través de NuGet. Solo tienen que buscarlo y aparece dentro de los resultados.

  
En el pasado había sido bastante difícil aislar los componentes SharePoint de una solución para realizar pruebas unitarias, debido a que estos componentes dependen enormemente de SharePoint, es decir, para poder realizar la ejecución de pruebas de esos componentes era necesario tener instalado SharePoint.
 
SharePoint Emulators Package nos permite crear pruebas unitarias y ejecutarlas sin tener instalado un ambiente de SharePoint, este paquete se basa en el concepto de Shims de Microsoft Fakes para crear un contexto (un conjunto de Shims) en memoria de los objetos más comunes dentro de la API de SharePoint. Con lo que, para crear una prueba unitaria que utiliza objetos de la API de SharePoint lo único que debe hacer es encerrar el código que estamos probando dentro de un SharePoint Emulator Scope.

[TestMethod]
public void CreateList()
{
	using (SharePointEmulationScope scope = new SharePointEmulationScope(Microsoft.QualityTools.Testing.Emulators.EmulationMode.Enabled))
	{
		using (SPSite site = new SPSite("http://UrldePrueba/"))
		{
			using (SPWeb web = site.OpenWeb())
			{
				Assert.IsNotNull(web);

				//Definiendo la lista
				Guid listId = site.RootWeb.Lists.Add(listName, listName, SPListTemplateType.GenericList);
				SPList list = site.RootWeb.Lists[listId];
				Assert.IsNotNull(list);
				Assert.AreEqual(list.Title, listName, "Bad list name!");

				//agregando los campos a la lista
				list.Fields.Add("Code", SPFieldType.Number, true);
				list.Fields.Add("Name", SPFieldType.Text, true);
				list.Fields.Add("Description", SPFieldType.Text, true);

				//agregando dos nuevos elemento a la lista
				SPListItem item1 = list.Items.Add();
				item1["Code"] = 1;
				item1["Name"] = "West";
				item1["Description"] = "West Region";
				item1.Update();

				SPListItem item2 = list.Items.Add();
				item2["Code"] = 1;
				item2["Name"] = "East";
				item2["Description"] = "East Region";
				item2.Update();

				Assert.AreEqual(list.ItemCount, 2, "Bad number of items");
			}
		}
	}
}


Como mencionaba antes, este paquete solo contiene los Shims para los objetos de uso más común dentro de la API de SharePoint, por lo que habrá casos es los que al hacer uso de ciertos métodos o propiedades, que devuelven un tipo de objeto que no tiene su correspondiente Shim dentro del emulador,  obtendremos excepciones de tipo NotSupportedException, como por ejemplo al intentar utilizar el método GetItems de un objeto SPList, que devuelve un objeto de tipo SPListItemCollection.


El siguiente código muestra el método que se está probando, en la línea 17 está la llamada al método GetItems que lanzará la excepción.
public string GetNameByCode(string listName, SPWeb web, int code)
{
	SPList list = web.Lists[listName];

	var listQuery = new SPQuery
	{
		Query = String.Format(
			"" +
			"" +
			"" +
			"{0}" +
			"" +
			"", code)
	};

	string name = string.Empty;
	foreach (SPListItem item in list.GetItems(listQuery))
	{
		name = item["Name"].ToString();
	}

	return name;
}


Lo genial de esto, es que es posible inyectar Shims al Emulador para los tipos no soportados, como por ejemplo para SPListItemCollection, de la forma tradicional, es decir usando Shims, como se aprecia de las líneas 41 a la 50
[TestMethod]
public void SearchItemById()
{
	int searchById = 2;

	using (SharePointEmulationScope scope = new SharePointEmulationScope(Microsoft.QualityTools.Testing.Emulators.EmulationMode.Enabled))
	{
		using (SPSite site = new SPSite("http://UrldePrueba/"))
		{
			using (SPWeb web = site.OpenWeb())
			{
				Assert.IsNotNull(web);

				//Definiendo la lista
				Guid listId = site.RootWeb.Lists.Add(listName, listName, SPListTemplateType.GenericList);
				SPList list = site.RootWeb.Lists[listId];
				Assert.IsNotNull(list);
				Assert.AreEqual(list.Title, listName, "Bad list name!");

				//agregando los campos a la lista
				list.Fields.Add("Code", SPFieldType.Number, true);
				list.Fields.Add("Name", SPFieldType.Text, true);
				list.Fields.Add("Description", SPFieldType.Text, true);

				//agregando dos nuevos elemento a la lista
				SPListItem item1 = list.Items.Add();
				item1["Code"] = 1;
				item1["Name"] = "West";
				item1["Description"] = "West Region";
				item1.Update();

				SPListItem item2 = list.Items.Add();
				item2["Code"] = 2;
				item2["Name"] = "East";
				item2["Description"] = "East Region";
				item2.Update();

				Assert.AreEqual(list.ItemCount, 2, "Bad number of items");

				//Agregamos el Shimp para el objeto SPListItemCollection
				if (scope.EmulationMode == EmulationMode.Enabled)
				{
					var fakeList = new ShimSPList(list);
					fakeList.GetItemsSPQuery = (query) =>
					{
						var shim = new ShimSPListItemCollection();
						shim.Bind(new[] { list.Items[0], list.Items[1] });
						return shim.Instance;
					};
				}

				RegionGateway gateway = new RegionGateway();

				//Ahora al realizar la llamada al metodo GetNameByCode, no se lanzara la excepcion
				string result = gateway.GetNameByCode(listName, site.RootWeb, searchById);
				Assert.AreNotEqual(string.Empty, result, true, "Search Failed!");


				Region searchItem = gateway.GetRegionById(listName, web, 1);
				Assert.IsNotNull(searchItem);
				Assert.AreEqual("West", searchItem.Name, true);
			}
		}
	}
}

Sin duda alguna un importante paquete que debiéramos usar desde ya para las pruebas unitarias de nuestro código. Ahora lo importante sería preguntar para cuando este paquete estará disponible para SharePoint 2013.

Por último les comparto el proyecto de Visual Studio para que vean el código y puedan analizarlo mejor.


Referencias

Happy Code!

miércoles, 9 de enero de 2013

JqGrid y la API REST de SharePoint 2013

El motivo de este post es mostrar un ejemplo de como combinar el uso del jqGrid obteniendo datos de una lista de SharePoint a través de la API REST.

Antes que nada, supongo que muchos de ustedes conocen el jqGrid, si no es así, se están perdiendo de un plugin de jquery bastante interesante, muy fácil de usar y con bastantes características como:

  • Formatear celdas
  • Reordenar columnas
  • Menús contextuales para filas
  • Barras de menús personalizadas
  • Carga de datos desde diferentes fuentes como XML, JSON, etc
  • Multiseleccion
  • Resizing
  • Edición en línea
  • Agrupación
  • Búsqueda
  • Paginación
  • Y un largo etcétera.

Lo anterior combinado con la practicidad de la nueva API REST de SharePoint 2013 permite a los desarrolladores crear aplicaciones realmente sorprendentes.

Escenario:
Tengo un sitio de SharePoint con una lista llamada Sales con las columnas y datos que se aprecian en la imagen

Requerimiento:
Es necesario mostrar en una pagina, una vista de la lista utilizando alguna de las características arriba mencionadas, como ordenación, menús contextuales, multiseleccion, etc.

Solución:
Como siempre y para cualquier problema, no hay una solución única, pero yo me inclinaría por usar jqGrid, porque la solución es muy sencilla, fácil de codificar y ni siquiera requiere crear un wsp o una App (cosa que hare en otro post) que se instale y despliegue en la granja.

Solo hare uso de la webpart Script Editor para agregar un poco de HTML y JavaScript a una pagina cualquiera de SharePoint.


A continuación el código completo utilizado:


 



 

 




La explicación:
Primero agregamos algunas referencias a los archivos js y css, tanto del grid como de jquery

 



 

 

Luego agregamos el código para configurar el jqGrid, es decir las columnas, el ordenamiento, etc, etc. y el código para realizar la llamada REST y manejar la respuesta para hacer el Binding de datos JSON con el grid
$(onPageLoad);

//funcion que se ejecuta en el evento onLoad de la pagina
function onPageLoad()
{
 //adjuntamos a el evento onclick del boton 'getSales' la funcion getSales()
 $("#getSales").click(getSales);
   $(document).ready(function () {
   //configuramos el jqGrid
    $("#SalesTable").jqGrid({
    datatype: 'local',
    autowidth: true,
    height: 230,
    colNames: ['ID', 'Order Date', 'Region', 'Representative', 'Item', 'Units', 'Unit Cost', 'Total'],
    colModel: [
     { name: 'ID', index:'ID', width: 80 , sorttype:"int"},
     { name: 'Title', width: 80 , sorttype:"date"},
     { name: 'Region', width: 80 },
     { name: 'Rep', width: 80 },
     { name: 'Item', width: 80 },
     { name: 'Units', width: 80 },
     { name: 'Unit_x0020_Cost', width: 80 },
     { name: 'Total', width: 80 , sorttype:"float"},
    ],
    caption: 'Sales via OData'
  });
   });
}

//funcion que se ejecuta cuando hacemos click en el boton 'Obtener Ventas'
function getSales(){
 //Url REST de la consulta que estamos haciendo, recuperamos los elementos de la lista 'Sales'
 var requestUri = "http://sp2013app/_api/web/lists/getByTitle('Sales')/items/";
 //Se establece el header ACCEPT de manera adecuada
 var requestHeaders={
      "ACCEPT":"application/json;odata=verbose",
 }
 //se realiza la llamada ajax mediante jquery
 $.ajax({
 url: requestUri,    //Url de la consulta a realizar
 type:"GET",      //tipo de la llamada
 contentType:"application/json", //el tipo de contenido que esperamos recuperar, JSON
 headers: requestHeaders,  //headers correctos
 error:onError,      //en caso de error se ejecuta la funcion onError()
 success: onGetSalesSuccess  //en caso de exito, se ejecuta la funcion onGetSalesSuccess()
 }); 
}

//funcion que se ejecuta en caso de que la llamada REST sea exitosa
function onGetSalesSuccess(data){
 if(!data)return;
  //por cada uno de los resultados obtenidos se agrega una fila al jqGrid
  $.each(data.d.results,function(index, value){
   $("#SalesTable").jqGrid('addRowData', value.ID, value);
  });
 $("#SalesTable").jqGrid('SalesTable','#pager2',{edit:false,add:false,del:false});
}

function onError(xhr, status){
 alert(xhr.response.message);
}

Y por ultimo tenemos el código HTML que sirve como contenedor del grid, me refiero al input, la tabla y el div. Solo basta descomentar el código HTML al ser usado


El resultado:


Happy Coding!

viernes, 4 de enero de 2013

HTML Field Security en SharePoint 2013

Una de las nuevas características en el área de Web Content Management en SharePoint 2013 es que ahora existe una opción llamada HTML Security Settings que se encuentra en la sección de configuración  de administradores de la colección de sitios, por lo que evidentemente se debe contar con este rol para utilizarla.
 
En SharePoint 2013 los creadores de contenido de un sitio puede agregar iframes en cualquier página, ya sea utilizando la opción Embed Code del menú Insert del Ribbon, o utilizando la nueva webpart Script Editor.

 
Hasta aquí ninguna novedad, en el pasado también se podían agregar iframes a una página de SharePoint, obviamente no utilizando las opciones arriba descritas ya que estas son nuevas en SharePoint 2013, pero si se podía hacer. Lo que la opción HTML Security viene a cubrir era la necesidad de los administradores de SharePoint (esos tipos a los que cualquier toque de creatividad les molesta) de poder restringir los dominios a los cuales se puede agregar o referenciar dentro de un iframe en un sitio de SharePoint.
 
Este setting permite habilitar a los usuarios con rol de "Contributors" el poder agregar iframes a partir de un listado de dominios permitidos, poder referenciar cualquier dominio o no poder insertar iframes con contenido externo a SharePoint en absoluto. La opción por default es permitir a los contribuidores agregar iframes con un listado de sitios también predefinido, como se aprecia en la imagen. 
 
Para este ejemplo voy a agregar un iframe a una página de SharePoint referenciando uno de mis sitios favoritos, Channel 9. Aunque como se puede apreciar en la imagen, obtendré un mensaje indicándome que no cuento con los permisos suficientes para agregar el iframe.
 
Lo que se debe hacer es ir a apartado de configuración de HTML Security Settings y agregar el dominio channel9.msdn.com (para este ejemplo), y acto seguido intentar de nuevo agregar el iframe.
 
Y con esto podemos ya agregar satisfactoriamente una referencia a contenido externo, de manera un poco más ordenada.
 

 
Un último comentario es que este setting solo aplica/restringe a los usuarios con el rol de "Contributors", es decir que si eres el dueño del sitio o un site collection administrator, ni te preocupes por este tipo de limitantes que solo aplican a los simples mortales. Lo que debería preocuparte es revisar tu modelo de gobernabilidad en SharePoint.
 
Happy Configuring! 

Etiquetas

SharePoint 2010 (38) Microsoft (32) Desarrollo SharePoint (31) Gerardo Reyes Ortiz (27) SharePoint (20) SharePoint 2013 (18) Errores SharePoint (12) México (10) PowerShell (9) Silverlight (8) Visio Services (7) Features (6) MVP (6) Silverlight 3 (6) WebCast (6) Workflows (6) Configuracion SharePoint 2010 (5) D.F. (5) API REST (4) Configuracion SharePoint 2010; (4) Troubleshooting (4) Visual Studio 2010 (4) Visual studio (4) WSS (4) Web parts (4) Apps (3) Comunidad SharePoint (3) Configuración SharePoint 2013 (3) ODATA (3) SharePoint Server (3) SharePoint; Instalación SharePoint; Troubleshooting; Search Service (3) Silverlight 3.0 (3) Silverlight Toolkit (3) WebParts (3) javascript (3) jquery (3) Eventos SharePoint (2) Office 2010 (2) PeoplePicker (2) REST (2) SQL Server (2) Scripting (2) Search Service Application (2) SharePoint Designer (2) UPA (2) UPS (2) Workflows SharePoint (2) host header (2) Apps Development (1) Big Bang (1) CAS (1) CSOM (1) Codeplex (1) CompartiMOSS (1) Configuracion SharePoint 2010; Errores SharePoint (1) Configuracion SharePoint 2010; SharePoint 2010 (1) Custom Actions (1) Custom Editor Parts (1) Delegate Controls (1) Deployment (1) DisableLoopbackCheck (1) Document Library (1) Entrevista (1) Examenes de Certificación (1) Extract WSP (1) FBA (1) FS4SP (1) Fakes (1) Fast Search Server 2010 For SharePoint (1) Fiddler (1) HTTP.SYS (1) HTTPS (1) JSON (1) Language Pack's (1) Latam (1) MAXDOP (1) MCSM (1) MSExpertos (1) MVC (1) Microsoft México (1) Microsoft; Codeplex; Screencast; (1) My Sites (1) SQL Server 2012 (1) SQL Server Reporting Services (1) Screencast (1) Screencast; (1) Service Applications (1) Service Pack (1) SharePoint 2007 (1) SharePoint 2010 SP 1 (1) SharePoint API (1) SharePoint Conference (1) SharePoint Emulators (1) SharePoint Farm (1) SharePoint Health Analyzer (1) SharePoint Magazine (1) SharePoint Online (1) SharePoint Search (1) SharePoint Test (1) SharePoint; Desarrollo SharePoint (1) Shims (1) Simposio (1) Simposio Latinoamericano (1) SkyDrive Pro (1) Soporte Microsoft (1) Templates (1) Tip (1) VSeWSS (1) Virtual Machine (1) Visual Studio 2012 (1) WCF (1) WSS; IIS 7 (1) Web API (1) Web Content Management (1) Web Services (1) Windows 8 (1) Windows Live ID (1) Xml (1) appcmd (1) iOS (1) jqGrid (1) onload function (1)