Un punto diferenciador de SharePoint, con respecto a otras plataformas de colaboración, es la capacidad de ofrecer una capa de abstracción en cliente, permitiendo el desarrollo de aplicaciones o integraciones externas. Desde el primer día, los servicios Web de SharePoint han permitido interactuar con la plataforma de una forma sencilla y universal, permitiendo, sin tener que desarrollar ninguna API de servidor, acceder a cualquier tipo de contenido en SharePoint.
Con SharePoint 2010, se incluyeron dos opciones nuevas, el Cliend-Side Objet Model (CSOM) y la interfaz REST, que complementaban a los servicios Web de la plataforma, ofreciendo más posibilidades de desarrollo. Mientras que en el último SharePoint, la versión 2013, se han añadido nuevas funcionalidades al CSOM, así como una nueva y más limpia interfaz REST.
Dentro de las múltiples versiones del modelo de objetos de cliente de SharePoint 2013, nos encontramos con un nuevo SDK para el desarrollo de aplicaciones móviles, el Mobile Object Model.
Una nueva oportunidad para mejorar la movilidad de las empresas que usan SharePoint, ya que pueden extender sus procesos empresariales de forma universal a todos los usuarios, estén donde estén y tanto con un ordenador como con un móvil. Pensemos en aplicaciones que permitan aprobar flujos de trabajo en SharePoint, consultar datos de clientes en listas o almacenar fotos geo-localizadas en una biblioteca de documentos en SharePoint.
En este artículo, haremos una introducción a la versión de Windows Phone, dejando para el siguiente la versión REST, que nos permitiría hacer un cliente más universal para Windows 8, iPhone o Android.
El CSOM de Windows Phone usa el servicio WCF, client.svc, que es el encargado de analizar y ejecutar las consultas del cliente, usando el modelo de objetos de servidor, y devolver el resultado en JSON al cliente para que pueda trabajar con los objetos que necesite. Siguiendo el siguiente diagrama de arquitectura.
Si ya hemos trabajado con la implementación Silverlight del CSOM, trabajar con esta versión para Windows Phone es prácticamente lo mismo, básicamente hay que tener presente las llamadas asíncronas y que los objetos no se cargan hasta que ejecutemos la consulta en el servidor.
Autenticación
Hasta ahora, el principal problema que teníamos con Windows Phone y la integración con SharePoint es el de la autenticación del usuario frente a SharePoint. Para esto, tenemos una clase, Authenticator, que se encarga de realizar la autenticación por nosotros, aunque podemos realizarla manualmente.
var context = new ClientContext("https://xxx.sharepoint.com");
var authenticator = new Authenticator();
authenticator.CookieCachingEnabled = true;
context.Credentials = authenticator;
Consultas a lista
No tenemos cambios significativos cuando realizamos consultas a listas, básicamente obtenemos la lista, con o sin el CAML query, trabajando siempre desde el contexto de cliente y en el ExecuteQueryAsync obtenemos los elementos de esa lista o consulta.
1 var lista = new ObservableCollection<Announcement>();2 var query = GetAnnouncementQuery();3 var items = Context.Web.Lists.GetByTitle("Announcements").GetItems(query);4 Context.Load(items);5 Context.Load(items, listItems => listItems.Include(item => item.FieldValuesAsText));6 Context.ExecuteQueryAsync(7 delegate(object sender, ClientRequestSucceededEventArgs args)8 {9 foreach (var item in items)10 {11 var anuncio = new Announcement();12 anuncio.ID = item.Id.ToString();13 anuncio.Title = item.FieldValuesAsText["Title"];14 anuncio.Body = item.FieldValuesAsText["Body"];15 anuncio.Expires = item.FieldValuesAsText["Expires"];16 anuncio.Created = item.FieldValuesAsText["Created"];17 lista.Add(anuncio);18 }19 },20 delegate(object sender, ClientRequestFailedEventArgs args)21 {22 //Manejamos el error de la consulta2324252627 var query = GetAnnouncementQuery();28293031 var items = Context.Web.Lists.GetByTitle("Announcements").GetItems(query);32333435 Context.Load(items);36373839 Context.Load(items, listItems => listItems.Include(item => item.FieldValuesAsText));4041424344454647 Context.ExecuteQueryAsync(48495051 delegate(object sender, ClientRequestSucceededEventArgs args)52535455 {56575859 foreach (var item in items)60616263 {64656667 var anuncio = new Announcement();68697071 anuncio.ID = item.Id.ToString();72737475 anuncio.Title = item.FieldValuesAsText["Title"];76777879 anuncio.Body = item.FieldValuesAsText["Body"];80818283 anuncio.Expires = item.FieldValuesAsText["Expires"];84858687 anuncio.Created = item.FieldValuesAsText["Created"];8889909192939495 lista.Add(anuncio);96979899 }100101102103104105106107 },108109110111 delegate(object sender, ClientRequestFailedEventArgs args)112113114115 {116117118119 //Manejamos el error de la consulta120121122123 });124
Crear elementos en lista
Como siempre, nos creamos un objeto del tipo ListItemCreationInformation y lo envíanos al contexto.
1 var subscriptionList = Context.Web.Lists.GetByTitle("HubSubscribers");2 Context.Load(subscriptionList);3 deviceItem = subscriptionList.AddItem(new ListItemCreationInformation());4 deviceItem["Title"] = displayName;5 deviceItem["UserAccount"] = acccountName;6 deviceItem["ChannelUri"] = pushChannel;7 deviceItem["ChannelUriDate"] = System.DateTime.Now;8 deviceItem["DeviceId"] = deviceId;9 deviceItem.Update();10111213 Context.Load(subscriptionList);14151617 deviceItem = subscriptionList.AddItem(new ListItemCreationInformation());18192021 deviceItem["Title"] = displayName;22232425 deviceItem["UserAccount"] = acccountName;26272829 deviceItem["ChannelUri"] = pushChannel;30313233 deviceItem["ChannelUriDate"] = System.DateTime.Now;34353637 deviceItem["DeviceId"] = deviceId;38394041 deviceItem.Update();42434445 Context.ExecuteQuery();46
Obtener un perfil de usuario
En el CSOM de SharePoint 2013 tenemos acceso a los perfiles de usuario, y en Windows Phone también. Para esto, nos creamos un PeopleManager y obtenemos las propiedades que necesitemos.
1 var peopleManager = new PeopleManager(Context);2 var personProperties = peopleManager.GetPropertiesFor(userAccount);3 context.Load(personProperties, p => p.AccountName, p => p.DisplayName, p => p.Email, p => p.UserProfileProperties,4 p => p.DirectReports, p => p.Peers, p => p.PictureUrl);5 context.ExecuteQueryAsync(6 delegate(object sender1, ClientRequestSucceededEventArgs args)7 {8 var profile = new Profile();9 profile.AccountName = personProperties.AccountName;10 profile.DirectReports = personProperties.DirectReports.ToList();11 profile.Peers = personProperties.Peers.ToList();12 profile.DisplayName = personProperties.DisplayName;13 profile.PictureUrl = personProperties.PictureUrl;14 profile.Email = personProperties.Email;15 profile.Manager = personProperties.UserProfileProperties["Manager"];16 profile.Status = personProperties.UserProfileProperties["SPS-StatusNotes"];17 profile.WorkPhone = personProperties.UserProfileProperties["WorkPhone"];18 profile.Department = personProperties.UserProfileProperties["Department"];19 loadProfileCompletedCallback(new LoadProfileCompleteEventArgs { Profile = profile });20 },21 delegate(object sender1, ClientRequestFailedEventArgs args)22 {23 //Manejamos el error de la consulta24252627 var personProperties = peopleManager.GetPropertiesFor(userAccount);2829303132333435 context.Load(personProperties, p => p.AccountName, p => p.DisplayName, p => p.Email, p => p.UserProfileProperties,36373839 p => p.DirectReports, p => p.Peers, p => p.PictureUrl);40414243 context.ExecuteQueryAsync(44454647 delegate(object sender1, ClientRequestSucceededEventArgs args)48495051 {52535455 var profile = new Profile();56575859 profile.AccountName = personProperties.AccountName;60616263 profile.DirectReports = personProperties.DirectReports.ToList();64656667 profile.Peers = personProperties.Peers.ToList();68697071 profile.DisplayName = personProperties.DisplayName;72737475 profile.PictureUrl = personProperties.PictureUrl;76777879 profile.Email = personProperties.Email;80818283 profile.Manager = personProperties.UserProfileProperties["Manager"];84858687 profile.Status = personProperties.UserProfileProperties["SPS-StatusNotes"];88899091 profile.WorkPhone = personProperties.UserProfileProperties["WorkPhone"];92939495 profile.Department = personProperties.UserProfileProperties["Department"];96979899100101102103 loadProfileCompletedCallback(new LoadProfileCompleteEventArgs { Profile = profile });104105106107 },108109110111 delegate(object sender1, ClientRequestFailedEventArgs args)112113114115 {116117118119 //Manejamos el error de la consulta120121122123 });124
Conclusiones
¿A qué esperamos para desarrollar aplicaciones de negocio para Windows Phone? Personalmente, creo que nos puede abrir un nuevo abanico de posibilidades y de aplicaciones empresariales que no han terminado de llegar con SharePoint 2010. Por cierto, aunque no lo he probado al 100%, este modelo de objetos de cliente también funciona con SharePoint 2010, salvo las funcionalidades nuevas, como los perfiles, que no tenían soporte de cliente en SharePoint 2010.
Alberto Diaz Martin
MVP SharePoint
adiazcan@hotmail.com
@adiazcan
http://geeks.ms/blogs/adiazmartin