Archivo de la categoría: SharePoint 2010

SharePoint 2010

Excel Services – Unable to process the request. Wait a few minutes and try performing this operation again

sharepoint2010Si estas intentado abrir un archivo Excel en SharePoint 2010, a través de Excel Services (partimos que el servicio ya esta instalado en la granja) y recibes este mensaje de error: “Unable to process the request. Wait a few minutes and try performing this operation again” puedes realizar varias acciones:

1) Puedes verificar que la conexión de servicio con Excel Service está habilitada, para ello, en la Administración Central te vas a: Application Management -> Manage Web Application -> <Nombre_Web_Application> -> Service connections.

excel_err1

En este formulario, has de asegurarte de el servicio Excel (según lo hayas llamado cuando lo creaste), esta habilitado:

 

excel_err2

 

Con estas verificaciones, debería funcionar correctamente el servicio y abrir tus archivos Excel en el navegador web.

2) Una segunda opción, es configurar la Coleccion de Sitio, para que, en vez de abrir el archivo Excel en el navegador web, te lo abra en el cliente de paquete Office. Para ello, te remito a este post de Mario Cortés donde se detallan todos los pasos a seguir.

 

Editar plantillas de SharePoint (archivos stp) de una forma cómoda y sencilla

sharepoint2010Es posible que te hayas visto en la necesidad de tener que editar plantillas de listas de SharePoint, para pasarlas a una granja diferente o similar… Una cosa que suele ser común es que en tu servidor de pruebas no tengas todos los Language Packs instalados, y cuando quieras usar dichas plantillas de lista, no puedas hacerlo.

Para poder realizar esto, es necesitar editar el manifiesto de la planilla y ponerle el idioma, que tengas instalado en tu entorno. Para editar un archivo stp existen varios métodos que comento a continuación:

1) Existe una forma manual de poder editarlos, cuyos pasos serian:

  1. Copia y renombra la extensión del archivo .stp por .cab
  2. Extrae el contenido del archivo (yo suelo usar WinRar)
  3. Abre el archivo manifest.xml en un editor de texto (Visual Studio, Notepad, Dreamweaver, etc.)
  4. Modifica el archivo manifest.xml
  5. Usando la linea de comando,  sitúate en el directorio/carpeta que contenga el manifiesto ya editado
  6. Teclea el siguiente comando, sustituyendo el <nombre_plantilla> por el deseado: makecab.exe manifest.xml <nombre_plantilla>.cab
  7. Esto generará un archivo .cab que deberemos renombrar de extensión a .stp
  8. Ya tenemos listo la plantilla modificada, preparada para subir a SharePoint

makecab01

2) Existe otra forma mucho mas sencilla, cuyos pasos serian:

  1. Copia y renombra la extensión del archivo .stp por .cab
  2. Extrae el contenido del archivo (yo suelo usar WinRar)
  3. Abre el archivo manifest.xml en un editor de texto (Visual Studio, Notepad, Dreamweaver, etc.)
  4. Modifica el archivo manifest.xml
  5. Buscar la aplicación iexpress.exe y ejecutala
  6. Sigue los pasos del wizard
  7. Esto generará un archivo .cab que deberemos renombrar de extensión a .stp
  8. Ya tenemos listo la plantilla modificada, preparada para subir a SharePoint

IEXPRESS02

Este segundo paso es mucho mas cómodo si tu plantilla, contiene mas de un archivo (es decir a aparte del manifest.xml) ya que no tienes que generar ningún script para que te haga el empaquetado usando makecab.

Para más información sobre IExpress Wizard, puedes consultar esta pagina de Microsoft.

 

DisableLoopbackCheck en SharePoint 2010

Uno de los problemas con el que nos podemos encontrar los desarrolladores de SharePoint 2010, es que cada vez que intentamos acceder a sitios de nuestros entornos de desarrollo o preproducción de SharePoint, que se habíamos creado previamente, nos pide en repetidas ocasiones (las tres de rigor) su usuario y contraseña antes de devolver un error 401.1 Access Denied y una entrada en el Visor de Eventos de Windows.

En principio esto no debe de ser un problema ya que ningún usuario usa directamente los servidores de la granja de SharePoint para navegar por los sitios ahí hosteados. Pero ¿y si es un servidor de desarrollo o pruebas? En estos casos es obligatorio que podamos acceder a los sitios sin que este comportamiento ocurra.

Este comportamiento del sistema, es una característica de seguridad introducida desde el Service Pack 1 de Windows Server 2003 y está presente hasta las versiones actuales de Windows Server 2008 R2. Básicamente lo que hace es bloquear el acceso a una Web Application usando el FQDN, si esto sucede la característica entra en acción y bloquea este acceso enviando un error 401.1 Access Denied. ¿Por qué se introdujo esta característica? Pues porque existían varios ataques que se basaban en reflección para engañar a IE y hacerle creer que se estaba trabajando de manera local y así burlar muchas restricciones de seguridad.

¿Como podemos solucionar este problema (siempre y cuando se traten de entornos no productivos)? Existen dos métodos .. uno a través de la ejecución de un comando de PowerShell y la segunda añadiendo una clave en el Registro de Windows.

1) Ejecución de este comando PowerShell:

New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -value "1" -PropertyType dword

En la siguiente imagen puede apreciarse la ejecución de este comando:

2) Modificando directamente el Registro de Windows:

– Pincha en el botón de Inicio o presiona la tecla Windows + X (dependiendo la versión de Windows), pincha en Ejecutar, escribe: “regedit” y presiona Intro.
– En el editor del Registro de Windows, localiza la siguiente clave: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
– Pincha con el botón derecho del ratón en Lsa, selecciona nueva Valor de DWORD (32 bits)


– Introducimos el nombre “DisableLoopbackCheck” y presionamos Intro.
– Pinchamos con el botón derecho del raton sobre esta nueva clave “DisableLoopbackCheck“, y seleccionamos Modificar.
– Introducimos un “1” en el campo Información del valor  y presionamos Intro.


– Cerramos el editor del Registro de Windows y reiniciamos el servidor para que los cambios tengan efecto.

Para información mas detallada sobre este problema, podéis consultar el KB896861.

 

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.

 

Eliminar características de la base de datos de contenido con PowerShell

Una de mis tareas es revisar el Analizador de mantenimiento de SharePoint 2010, donde quedan registrados problemas que pueden hacer que una granja de SharePoint tenga problemas de seguridad, escalabilidad, rendimiento…

Uno de los problemas que he detectado y que viene siendo recurrente, es soluciones han sido eliminadas, antes de que las características correspondientes hayan sido desactivadas de colecciones de sitios y sitios.

Este problema queda registrado en el  Analizador de mantenimiento, con la categoría a nivel de granja: “Missing server side dependencies“.

Dentro de esta categoría, puedes encontrar registros como el siguiente, donde se detallo el problema:

[MissingFeature] Database [_WSS_DSI01] has reference(s) to a missing feature: Id = [e8389ec7-70fd-4179-a1c4-6fcb4342d7a0]. The feature with Id e8389ec7-70fd-4179-a1c4-6fcb4342d7a0 is referenced in the database [_WSS_DSI01], but is not installed on the current farm. The missing feature may cause upgrade to fail. Please install any solution which contains the feature and restart upgrade if necessary.

Este mensaje de error indica un nombre de base de datos de contenido (_WSS_DSI01) y el identificador de la característica (e8389ec7-70fd-4179-a1c4-6fcb4342d7a0), lo malo es que este error, no informa sobre los sitios o colecciones de sitios donde la característica existe y además, aunque supiéramos donde se ha activado esta característica, no es posible desde el interfaz de usuario desactivarla, ya que dicha solución se ha eliminado de la granja.

El siguiente script de PowerShell, nos informa de que sitios o colecciones de sitio de la base de datos de contenido, contienen la referencia a esta característica y fuerza la desactivación de de esta.

function Remove-SPFeatureFromContentDB($ContentDb, $FeatureId, [switch]$ReportOnly)
{
    $db = Get-SPDatabase | where { $_.Name -eq $ContentDb }
    [bool]$report = $false
    if ($ReportOnly) { $report = $true }
    $db.Sites | ForEach-Object 
    {
        Remove-SPFeature -obj $_ -objName "site collection" -featId $FeatureId -report $report
        $_ | Get-SPWeb -Limit all | ForEach-Object 
        {
            Remove-SPFeature -obj $_ -objName "site" -featId $FeatureId -report $report
        }
    }
}

function Remove-SPFeature($obj, $objName, $featId, [bool]$report)
{
    $feature = $obj.Features[$featId]
    if ($feature -ne $null) 
    {
        if ($report) 
        {
            write-host "Feature found in" $objName ":" $obj.Url -foregroundcolor Red
        }
        else
        {
            try 
            {
                $obj.Features.Remove($feature.DefinitionId, $true)
                write-host "Feature successfully removed from" $objName ":" $obj.Url -foregroundcolor Red
            }
            catch 
            {
                write-host "There has been an error trying to remove the feature:" $_
            }
        }
    }
    else 
    {
        #write-host "Feature ID specified does not exist in" $objName ":" $obj.Url
    }
}

 

Si únicamente queremos obtener el listado de sitios y colecciones de sitios, donde se referencia esta característica, deberás utilizando de la siguiente forma:

Remove-SPFeatureFromContentDB -ContentDB "_WSS_DSI01" -FeatureId "e8389ec7-70fd-4179-a1c4-6fcb4342d7a0" –ReportOnly

 

Si por el contrario, además de obtener el listado, deseas eliminar las referencias de la base de dato de contenido, deberás utilizarlo como anteriormente, el único cambio es que has de eliminar el parámetro “–ReportOnly”:

Remove-SPFeatureFromContentDB -ContentDB "_WSS_DSI01" -FeatureId "e8389ec7-70fd-4179-a1c4-6fcb4342d7a0"

 

Una vez hayas eliminado todas las referencias erróneas, es necesario volver a analizar  la categoría: “Missing server side dependencies” dentro del Analizador de mantenimiento, para comprobar que los errores han desaparecido.

Referencia: get-spscripts.com

 

Consultar los logs de ULS de SharePoint 2010 en SQL Server

Es posible que se os haya dado el caso de tener que consultar los logs de SharePoint 2010 conocidos como Unified Logging System (ULS), en entornos formados por grandes granjas de servidores… Este proceso en este caso se convierte en algo tedioso, si no tienes forma de identificar en que servidor físico estas haciendo la petición, para consultar su Correlation ID.

Antes de nada indicar que Microsoft recomienda no acceder directamente a la información de la plataforma, desde la base de datos, sino que debe hacerse a través del modelo de objetos de SharePoint. Este seria el único caso excepcional, donde podemos acceder directamente a través de consultas SQL.

SharePoint 2010, a nivel de granja crea una base de datos llamada WSS_Logging, donde almacena toda la información de la plataforma SharePoint, como por ejemplo: información que envía al visor de eventos de Windows, información de los contadores de rendimiento, inventario de los sitios existentes, los logs de ULS… eso si con un periodo de antigüedad de 14 días, toda información que pase de ese tiempo sera eliminada.

La información de esta base de datos, también es usada para consolidar los datos de Web Analytics a través de los jobs Microsoft SharePoint Foundation Usage Data Import y Microsoft SharePoint Foundation Usage Data Processing.

También es importante remarcar, que la información se vuelca desde la granja a la tabla WSS_Logging, a través de varios timer job que se ejecuta cada varios minutos (este valor es diferente para cada uno de ellos). Aquí tenéis el listado de jobs que vuelca información a esta base de datos (estos datos pueden consultarse en la Administración Central):

El job en concreto, que actualiza la información en el ULS es: Diagnostic Data Provider: Trace Log y se ejecuta por defecto cada 10 minutos y esta es su configuración:

Si, como comentábamos anteriormente, queremos acceder a la misma información que existe en los logs de SharePoint, directamente, deberemos realizar consultas sobre la vista: [dbo].[ULSTraceLog]. Es importante remarcar que debes utilizar las vistas y no las tablas para realizar las consultas.

Los campos de esta vista son: [PartitionId], [RowId], [LogTime], [MachineName], [ProcessID], [ProcessName], [ThreadID], [Area], [Category], [Level], [EventID], [Message], [CorrelationId], [RowCreatedTime]. Estos campos son similares a la informacion que se almacenan en los archivos de ULS.

Como veis existe el campo  [CorrelationId] por el cual vamos a conseguir filtrar los datos, para cotar la información.

De este modo, podrás localizar registros de logs sin tener que buscar físicamente en varios archivos ULS, a través de la granja…

Crear items con servicios REST de diferentes tipos de contenido

Otra duda que se nos ha planteado es la creación de items en una lista o biblioteca de diferentes tipos de contenido utilizando servicios REST en  SharePoint 2010.

Previamente ya habíamos visto como Listar, añadir, modificar y borrar información con REST en SharePoint 2010, pero partíamos que la lista o biblioteca solo tenia un tipo de contenido por defecto. Ahora se nos plantea un complicación mas, que es crear un ítem de un tipo de contenido concreto.

Suponemos que tenemos la lista “DemoList” a la cual tiene el tipo de contenido por defecto “Elemento” y le hemos añadido un segundo tipo de contenido llamado “Anuncio”.

Ahora queremos añadir un nuevo item de tipo “Anuncio”… lo que deberíamos hacer es definir ese nuevo item y especificar su tipo de contenido:

static void CreateDataContentType()
{
	//recupera la informacion del servicio 
	DevPortalHomeDataContext dataContext = new DevPortalHomeDataContext(new Uri("http://servidor/_vti_bin/listdata.svc/"));
	dataContext.Credentials = CredentialCache.DefaultNetworkCredentials; 
	//crea y define un nuevo item en la lista DemoList (definimos el Content Type)
	DemoListItem demoListItem = new DemoListItem()
	{
		ContentType = "Announcement",
		Title = "Registro de tipo contenido Anuncio",
		Cuerpo = "Cuerpo del anuncio"
	};
	//añade el item a la lista
	dataContext.AddToDemoList(demoListItem);
	dataContext.SaveChanges();
	Console.WriteLine("Registro creado correctamente");
}

En el caso de no especificar el campo ContentType, SharePoint asume que el nuevo item, es del tipo de contenido predeterminado.

En la siguiente imagen se puede apreciar, como el nuevo registro añadido con esta función, es de tipo “Anuncio”:

 Es importante saber que has de conocer los posibles tipos de contenido de la lista en cuestión, ya que no hay forma de saber (ni siquiera con los metadatos: http://servidor/_vti_bin/listdata.svc/$metadata) de los posibles valores del campo: <Property Name=”TipoDeContenido” Type=”Edm.String” Nullable=”true” />.

 

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.