Archivo de la etiqueta: C#

Como lanzar un Workflow programaticamente en SharePoint

Tras un periodo de inactividad, vuelvo con un nuevo post… Esta vez se trata, de la necesidad de lanzar un nuevo Workflow a nivel de listas o biblioteca en todos los ítems, previamente creados. Si el Workflow se configura para que se lance a la creación o modificación del ítem, puede que haya muchos ítems, que por antigüedad, no se modifiquen y no se lance el Workflow asociado.

Para realizar esto desde el API de SharePoint, la forma mas sencilla es la siguiente:

static private void LanzarWorkflow()
{
  //definición de variables necesarias para la ejecución
  string SP_URL = "http://mi_sharepoint";
  string LIST_NAME = "Pages";
  //este es el GUID del BaseID del Workflow que queremos ejecutar
  Guid wfBaseId = new Guid("b81abdd8-faf4-4bed-86ca-a49d9cc9913e");
  //se ejecuta con privilegios elevados
  SPSecurity.RunWithElevatedPrivileges(delegate
  {
	try
	{
	  using (SPSite site = new SPSite(SP_URL))
	  {
		using (SPWeb web = site.OpenWeb())
		{
		  SPList list = web.Lists[LIST_NAME];
		  SPListItemCollection items = list.Items;
		  for (int i = 0; i < items.Count; i++)
		  {
			web.AllowUnsafeUpdates = true;  
			//selecciona el item actual
			SPListItem item = items[i];
			Console.WriteLine(string.Format("[Item] Lanzando workflow para el item Id: {0} ", item.ID));
			//obtiene el objeto plantilla de WF asociado a la lista que queremos
			SPWorkflowAssociation associationTemplate = list.WorkflowAssociations.GetAssociationByBaseID(wfBaseId);
			//lanzar el WF a nivel del item
			web.Site.WorkflowManager.StartWorkflow(item, associationTemplate, associationTemplate.AssociationData);
			web.AllowUnsafeUpdates = false; //bloquea cambios por codigo
		  }
		}
	  }
	}
	catch (Exception ex) { Console.WriteLine(string.Format("***ERROR*** {0}", ex.ToString())); }
  });
}

Los únicos parámetros que necesita esta función son:

  • SP_URL: Url del sitio de SharePoint donde se encuentra la lista que tiene asociada el Workflow que deseamos lanzar
  • LIST_NAME: Nombre de la lista o biblioteca que tiene el Workflow
  • wfBaseId: por ultimo el Base ID del Workflow (este GUID lo puedes ver, por ejemplo, desde SharePoint Designer, editando el archivo XML de configuración del propio Workflow)

 

Como obtener el estado del Workflow de un Item con C#

Una de las cosas que me ha surgido, ha sido la necesidad de obtener mediante programación, el estado de los items de una lista o biblioteca en SharePoint 2010.

Es un proceso muy sencillo, ya que hay que obtener la el valor del campo que se genera en la lista, cuando se le asocia un workflow, únicamente lo que debemos hacer posteriormente, es convertir el numero por un texto legible. En caso de ser necesario puedes hacer directamente la traducción en castellano de los estados, si eliminas la linea System.Enum.GetName(typeof(SPWorkflowStatus), WorkflowStatusValue) y la sustituyes por un switch por ejemplo…

Aquí os dejo una función que podrá serviros de ayuda, los unicos parametros que hay que pasar son:

siteUrl = Url del sitio
listName = Nombre de la lista o libreria a la cual esta asociado el workflow
itemId = Identificador, del item que queremos consultar
wfFieldName = Nombre del campo de la lista donde se muestra el estado del workflow

 

static string GetWorkflowStatud(string siteUrl, string listName, int itemId, string wfFieldName)
{
  string result = string.Empty;
  SPSecurity.RunWithElevatedPrivileges(delegate
  {
	try
	{
	  using (SPSite spSite = new SPSite(siteUrl))
	  {
		using (SPWeb spWeb = spSite.OpenWeb())
		{
		  //instancia la lista/biblioteca
		  SPList spList = spWeb.Lists[listName]; 
		  //recupera el item de la lista
		  SPListItem spListItem = spList.Items.GetItemById(itemId); 
		  //recupera el valor del campo del Workflow
		  Int32 WorkflowStatusValue = Convert.ToInt32(spListItem[wfFieldName]); 
		  //traduce el numero a texto
		  string WorkflowStatusString = System.Enum.GetName(typeof(SPWorkflowStatus), WorkflowStatusValue); 
		  //almacena el estado del Workflow
		  result = WorkflowStatusString;
		}
	  }
	}
	catch (Exception ex) { result = ex.Message; }
  });
  return result;
}

Para más información, puedes consultar el enumerador SPWorkflowStatus con los posibles estados del workflow.

 

Como subir documentos con el servicio REST en SharePoint 2010

Otra de las funcionalidades clave, con respecto a uso de servicios REST en SharePoint 2010, y siguiendo con el post: Listar, añadir, modificar y borrar información con REST en SharePoint 2010, es la posibilidad de subir un documento, a una biblioteca de documentos.

Esta funcionalidad, debe realizarse desde un cliente (en este caso voy a hacerlo desde una aplicación de consola). Parto de la anterior aplicación que montamos en mi anterior post,  lo único que vamos a realizar es añadirle un método más, parar poder subir documentos.

Este seria el método:

static void UploadDocument()
{
	using (FileStream file = File.Open(@"C:\TEMP\Documento.txt", FileMode.Open))
	{
		//recupera la informacion del servicio 
		DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/"));
		dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
		//declara las caracteristicas del el documento
		string path = "/Documents/Documento.txt";
		string Name = "Documento";
		string contentType = "plain/text";
		string Title = "Documento";
		//crea el documento
		DocumentsItem documentItem = new DocumentsItem()
		{
			ContentType = contentType,
			Name = Name,
			Path = path,
			Title = Title
		};
		//sube el documento al la biblioteca
		dataContext.AddToDocuments(documentItem);
		dataContext.SetSaveStream(documentItem, file, false, contentType, path);
		dataContext.SaveChanges();
		Console.WriteLine("Documento subido correctamente");
	} 
}

Para su correcto funcionamiento, hay que crear un archivo de texto plano en C:\TEMP\ llamado Documento.txt.

Se puede añadir para un correcto funcionamiento de la aplicación, control de excepciones…   ya que si existe algún problema, se produzca una excepción de tipo System.Data.Services.Client.DataServiceRequestException.

Nota: El método DataServiceContext.SetSaveStream únicamente está accesible desde el Framework 4.0.

Listar, añadir, modificar y borrar información con REST en SharePoint 2010

Una de las ventajas que tiene SharePoint, es el uso de servicios con protocolos estándares para poder consumir información desde cualquier plataforma… Este es el caso de REpresentational State Transfer (REST),  que técnicamente no es un protocolo, pero puede definirse como un modelo de diseño de Servicio Web basado en XML sobre HTTP.

¿Como podemos usar servicios REST en SharePoint 2010? Si tenemos un sitio de SharePoint, podemos acceder a su contenido, a través de  ADO.NET Data Services, lo único que debemos hacer es poner en el navegador la siguiente url: http://<servidor>/_vti_bin/listdata.svc/ para ver el listado de todas las entidades con las que podemos interactuar.

En caso de que tengáis problemas para recuperar esta información, podéis consultar este post: No se puede cargar IDataServiceUpdateProvider usando REST en SharePoint 2010.

Es posible navegar entre las entidades, incluyendo en la url el nombre de la entidad, como por ejemplo, del listado de objetos que tenemos en este sitio:

Podríamos visualizar los items de la lista DemoList accediendo a la url:

  • http://<servidor>/_vti_bin/listdata.svc/DemoList

Es necesario conocer los metadatos de la entidad, para realizar consultas mas complejas…

  • http://<servidor>/_vti_bin/listdata.svc/$metadata

Si queremos recuperar la información del primer item de esta lista, podemos obtenerlo de la siguiente forma: 

  • http://<servidor> /_vti_bin/listdata.svc/DemoList(1)

Si queremos únicamente devolver el titulo del item, solo tienes que especificar el nombre del campo:

  • http://<servidor>/_vti_bin/listdata.svc/DemoList(1)/Título

También podemos hacer filtros, como por ejemplo, con el titulo del item:

  • http://<servidor>/_vti_bin/listdata.svc/DemoList?$filter=Título eq ‘Otro nuevo registro’ 
Incluso podemos ordenar la información:
  • http://<servidor>/_vti_bin/listdata.svc/DemoList?$orderby=Título

 

Si preferimos consumir este servicio desde una aplicación .NET es tan sencillo como:

1) Desde Visual Studio, creas un nuevo proyecto de tipo “Console Application”

2) Añades una referencia al servicio REST, es decir con el botón derecho sobre el proyecto pulsas “Add Service Reference…” y añades la url del servicio:

3) Dependiendo de la acción que queramos realizar, podemos usar cualquiera de los siguientes métodos, que gracias a LYNQ podemos trabajar con la información:

Listado de items de una lista:

static void GetData()
{
	//recupera la informacion del servicio 
	DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/"));
	dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
	//recupera el listado de items de la lista DemoList
	var result = from items in dataContext.DemoList select new { Title = items.Title, };
	foreach (var item in result)
	Console.WriteLine(item);
}

Creación de un nuevo item en una lista:

static void CreateData()
{
	//recupera la informacion del servicio 
	DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/"));
        dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
	//crea un nuevo item en la lista DemoList
	DemoListItem demoListItem = new DemoListItem();
	demoListItem.Title = "Registro REST";
	dataContext.AddToDemoList(demoListItem);
	dataContext.SaveChanges();
	Console.WriteLine("Registro creado correctamente");
}

Actualización de un item existente en una lista:

static void UpdateData()
{
	//recupera la informacion del servicio 
	DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/")); 
        dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
	//modifica un item existente en la lista DemoList
	DemoListItem demoListItem = dataContext.DemoList.Where(i => i.Title == "Registro REST").FirstOrDefault();
	demoListItem.Title = "Registro REST - modificado";
	dataContext.UpdateObject(demoListItem);
	dataContext.SaveChanges();
	Console.WriteLine("Registro modificado correctamente");
}

Eliminacion de un item de una lista:

static void DeleteData()
{
	//recupera la informacion del servicio 
	DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/")); 
        dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
	//borra el item existente en la lista DemoList
	DemoListItem demoListItem = dataContext.DemoList.Where(i => i.Title == "Registro REST - modificado").FirstOrDefault();
	dataContext.DeleteObject(demoListItem);
	dataContext.SaveChanges();
	Console.WriteLine("Registro borrado correctamente");
}

 

Aquí tenéis el resultado de la ejecución de estos métodos:

Para mas información sobre el servicio REST en SharePoint 2010 podéis consultar la pagina de MSDN: Interfaz de REST de SharePoint Foundation.

 

Modificar la propiedad NoCrawl de un campo

Se me plantea la necesidad de modificar un atributo NoCrawl, de un campo de una lista creada en base a una plantilla de lista… es algo muy sencillo, aunque parezca un trabalenguas 🙂

El atributo NoCrawl a nivel de SPField, indica si dicho campo puede ser reastreado por el buscador (a través de un valor buleano: TrueFalse), aqui tienes toda la informacion con respecto a esta propiedad.

En este caso, lo que se quiere es que el buscador reastee contenido almacenado en un campo, que tiene el atributo NoCrawl = True. Cambiar esto a través del modelo de objetos es una labor muy sencilla, aqui teneis un ejemplo de como podriamos modificarlo.

using (SPWeb oWebsite = SPContext.Current.Site.AllWebs["MiSitio"])
{
    SPList oList = oWebsite.GetList("Lists/Announcements/AllItems.aspx");
    SPFieldCollection collFields = oList.Fields;
    SPField oField = collFields.GetField("MiColumna");
    oField.NoCrawl = true;
    oField.Update();
    oList.Update();
}

Hay que tener en cuenta, que si quieres modificar esta propiedad en varias listas de la misma coleccion de sitios, deberas primero recorer y almacenar cual van a ser las listas que has de modificar, y posteriormente modificarlas o de lo contrario SharePoint te dará un error, ya que se esta haciendo una llamada al metodo Update de la lista…

Escribir en ULS con SharePoint 2010

SharePoint 2007/20010 dispone de un sistema de logs/trazar llamado Unified Logging System (ULS). Si quieres mas información sobre ULS puedes consultar este artículo de Microsoft: Introducción al registro del sistema de creación de registros unificado (ULS).

Cuando implementamos una aplicación sobre esta plataforma, es muy conveniente, registrar cualquier excepción al ULS para un futuro análisis, que nos permita evaluar y corregir el problema.

En SharePoint 2010 se implementa la clase SPDiagnosticsServiceBase, con la que podemos acceder de una forma mas cómoda que en versiones anteriores a este tipo de información, ya que cuenta con los métodos WriteEvent y WriteTrace, para ello.

El siguiente codigo muestra un ejemplo de como escribir en ULS volcando la información de una excepción capturada:

try
{
    var i = 0;
    var a = 2 / i;
}
catch (Exception ex)
{
    SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Mi Categoria", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
}

En el caso que queramos volcar información al ULS sin necesidad de estar dentro de un “catch” puedes usar esto:

SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Mi Categoria", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, "Mi descripcion del error", string.Empty);

Por ultimo, existe una herramienta de Microsoft, para visualizar estos archivos de logs, llamada ULS Viewer, que puedes descargar de forma gratuita desde aquí.

 

Error en el control TaxonomyPicker.ascx

Revisando el visor de eventos de los entornos de producción, donde está instalado SharePoint 2010, me he encontrado un error muy curioso, relacionado con el control TaxonomyPicker.ascx. La descripción del error es:

Error al cargar el archivo de plantilla de control /_controltemplates/TaxonomyPicker.ascx: Could not load type ‘Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker’ from assembly ‘Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’.

Revisando el codigo del control TaxonomyPicker.ascx me he dado cuenta que en vez de aparecer una “,” aparece “&#44;”.

<%@ Control className=”TaxonomyPickerControl” Language=”C#” Inherits=”Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker&#44;Microsoft.SharePoint.Portal,Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>

Para solucionar este problema existen dos opciones:

  • La primera es cambiar “&#44;” por “,” en todos los servidores de la granja.
  • La otra opción es, como el control TemplatePicker.ascx no se utiliza desde la versión de SharePoint 2010 RTM, se puede renombrear el archivo con una extensión que no sea “.ascx”, por ejemplo “TemplatePicker.ascx.error” y dejará de compilarse.

Existe un articulo de soporte donde Microsoft, trata este error: KB2481844

Como crear un subsitio en SharePoint 2010

Como hacer las cosas desde la interface web de SharePoin, no tiene mucho misterio y además puedes encontrarlo facilmente buscando en Internet, prefiero detallar como se harian las cosas programaticamente, ya que esto es lo que nos va a permitir personalizar SharePoint a nuestro gusto/necesidad.

Este es el caso de la creación de un subsitio y mas contratamente un sitio de tipo blog en idioma Español. A continuación os dejo una funcion, que crea un nuevo sitio utilizando la plantilla de blog: SPWebTemplate.WebTemplateBLOG (aunque se puede usar cualquier otra que necesitemos, para ello puedes consultar el listado de plantillas de sitio de SharePoint 2010). He de destacar que el idioma de creacion de estas plantillas enla funcion es el Spanish – Spain (Modern Sort), pero se puede usar cualquier otro, solo tienes que cambiar el LCID:

private void CreateBlogSite(SPWeb web, string siteUrl, string siteTitle, string siteDescription)
{
  string siteTemplate = SPWebTemplate.WebTemplateBLOG; //epecifica la plantilla de sitio (Blog)
  uint siteLocationID = 3082; //idioma Spanish - Spain (Modern Sort)
  SPWebCollection subsite = web.Webs;
  subsite.Add(siteUrl, siteTitle, siteDescription, siteLocationID, siteTemplate, true, false);
}

Para más informacion puedes consultar el metodo SPWebCollection.Add.

Como establecer el administrador primario y secundario de una colección de sitios

Esta es la forma de establecer a través de código el administrador primario y secundario de una coleccion de sitios en SharePoint 2010. Para ello vamos a usar las propiedades SPSite.Owner y SPSite.SecondaryContact. Esta es un funcion de ejemplo que puede mejorarse verificando si los usuarios vienen vacios, o si el objeto SPSite es nulo… pero espero que os sirva a modo plantilla sobre la que partir. He decidido pasarle como parámetro el objeto SPSite, ya que asi puede ser llamada, por ejemplo, desde un WebPart de administración como desde la activación de una característica:

private bool SetAdministrators(SPSite site, string admPrimary, string admSecondary)
{
  bool result = false;
  try
  {
      SPUser PrimaryUser = site.RootWeb.EnsureUser(admPrimary);
      SPUser SecondaryUser = site.RootWeb.EnsureUser(admSecondary);
      site.Owner = PrimaryUser; //asigna el administrador primario
      site.SecondaryContact = SecondaryUser; //asigna el administrador secundario
      result = true;
  }
  catch (Exception) { }
  return result;
}