“An error occurred when updating a property” mapeando atributos de AD en User Profile Service de SharePoint 2010

20.12.11 / Comments (0) / by Enric Carrión

Creando propiedades de usuario en el User Profile Service que están mapeadas a atributos del Directorio Activo, podemos encontrarnos con que, al definir un mapping y guardar la propiedad nos de error, sin más explicación: “An error occurred when updating a property.

image

La operación ha producido eventos de los servicios de Forefront Identity Manager (FIM) en el Visor de sucesos, pero son demasiado crípticos como para dar pistas claras. Los reproduzco en su totalidad para quién busque similitudes (siento la enorme longitud).

Log Name:      Application
Source:        FIMSynchronizationService
Date:          19/12/2011 17:50:53
Event ID:      6306
Task Category: (3)
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      SV-PUIGINTRA.PUIGINTRA.local
Description:
The server encountered an unexpected error while performing an operation for the client.
 
"BAIL: MMS(3140): mastate.cpp(11174): 0x8023060f (The version identifier passed in is not the latest)
BAIL: MMS(3140): server.cpp(4068): 0x8023060f (The version identifier passed in is not the latest)
Forefront Identity Manager 4.0.2450.34"
Event Xml:
<Event xmlns="
http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="FIMSynchronizationService" />
    <EventID Qualifiers="49152">6306</EventID>
    <Level>2</Level>
    <Task>3</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2011-12-19T16:50:53.000000000Z" />
    <EventRecordID>5937</EventRecordID>
    <Channel>Application</Channel>
    <Computer>SV-PUIGINTRA.PUIGINTRA.local</Computer>
    <Security />
  </System>
  <EventData>
    <Data>BAIL: MMS(3140): mastate.cpp(11174): 0x8023060f (The version identifier passed in is not the latest)
BAIL: MMS(3140): server.cpp(4068): 0x8023060f (The version identifier passed in is not the latest)
Forefront Identity Manager 4.0.2450.34</Data>
  </EventData>
</Event>

Log Name:      Application
Source:        Forefront Identity Manager
Date:          19/12/2011 17:47:21
Event ID:      3
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      XXXXXXXX
Description:
Microsoft.ResourceManagement: Microsoft.ResourceManagement.ResourceManagementException: Exception from HRESULT: 0x8023060F ---> System.Runtime.InteropServices.COMException (0x8023060F): Exception from HRESULT: 0x8023060F
   at MIISRCW.IMMSServer.SetMVData(String pszMVDataXML)
   at Microsoft.ResourceManagement.SyncConfig.ModifyMVData(String mvDataXml)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.Update(Guid objectId, CultureInfo locale, IList`1 updateParameters, Guid cause)
   --- End of inner exception stack trace ---
   at Microsoft.ResourceManagement.Utilities.ExceptionManager.ThrowException(Exception exception)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.Update(Guid objectId, CultureInfo locale, IList`1 updateParameters, Guid cause)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.ProcessInputRequest(RequestType request)
   at Microsoft.ResourceManagement.ActionProcessor.ActionDispatcher.ProcessInputRequest(RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.ExecuteAction(RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.ExecuteAction[ResponseBodyType](RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest[ResponseBodyType](RequestType request, Guid requestIdentifier, Object redispatchSingleInstanceKey)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest[ResponseBodyType](RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest(RequestType request)
   at Microsoft.ResourceManagement.WebServices.ResourceManagementService.Put(Message request)
Event Xml:
<Event xmlns="
http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Forefront Identity Manager" />
    <EventID Qualifiers="0">3</EventID>
    <Level>2</Level>
    <Task>0</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2011-12-19T16:47:21.000000000Z" />
    <EventRecordID>5933</EventRecordID>
    <Channel>Application</Channel>
    <Computer>XXXXXXXX</Computer>
    <Security />
  </System>
  <EventData>
    <Data>Microsoft.ResourceManagement: Microsoft.ResourceManagement.ResourceManagementException: Exception from HRESULT: 0x8023060F ---&gt; System.Runtime.InteropServices.COMException (0x8023060F): Exception from HRESULT: 0x8023060F
   at MIISRCW.IMMSServer.SetMVData(String pszMVDataXML)
   at Microsoft.ResourceManagement.SyncConfig.ModifyMVData(String mvDataXml)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.Update(Guid objectId, CultureInfo locale, IList`1 updateParameters, Guid cause)
   --- End of inner exception stack trace ---
   at Microsoft.ResourceManagement.Utilities.ExceptionManager.ThrowException(Exception exception)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.Update(Guid objectId, CultureInfo locale, IList`1 updateParameters, Guid cause)
   at Microsoft.ResourceManagement.ActionProcessor.SyncConfigActionProcessor.ProcessInputRequest(RequestType request)
   at Microsoft.ResourceManagement.ActionProcessor.ActionDispatcher.ProcessInputRequest(RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.ExecuteAction(RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.ExecuteAction[ResponseBodyType](RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest[ResponseBodyType](RequestType request, Guid requestIdentifier, Object redispatchSingleInstanceKey)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest[ResponseBodyType](RequestType request)
   at Microsoft.ResourceManagement.WebServices.RequestDispatcher.DispatchRequest(RequestType request)
   at Microsoft.ResourceManagement.WebServices.ResourceManagementService.Put(Message request)</Data>
  </EventData>
</Event>

Me dirijo a la referencia más clara respecto a mapeos de propiedades de AD con propiedades de usuario, que es:

http://blogs.msdn.com/b/tehnoonr/archive/2010/11/22/mapping-user-profile-properties-in-sharepoint-2010-to-ldap-attributes.aspx

Ahí, ejecuto el script PowerShell de mapeo entre propiedad de AD/FIM y SharePoint que proporciona Tehnoon Raza. En el método AddNewMapping, se produce una excepción con mensaje “Unable to process Put message”, que provoca los mismos eventos en el Visor de sucesos.

El autor nos dice que, para este caso, comprobemos que la propiedad de AD está expuesta en FIM usando el Forefront Service Manager (miisclient.exe). Nota: para ejecutarlo deberéis hacerlo con runas y con las credenciales que tengáis configuradas en los servicios “Forefront Identity Manager Service” y “Forefront Identity Manager Synchronization Service” (típicamente la cuenta de granja de SharePoint). Ejecuto el Manager pero: chasco. La propiedad está ahí, disponible, vivita y coleando. Camino cerrado.

Y de pronto… destello de lucidez. ¿Permisos? La cuenta de servicio de los servicios de FIM es mi cuenta de granja de SharePoint, y recuerdo que para provisionar el User Profile Service, dicha cuenta debe ser “ascendida” al grupo de administradores locales del servidor. Remember: http://sharejoint.blogspot.com/2011/10/user-profile-synchronization-service-de.html

Actúo resolutiva y firmemente: añado mi cuenta de granja de SharePoint a administradores locales del servidor y reinicio el “SharePoint 2010 Timer” (OWSTIMER). ¿Suficiente? Aún no. Reinicio el User Profile Synchronization Service en Services on server. ¿Suficiente? ¡Sí! ¡Ya puedo añadir mapeos!

Resumen de la solución:

  • Establecer cuenta de los servicios “Forefront Identity Manager Service” y “Forefront Identity Manager Synchronization Service” como administrador local
  • Reiniciar servicio “SharePoint 2010 Timer”
  • Reiniciar “User Profile Synchronization Service” desde Services on server

 

Una vez añadidos los mapeos, tened en cuenta que habría que revertir el cambio y volver a quitar esa cuenta de admnistradores locales.

Espero que toda esta odisea os sirva como guía de troubleshooting para fenómenos similares en vuestros entornos.

Instalar SharePoint 2010 de forma desatendida con AutoSPInstaller

27.10.11 / Comments (0) / by Enric Carrión

Brian Lalancette es el señor al que hay que hacer un monumento por haber materializado un sueño que muchos anhelábamos pero creíamos utópico: la instalación y configuración de una granja SharePoint mediante scripts.

El proyecto se llama AutoSPInstaller y para aquellos escépticos que duden si vale la pena invertir tiempo en ello os diré: habéis estado perdiendo el tiempo viviendo sin ello. Más vale tarde que nunca.

http://autospinstaller.codeplex.com/

http://blogs.technet.com/b/heyscriptingguy/archive/2010/03/23/hey-scripting-guy-march-23-2010.aspx

De forma resumida, todo el proceso de instalación de prerequisitos, binarios de SharePoint, wizard de SharePoint Technologies, wizard de configuración de granja, servicios, my site, aplicaciones web iniciales, indexación de PDF con iFilter y otros tweaks de configuración (disable loopback adapter, comprimir las carpetas de logs) queda automatizado en un PowerShell que podemos invocar y al que proporcionamos un fichero de configuración XML que contiene todos los parámetros necesarios: URLs, conexión a BD, nombres de BD, cuentas de servicio, servicios a provisionar… Este es el aspecto de SQL Server una vez configurado todo.

Esto sí que es tenerlo todo bien ordenadito, ¿eh? Y la guinda es que, al terminar todo el proceso, abrirá un navegador con tres pestañas tres, para que os maravilléis de cómo os deja funcionando el Central Administration, el My Site y el portal de inicio.

Este script requiere por nuestra parte de la confección de una carpeta con el slipstream de todos los instaladores y actualizaciones a ejecutar (incluyendo Service Packs, Cumulative Updates y Language Packs). También requiere temporalmente desactivar la UAC de Windows y deshabilitar las alertas sobre descarga de ficheros .EXE. Y por supuesto, de la creación de los usuarios de dominio para las cuentas de servicio con sus permisos iniciales

Estos requisitos quedan mucho mejor explicados en este post de Tobias Lekman. Seguidlo y comprobaréis qué facil es instalar una granja desde cero.

http://blog.lekman.com/2010/11/automated-sharepoint-2010-installations.html

Tips:

1) Recordad a ejecutar el script desde sesión de Windows iniciada con el usuario de instalación (típicamente spsetup)

2) Contad con espacio libre suficiente en disco antes de empezar la instalación (mínimo 2-3GB).

3) Usad una PassPhrase de granja compleja que contenga al menos tres de estos cuatro tipos de carácteres: mayúscula, minúscula, número, carácter no alfabético. Si no es así, el proceso fallará.

User Profile Synchronization Service de SharePoint 2010 no arranca

20.10.11 / Comments (0) / by Enric Carrión

Para evitar teneros media hora dando vueltas por los posts de social.technet buscando las dos soluciones más típicas a este problema, intentaré resumirlas en este post.

Contexto: vamos a crear conexiones de sincronización de perfiles (User Profile Service > Configure Synchronization Connections) y el central admin nos dice, con buen criterio, que el User Profile Syncronization Service (Servicio de sincronización de perfiles de usuario) está parado.

Untitled

Vamos a arrancarlo desde Services on server pero, después de un rato en Starting, se vuelve a quedar parado. Entonces consultamos Event Viewer y… imagen del drama.

image

The Execute method of job definition Microsoft.Office.Server.Administration.ProfileSynchronizationSetupJob (ID 745c5ecf-f4b2-4497-9526-48e18e0118c0) threw an exception. More information is included below.

An update conflict has occurred, and you must re-try this action. The object UserProfileApplication Name=User Profile Service Application was updated by XXX\xxxxx, in the OWSTIMER (2572) process, on machine ESDCMOS201.  View the tracing log for more information about the conflict.

Bien, lo más probable es que la cuenta de servicio de granja (Farm Account) no esté incluida en el grupo local de Administradores del servidor. La solución pasa por incluirla y, acto seguido, reiniciar el SharePoint Timer Service (desde Services.msc o, por ejemplo, con el comando net stop sptimerv4 && net start sptimerv4 desde una consola de comandos ejecutada como administrador). Podría, en algunos casos, necesitarse del reboot completo del servidor.

Una vez superado esto, puede que el servicio siga un rato largo a Starting y luego siga parándose. En el momento en que finalmente para, si vamos al log de SharePoint podemos ver un evento del tipo:

10/20/2011 09:58:00.70     OWSTIMER.EXE (0x0B38)                       0x1164    SharePoint Portal Server          User Profiles                     g145    Unexpected    Maximum number of retries done for starting FIM SpService. Please look at ULS logs and event viewer to look for the root cause of the failure.    b34705e0-4941-485a-8faf-6c372858364c

El FIM no es otra cosa que el Forefront Identity Manager Service. Éste y el Forefront Identity Manager Synchronization Service son arrancados automáticamente por el User Profile Synchronization Service y él mismo les establece como cuenta de servicio la cuenta de sincronización de perfiles que hemos especificado al arrancar el servicio.

image

Con lo cual, si hacemos la inútil intentona de arrancarlos manualmente, bofetada:

image

¿Qué debemos garantizar entonces? Que la cuenta que estemos especificando tenga el password correcto. ¿Lo tiene? Estupendo. ¿Seguro al 100%? Mejor comprobarlo. En mi caso no era correcto y he perdido mucho tiempo comprobando otras cosas.

Si ninguno de estos dos trucos os funcionan, empezad a plantearos otras alternativas más allá de este post, como por ejemplo la que brinda mi colegui David Martos:

http://david-martos.blogspot.com/2011/05/el-servicio-de-sincronizacion-de.html

Recordad, esto es solo para conseguir (por lo menos) arrancar el servicio; lo que viene después ya es otra historia. Y como siempre, referiros a la literatura oficial que debería seguirse al pie de la letra:

Habilitar edición de documentos Office desde SharePoint 2010 en Windows Server 2008

18.10.11 / Comments (0) / by Enric Carrión

Es muy sencillo y lo hemos hecho todos ya, pero lo procedimento para que quede para la posteridad:

 

Habilitar el servicio WebClient: esto se consigue activando la Feature “Desktop Experience” de Windows Server.

  • Server Manager > Features > Add Feature > Desktop Experience > Next > Install
  • Pedirá reiniciar la máquina después de instalar la Feature.
  • Si queréis pantallazos, aquí se los han currado mucho en la primera parte de http://www.tech-recipes.com/rx/2576/windows_server_2008_install_desktop_experience_vista_theme/
  • Nota: qué conseguimos habilitando WebClient? Pues básicamente estamos habilitando la interfaz cliente WebDav para poder acceder a las colecciones de sitios del servidor de SharePoint. Una vez habilitado, podréis acceder a la raíz de un sitio a través de la ruta de red \\servidor:puerto\davwwwroot

 

Deshabilitar la opción “Enable Protected View for files located in potentially unsafe locations” de los programas cliente de Office (habrá que hacerlo para cada aplicación: Word, Excel…).

  • File > Options > Trust Center > Protected View > Desmarcar la opción “Enable Protected View for files located in potentially unsafe locations”
  • Pantallazos deluxe en: http://maspoint.wordpress.com/2011/03/30/office-integration-problem-with-sharepoint-2010/
  • Nota: qué conseguimos deshabilitando esto? Que Office pueda abrir correctamente ficheros desde la ubicación del servidor sin mostrar los molestos mensajes:
  • Word experienced an error trying to open the file
  • Word has encountered a problem: There is a problem saving the file […]

image

image

Habilitar/deshabilitar los ficheros Javascript de debug de SharePoint

5.10.11 / Comments (0) / by Enric Carrión

SharePoint viene con versiones de debug de los ficheros estándar .JS. : core.debug.js, init.debug.js… Estas versiones contienen el mismo código que los originales pero con las líneas formateadas y las funciones y variables con nomenclatura inteligible. Los JS originales tienen el código reducido al máximo para pesar el tamaño mínimo.

Alguna vez te preguntarás por qué las páginas de tu portal están cargando las versiones debug en lugar de las versiones reducidas. Por ejemplo, puedes constatar este hecho con las Internet Explorer Development Tools:

image

La explicación es muy simple y proviene de la configuración del atributo debug del nodo <compilation> dentro de <system.web> del web.config de tu portal.

<compilation batch="false" debug="false">   –> Ficheros JS originales

<compilation batch="false" debug="true">   –> Ficheros JS debug

Otro motivo muy importante para no olvidarse de poner debug=”false” en los entornos de producción.

Detectar inicio de sesión de usuario en SharePoint

23.9.11 / Comments (2) / by Enric Carrión

Dado que el uso del evento Session_Start en las aplicaciones web de SharePoint es un mecanismo de más que dudoso éxito:

http://stackoverflow.com/questions/2948471/handling-session-start-and-session-end-events-global-asax-sharepoint

http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/79f69293-a91c-4426-acac-ff23030db1ee/

propongo un método para detectar cuando un usuario ha empezado sesión en una aplicación web SharePoint. Aprovechando que disponemos de master pages, podemos incrustar en ellas un control de servidor que se ejecutará en todas las páginas y añadirle la siguiente lógica:

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (HttpContext.Current.Request.Cookies["Mi_Cookie_Sesion"] == null)
{
// Realizar nuestras acciones de inicio de sesión
// ...

HttpContext.Current.Response.Cookies.Add(new HttpCookie("Mi_Cookie_Sesion"));
}
}





Resumiendo, crear una cookie de sesión ASP.NET (objeto HttpCookie sin tiempo de expiración) y comprobar su existencia al iniciar. Las cookies de sesión persisten mientras el usuario está autenticado y caducan cuando hace logout o cuando cierra el navegador.

Activar el visor de XPS en Windows Server 2008

23.9.11 / Comments (2) / by Enric Carrión

Es muy fácil, pero puede volverse uno loco intentando abrir ficheros XPS en Word y que éste diga:

The file ***.xps cannot be opened because there are problems with the contents.

o intentar abrirlos en Internet Explorer y que éste, en lugar de abrirlos, muestre el diálogo de descarga de fichero.

Simplemente hay que activar la feature de servidor XPS Viewer.

image

A partir de entonces, el programa estará disponible en %systemroot%\system32\xpsrchvw.exe y podremos seleccionarlo como programa predeterminado para la extensión .XPS

image

Herramientas para consultar el Directorio Activo

21.9.11 / Comments (0) / by Enric Carrión

Vamos a arrejuntar algunos recursos e ideas interesantes a tener en cuenta cuando queremos examinar un Directorio Activo que no gestionamos. Si no tenemos acceso al controlador de dominio (ni ganas) pero necesitamos navegar por las OU’s, los usuarios, los equipos o el esquema, disponemos de lo siguiente:

  • Las herramientas de Active Directory Services incluídas en Windows 2008. Se instalan como Features en la consola de servidor e incluyen

image

      • el Snap-in de MMC que todos conocemos de Active Directory Users and Computers (evidentemente restringido a los permisos del usuario con que estemos navegando). Probablemente la forma más directa de consultar usuarios, equipos y sus propiedades básicas.

image

      • el Active Directory Administrative Center, una herramienta GUI algo más avanzada que el Snap-in y, chico, un descubrimiento. Puedo ver el listado completo de atributos (incluídos los extendidos), filtrar, hacer búsquedas y guardar consultas.
  • image
  • image
  • Si no disponemos de permisos de instalación para añadir Features en el servidor, quizá nos interese una herramienta de terceros como Active Directory Explorer de Sysinternals, muy completa y con opciones avanzadas de edición, como por ejemplo, los snapshots del AD y la comparación entre ellos.
  • Por otra parte, disponemos de otra herramienta en forma de librería de CmdLets de Powershell: el ActiveRoles Management Shell de Quest Software (los de PowerGUI), que proporcia bastantes más opciones de las que ofrece el Active Directory Module de MS. Este blog es una buena referencia de posibles operaciones a realizar sobre el AD: http://dmitrysotnikov.wordpress.com/category/ad-cmdlets/  Por ejemplo, listar todos los atributos existentes se resolvería con el siguiente (y simple) comando:
      • Get-QADUser -ReturnPropertyNamesOnly -IncludeAllProperties
  • Finalmente, cabe comentar que, si todo esto nos queda pequeño, para implementar nuestras consultas personalizadas y aplicar la lógica añadida que deseemos tenemos el namespace System.DirectoryServices de .NET para atacar las interfaces de servicio de AD.

“Attempted to use an object that has ceased to exist” al eliminar web parts

19.9.11 / Comments (0) / by Enric Carrión

Otra batallita más para cuando sea abuelo. En una página hay dos instancias de mi web part personalizado (es decir, con código mío). Todo va bien hasta que, una vez configuradas las propiedades del primero, me pongo a configurar las del segundo. Guardo la página y –puf- las propiedades no se han guardado. Qué raro. Intento el eliminar el web part y aparece esta alarmante excepción:

Attempted to use an object that has ceased to exist. (Exception from HRESULT: 0x80030102 (STG_E_REVERTED))

Afortunadamente, googleando (yo también lo uso, Chan), llegamos a la conclusión que mi código está usando tácticas de guerrilla para cargarse el contexto de SharePoint:

SPSite site;

try
{

// site = new SPSite
site = SPContext.Current.Site; // miedo y pavor

// ... decenas de líneas de código más tarde

}
finally
{
if (site != null) site.Dispose(); // ouch!
}






Evidentemente si liberas un SPSite obtenido del contexto, el siguiente web part que intente usarlo va a acabar con la cara negra de pólvora. Así pues, prescindo del Dispose y mi página ya funciona con total normalidad. Circulen, no hay nada que ver.

URLs directas de edición de página y web parts

7.9.11 / Comments (0) / by Enric Carrión

Grabado a fuego quede este post para poder consultarlo siempre que olvidemos cuales son las URL para ir directamente a la edición de páginas de publicación:

http://sitio/Pages/pagina.aspx?ControlMode=Edit&DisplayMode=Design

así como a la página de mantenimiento de página de elemento web (Webpart Maintenance Page):

http://sitio/Pages/pagina.aspx?Contents=1

o bien

http://sitio/_layouts/spcontnt.aspx?&url=%2fsitio%2fPages%2fpagina.aspx

FeatureReceiver para configurar la Global Navigation: reordenar y ocultar nodos

7.7.11 / Comments (0) / by Enric Carrión

Si alguien acude a este post buscando milagros, los encontrará (por una vez). Pero no es a mi a quien hay que besar los pies, sino al gran, enorme, incommensurable Waldek Mastykarz, que ha conseguido dar con la fórmula mágica y definitiva para hacer eso que todos hemos deseado alguna vez y nunca hemos logrado. Y no, no me estoy refiriendo a eso, mentes enfermas.

Creo mi jerarquía de sitios de publicación y deseo configurar la navegación, es decir, el caso más usual es querer reordenar y ocultar los nodos correspondientes a los sitios y las páginas que cuelgan de un determinado sitio. Ningún problema en hacerlo manualmente pero muchos problemas si intentamos hacerlo mediante la API.

new PublishingWeb(web).Navigation.GlobalNavigationNodes.Count = 0

No hay nodos. ¡Pero si yo los veo en la página de configuración de navegación! Sí, pero no estarán realmente ahí hasta que alguien haya modificado manualmente la configuración y esa colección se rellene. Esto implica un paso manual adicional en nuestros procedimientos de despliegue automatizados y no podemos permitirnos esa mancha en nuestro expediente. Hasta ahora teníamos la opción de utilizar la opción de "Ordenar automáticamente por fecha de creación", con lo cual creábamos subsitios siguiendo nuestro orden arbitrario y quedaba solucionado. Sin embargo, no había solución posible para ordenar subsitios y páginas conjuntamente. Primero aparecen los subsitios y luego las páginas. Fin del primer acto.

Waldek ha implementado una función PowerShell que es la absoluta caña y que permite especificar qué nodos queremos configurar y en qué orden. Los nodos no especificados se ocultarán. De este modo, una vez tengo mi jerarquía creada, invoco esta función y me configurará la navegación a mi gusto.

http://blog.mastykarz.nl/programmatically-configuring-menu-items-sharepoint-2010/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+WaldekMastykarz+%28Waldek+Mastykarz%29

Funciona a las mil maravillas. ¿Por qué? Porque primero hace una precarga de los nodos de navegación en la colección GlobalNavigationNodes y después, hace las reordenaciones y configuraciones especificadas.
Solo me quedaba una pequeña espinita en mi sueño, que era disponer de esa misma funcionalidad en un feature receiver para poder activarlo a nivel de sitio (SPWeb). Evidentemente, esta activación deberá lanzarse después de haber creado la jerarquía entera de sitios (si la lanzamos desde un onet.xml no tendrá efecto), pero por fin podremos definir features que personalicen completamente la navegación de un sitio.
Helo aqui para todos vosotros:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Diagnostics;
using System.Reflection;
using System.Web;
using System.Web.Configuration;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.Navigation;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Navigation;

namespace SetNavigationFeatureReceiver
{
public class SetNavigationFeatureReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
base.FeatureActivated(properties);

SetNavigation(properties);
}

private void SetNavigation(SPFeatureReceiverProperties properties)
{
var web = properties.Feature.Parent as SPWeb;

// TODO: Retrieve menu items from feature properties
var menuItems = new string[] { "/sites/test/site3", "/sites/test/site2", "/sites/test/site1" };

// Fake context
var request = new HttpRequest("", web.Url, "");
var sw = new System.IO.StringWriter();
var hr = new HttpResponse(sw);
HttpContext.Current = new HttpContext(request, hr);
SPControl.SetContextWeb(HttpContext.Current, web);

// Initalize what has to be initialized
var pweb = PublishingWeb.GetPublishingWeb(web);
var dictionary = new Dictionary();
var collection = pweb.Navigation.GlobalNavigationNodes;

// Get current nodes
var globalNavSettings = new ProviderSettings("GlobalNavSiteMapProvider",
@"Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, 
Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
globalNavSettings.Parameters["NavigationType"] = "Global";
globalNavSettings.Parameters["EncodeOutput"] = "true";
PortalSiteMapProvider globalNavSiteMapProvider =
ProvidersHelper.InstantiateProvider(globalNavSettings, typeof(PortalSiteMapProvider))
as PortalSiteMapProvider;
PortalSiteMapNode currentNode = globalNavSiteMapProvider.CurrentNode as PortalSiteMapNode;
var children = currentNode.GetNavigationChildren(NodeTypes.Default, NodeTypes.Default,
OrderingMethod.Manual, AutomaticSortingMethod.Title, true, -1);

// Reorder nodes
Array.Reverse(menuItems);
var menuNodes = new System.Collections.ObjectModel.Collection();
foreach (PortalSiteMapNode node in children)
{
menuNodes.Add(node);
}

foreach (var menuItem in menuItems)
{
PortalSiteMapNode node = null;
foreach (var p in menuNodes)
{
if (p.InternalUrl == menuItem)
{
node = p;
break;
}
}

if (node != null)
{
menuNodes.Remove(node);
menuNodes.Insert(0, node);
}
}

foreach (var node in menuNodes)
{
var quickId = GetQuickId(node);
if (quickId != null)
{
string typeId = null;
if ((node.Type == NodeTypes.Area || node.Type == NodeTypes.Page))
{
if (node.PortalProvider.NavigationType == PortalNavigationType.Current)
{
typeId = PortalNavigationType.Current.ToString() + "_" + node.Type.ToString();
}
else
{
typeId = PortalNavigationType.Global.ToString() + "_" + node.Type.ToString();
}
}
else
{
typeId = node.Type.ToString();
}

var id = quickId.Split(',');
var objId = new Guid(id[0]);
var nodeId = System.Int32.Parse(id[1]);

var navigationNode = GetNavigationNode(objId, nodeId, node.InternalTitle, node.InternalUrl,
node.Description, node.Type, node.Target, node.Audience, collection, dictionary);
var containsNode = false;
foreach (var mi in menuItems)
{
if (mi == node.InternalUrl)
{
containsNode = true;
break;
}
}

if (containsNode)
{
pweb.Navigation.IncludeInNavigation(true, objId);
}
else
{
pweb.Navigation.ExcludeFromNavigation(true, objId);
}
}
}

pweb.Web.Update();

HttpContext.Current = null;
}

private string GetQuickId(PortalSiteMapNode node)
{
string quickId = null;

var portalSiteMapNodeType = node.GetType();
var QuickId = portalSiteMapNodeType.GetProperty("QuickId", BindingFlags.Instance | BindingFlags.NonPublic);
quickId = (string)QuickId.GetValue(node, null);

return quickId;
}

private Microsoft.SharePoint.Navigation.SPNavigationNode GetNavigationNode(
Guid objId,
System.Int32 nodeId,
string name,
string url,
string description,
NodeTypes nodeType,
string target,
string audience,
SPNavigationNodeCollection collection,
Dictionary oldDictionary)
{
Microsoft.SharePoint.Navigation.SPNavigationNode node = null;
if ((objId != Guid.Empty) && (nodeId >= 0))
{
if (oldDictionary.TryGetValue(nodeId, out node))
{
oldDictionary.Remove(nodeId);
node = SPNavigationSiteMapNode.UpdateSPNavigationNode(node.Navigation.GetNodeById(node.Id), null,
name, url, description, target, audience, false);
node.MoveToLast(collection);
}

return node;
}

node = SPNavigationSiteMapNode.CreateSPNavigationNode(name, url, nodeType, collection);
return SPNavigationSiteMapNode.UpdateSPNavigationNode(node, null, name, node.Url, description, target,
audience, false);
}
}
}
Recordemos, este receiver solo reordena/oculta/muestra, no añade nuevos nodos. Eso ya será otra historia...

NullReferenceException en Microsoft.Office.Server.Administration. UserProfileApplicationProxy. get_ApplicationProperties

23.6.11 / Comments (1) / by Enric Carrión

Puede suceder que, accediendo a un sitio por navegador o través de la API (configurando navegación o accediendo al UserProfileManager), nos aparezca esta NullReferenceException provocada por el método get_ApplicationProperties de Microsoft.Office.Server.Administration.UserProfileApplicationProxy. Un posible stack trace puede ser:

Object reference not set to an instance of an object

at Microsoft.Office.Server.Administration.UserProfileApplicationProxy.get_ApplicationProperties()
at Microsoft.Office.Server.Administration.UserProfileApplicationProxy.get_PartitionIDs()
at Microsoft.Office.Server.Administration.UserProfileApplicationProxy.IsAvailable(SPServiceContext serviceContext)
at Microsoft.Office.Server.Audience.AudienceManager.IsCurrentUserInAudienceOf(AudienceLoader audienceLoader, String audienceTextRepresentation, Boolean showUntargetedAudience)
at Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapNode.GetNavigationChildren(NodeTypes includedTypes, NodeTypes includedHiddenTypes, Boolean trimmingEnabled, OrderingMethod ordering, AutomaticSortingMethod method, Boolean ascending, Int32 lcid)
at Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapNode.GetNavigationChildren(NodeTypes includedTypes, NodeTypes includedHiddenTypes, OrderingMethod ordering, AutomaticSortingMethod method, Boolean ascending, Int32 lcid)
at SetNavigationFeatureReceiver.SetNavigationFeatureReceiver.SetNavigation(SPFeatureReceiverProperties properties)
at SetNavigationFeatureReceiver.SetNavigationFeatureReceiver.FeatureActivated(SPFeatureReceiverProperties properties)
at Microsoft.SharePoint.SPFeature.DoActivationCallout(Boolean fActivate, Boolean fForce)

Bien, las pistas para la solución las da este post:

http://social.technet.microsoft.com/Forums/en/sharepoint2010setup/thread/bdb0ea0e-13f1-4191-8f92-9d2fc2605115


En principio, el usuario que ejecuta ese código (depende del contexto será el usuario logado al sitio, el que ejecuta la aplicación, el owstimer, el usuario del pool, ...) debería tener permisos suficientes sobre la User Profile Service Application.

Lo que se propone es que, si se está ejecutando desde Visual Studio, el usuario desarrollador logado en sesión Windows debería ser administrador de la User Profile Service Application y ser administrador de granja. Bien, yo añado sobre esto que es necesario realizar un IISRESET (no parece suficiente con reiniciar el app pool) para que empiece a funcionar.

SharePoint 2010 no hace caso de los anchor tags al cargar la página

23.6.11 / Comments (0) / by Enric Carrión

Tema candente en la comunidad forera de SharePoint, con aportaciones frescas en:

http://sharepoint.stackexchange.com/questions/10994/sharepoint-2010-and-anchor-tags
http://social.msdn.microsoft.com/Forums/en/sharepoint2010general/thread/f7eab808-da8a-44fd-9933-f9b992f5affc

La situación es que, al cargar una página de publicación o wiki con un anchor (es decir, mipagina.aspx#mianchor, donde mipagina tiene un <a id="mianchor" name="mianchor"></a>), SharePoint no pone el foco en dicho anchor sino arriba del todo de la página. Motivo: código javascript incrustado y ejecutado por el propio SharePoint. Categoría: probablement un bug de producto. Workaround: complicado. A falta de bucear en las procelosas aguas de los eventos javascript de la plataforma para intentar reescribir la función que causa este desaguisado, la única propuesta surgida en la comunidad es:
<script type="text/javascript">
 setTimeout(Reload,2000);
 function Reload()
 {
  window.location.hash=self.document.location.hash.substring(1);
 }
</script>

Es decir, un timeout que vuelva a "cargar" la página, lo que fuerce de nuevo el cambio de foco a nuestro anchor. ¿Funciona? Sí. ¿Es elegante? No. ¿Al cargar produce un efecto raro de foco arriba y abajo? Sí. ¿Seguiremos informando cuando podamos dar con una solución mejor? No hay duda.

Post dedicado a Carlos Giol.

Content query web part: The query cannot be completed because the number of lists in the query exceeded the allowable limit

1.6.11 / Comments (1) / by Enric Carrión

Bueno, una perla más de conocimiento adquirida gracias a la experiencia empírica de comprobar cómo un sitio de producción que funciona perfectamente, después de crear un nuevo subsitio, empieza a mostrar en web parts de consulta de contenido el siguiente mensaje:

There is a problem with the query that the web part is using. Check the configuration of the web part and try again.

Mirando el log de SharePoint aparece la siguiente excepción:

The query cannot be completed because the number of lists in the query exceeded the allowable limit.

Después de agotar la sarta de improperios producto del momento de tensión, la solución encontrada es localizada rápidamente y puesta en funcionamiento con éxito.

  • El límite de listas soportadas en un CQWP cross-site (o SPSiteDataQuery) por defecto es 1000.
  • Ese límite se puede ampliar al valor deseado o poner a 0 (infinito) pero mucho ojo con los efectos colaterales de rendimiento.
  • En un SPSiteDataQuery podemos usar directamente la propiedad MaxListsLimit para establecer ese valor.
  • En un CQWP debemos establecer la propiedad ListsOverride del web part a:
  • <property name="ListsOverride" type="string">
    <![CDATA[<Lists ServerTemplate="#ID_TEMPLATE_LISTA#" MaxListLimit="#LÍMITE#">]]>
    </property>

ID de templates de lista. Los más comunes:

100 Generic list

101 Document library

102 Survey

103 Links list

104 Announcements list

105 Contacts list

106 Events list

107 Tasks list

108 Discussion board

109 Picture library

119 Wiki Page library

301 Blog Posts list

302 Blog Comments list

303 Blog Categories list

850 Page Library

Más en http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splisttemplatetype.aspx

 

Info extraída de http://technet.microsoft.com/en-us/library/cc263061(office.12).aspx#Content_query

Provisionar una sandboxed solution con el Solution Hash y el Solution Id

26.5.11 / Comments (2) / by Enric Carrión

Guardamos un artefacto (un sitio, por ejemplo) como sandboxed solution y obtenemos un bonito fichero WSP que luego queremos volver a desplegar declarativamente en otro entorno. Eso es posible si creamos un elemento Module en nuestro proyecto SharePoint 2010 de nuestra solución de Visual Studio. Pero junto con el fichero hay que provisionar una serie de metadatos indispensables para disponer de la solución lista para activar en el entorno de destino. Esto quedaría como:

<Module Name="MySolutions" Url="_catalogs/solutions" Path="MySolutions" RootWebOnly="TRUE">
<File Url="MyExportedSolution.wsp" Type="GhostableInLibrary">
<Property Name="SolutionId" Value="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx}" />
<Property Name="SolutionHash" Value="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<Property Name="SolutionTitle" Value="Mi solución" />
</File>

El SolutionTitle en el fondo no es relevante, pero sí lo son, y mucho, los otros dos. ¿Cómo obtenerlos? PowerShell al rescate de nuevo. En el entorno origen, en la colección de sitios donde acabamos de exportar la solution sandbox, iremos a buscar estos metadatos de la siguiente manera:

$site = get-spsite http://misitio
$web = $site.OpenWeb()
$list = $web.lists["Solution Gallery"]
$item = $list.GetItemById(1)   # El id que corresponda, of course
$item["SolutionHash"]
$item["SolutionId"]

Y simplemente utilizarlos para el Module. Finalmente, una vez provisionada la solución en el entorno destino, podemos activarla manualmente o mediante el comando PowerShell install-spusersolution:

install-spusersolution -identity MySolution.wsp -site http://misitiodestino

SharePoint + Enterprise + Social = Beezy

14.5.11 / Comments (0) / by Enric Carrión

Es turno de anunciar el lanzamiento de un nuevo producto aquí en Spenta, que está destinado a ser un verdadero huracán en el mundo corporativo. Un mundo que, hasta ahora, carecía de soluciones realmente enfocadas a la colaboración ágil entre empleados beneficiándose de las herramientas Web 2.0 a que tan acostumbrados estamos fuera del ámbito laboral. Ahora estas herramientas tienen su lugar en la empresa gracias a Beezy, construido “on top of” SharePoint 2010 y Office 365.

beezy-logo
http://www.gobeezy.com/
http://twitter.com/#!/followbeezy

La solución, técnicamente, consiste en una capa de aplicación por encima de SharePoint, que registra, analiza y explota convenientemente un conjunto de datos sociales obtenidos de la propia plataforma cruzándolos con datos sociales propios (comentarios, follows, likes, actividades). Fácil resumirlo, pero se trata de un auténtico reto técnico, el éxito del cual tiene mucho que ver con mi querido compi David Martos. Enhorabuena a él, a todos los implicados y… stay tuned.

Microsoft Theme Builder beta disponible para Office y SharePoint

19.4.11 / Comments (0) / by Enric Carrión

Microsoft ha publicado la beta de un programa más que prometedor: SharePoint Theme Builder, una aplicación de escritorio que permite crear y editar visualmente temas de Office 2007/2010 (ficheros THMX). Evidentemente podemos utilizar los temas generados por este aplicativo para subirlos a SharePoint Foundation y Server 2010 y aplicarlos a nuestros sitios.

image

Podéis descargarlo en http://connect.microsoft.com/ThemeBuilder

 

Requisitos software:

.NET Framework 3.0:

http://www.microsoft.com/downloads/details.aspx?FamilyId=10CC340B-F857-4A14-83F5-25634C3BF043&displaylang=en

Primary Interop Assemblies:

http://www.microsoft.com/downloads/details.aspx?familyid=59daebaa-bed4-4282-a28c-b864d8bfa513&displaylang=en

Herramientas para rastreo de logs de SharePoint (2)

14.4.11 / Comments (2) / by Enric Carrión

Mandíbula desencajada se me ha quedado al conocer gracias a Javier Hidalgo la existencia desde 2009 de ULS Viewer, aplicación de explotación de logs ULS que, por comparación, deja a las dos anteriores herramientas comentadas en la categoría de semi-chusta.

image

ULS Viewer es también un simple ejecutable, descargable desde MSDN (pero al parecer sin soporte de la propia MS), y ofrece todo lo siguiente:

  • Leer eventos de log de ficheros especificados por usuario, de portapapeles o en tiempo real desde los ficheros de log ULS del servidor. Podemos agregar (append) eventos de distintas fuentes.

image

  • Filtrado, por supuesto, por todos los atributos del evento y por distintas operaciones de filtrado. Los criterios de filtro pueden acumularse con ANDs y ORs.

image

  • Notificaciones: es decir, la posibilidad de ser informado en el área de notificaciones de Windows de los eventos críticos o de aquellos eventos filtrados por nuestros criterios. Esta funcionalidad tiene muchas similitudes con el SPTraceView de Hristo Pavlov, herramienta que también circula por el mundillo desde hace tiempo, pero que a mi juicio queda desfasada ante la potencia de ésta.

image

image

  • Posibilidad de definir formato condicional de color para ciertos eventos que cumplan ciertos filtros. Nuestros ojos agradecerán sin duda evitar tanta monocromía.

image

  • Podemos organizar y filtrar los eventos por los Correlation ID.

image

  • Smart Highlight: ésta me encanta. Cuando se activa, al hacer hover encima de un valor determinado de una columna, resalta automáticamente el resto de valores iguales.

image 

  • Podemos añadir bookmarks en los eventos de log.
  • Podemos buscar textualmente en toda la información de log abierta en la sesión.
  • Podemos ocultar/reordenar las columnas de los eventos.
  • Podemos guardar la sesión en ficheros de log o en workspaces (conjunto de fuentes, filtros, formatos y notificaciones configuradas en una sesión).

 

Simplemente apabullante. SharePoint 2010 debería haber contado con algunas si no todas estas funcionalidades de serie. Mis servidores ya no usan otra cosa.

Windows Media Load Simulator: configuración y referencia de errores

13.4.11 / Comments (0) / by Enric Carrión

Al testear una plataforma de Windows Media Services 2008 disponemos de la herramienta Windows Media Load Simulator (conocida también como LoadSim y que no tiene versión específica para 2008 sino que sigue utilizándose la de WMS 9 Series). A continuación lanzo algunos tips de configuración y uso sobre ella.

image

image

  • Puede instalarse sobre Windows XP, Windows 7 o un sistema operativo de servidor como Server 2008.
  • Sirve para probar conexiones unicast RTSP o HTTP (publishing points on-demand), pero no multicast (publishing points broadcast).
  • Debe ejecutarse la herramienta siempre con el usuario con el que se instaló. El motivo es que durante la instalación se realizan algunos cambios sobre ficheros de configuración de Windows Media Player que dependen de usuario.
  • Para habilitar un servidor de Windows Media Services, es necesario:
        • Crear un fichero WMLoad.asf en la carpeta c:\wmpub\wmroot del servidor.
        • Permitir conexiones entrantes sobre el publishing point unicast <Default>.
        • En algunos foros se comenta la necesidad de crear un fichero wmloadsafe.txt en la carpeta c:\, pero por lo menos en las pruebas que he efectuado hasta ahora, la existencia o no de ese fichero no parece determinante.
  • Las URL’s de streams de video deben corresponder todas al mismo servidor (que debe coincidir con el que está configurado en “Default Server”).
  • Lo ideal antes de probar la herramienta de carga es verificar que desde ese mismo equipo con Windows Media Player es posible reproducir el video a testear (si somos capaces de verlo en el reproductor, nos descartará cualquier tipo de problema de conectividad con el servidor, de misspellings con URL’s o de servicios/puntos de publicaciones no iniciados).

 

Errores:

  • SERVER ACCESS DENIED,, Code 0x00000000, Description: The operation completed successfully.
    • Este error puede apuntar hacia varios motivos: no hay conectividad con el host del servidor de video, el servidor está parado, la herramienta no ha podido encontrar el fichero WMLoad.asf o bien el publishing point <Default> está parado.
  • Code: 0x80070002, Description: Error Description Not Found
    • Este error aparece al intentar ejecutar WMLS desde una sesión de un usuario distinto al que instaló la herramienta.
  • Code: 0xC00D2EE6, Description: Error Description Not Found
    • Este error puede aparecer cuando el publishing point del video a testear está parado o al superar el límite de conexiones simultáneas permitidas por el servidor o el publishing point.
  • Code: 0xC00D001A, Description: Error Description Not Found
    • Este error se produce al no encontrar el fichero de video referenciado en el servidor.

 

Para profundizar más sobre el tema:

Checking Server Performance with Windows Media Load Simulator

Libro: Microsoft Windows Media Resource Kit

Entity Framework 4 + WCF + lazy loading: toma solo lo necesario

12.4.11 / Comments (0) / by Enric Carrión

Tema ya trillado en otros idiomas pero sobre el cual me apecete disertar en la lengua de Cervantes. Premisas: trabajamos en un proyecto con Entity Framework 4, que genera nuestro modelo de entidades (con relaciones entre ellas) a partir de nuestro repositorio (típicamente un SQL Server). Exponemos parte de ese modelo a través de una capa de servicios WCF utilizando como valores de retorno de los métodos del servicios las propias clases del modelo. Ningún problema puesto que WCF se encargará de serializar adecuadamente esas clases y las ofrecerá a los clientes del servicio como clases proxy.

Un buen día descubrimos que el aplicativo empieza a ofrecer un rendimiento más que discutible y nos preguntamos por qué. Acude al rescate una herramienta de pago con trial de 30 días que bien vale, por lo menos, evaluarla. Entity Framework Profiler (EFProf), de los conocidos Hibernating Rhinos, permite hacer un profiling de las SQL lanzadas contra nuestra base de datos por el engine de Entity Framework. Simplemente hay que incluir la siguiente llamada en la inicialización de nuestra aplicación (p. ej. en Application_Start de global.asax):

HibernatingRhinos.Profiler.Appender.EntityFramework.EntityFrameworkProfiler.Initialize();

La herramienta EFProf monitorizará desde ese momento cada todo Object Context de EF y nos mostrará qué comandos SQL está utilizando para cargar datos:

image

Dos cosas: primero, hay un contexto que necesita 97 SQL queries?!?!? Esto es mucho comparado con el resto de contextos. Segundo, ese mismo contexto está lanzando advertencias Using a single object context in multiple threads is likely a bug, SELECT N+1 y Too many database calls per object context.

image

image

Bueno, ahí hay un problema, claramente. Pero esas alarmas es difícil que provengan de mi código. Al fin y al cabo, mi interacción con EF se limita a utilizar queries de LINQ-To-Entity Framework del estilo:

var result = myObjectContext.Reports
.OrderBy(r => r.Title);

¿Como puede generar eso tanta query SQL? Bien, pues desvelo el misterio sin más. Hay dos claves: la primera está en WCF, que juega un papel más importante del que podríamos pensar. WCF necesita devolver los objetos del modelo y para ello serializar sus propiedades y colecciones. El proceso de serialización no sabe lo que hay por debajo, simplemente accede a esas propiedades para obtener sus valores, y punto. La segunda clave es lo que hay realmente por debajo, que es la configuración de lazy loading del Object Context de EF. Por defecto en la versión 4 los contextos son creados con la propiedad LazyLoadingEnabled a true. Esto significa que al acceder a propiedades de un objeto del modelo, si esas propiedades son a su vez otros objetos relacionados, esos accesos van a generar queries de carga adicionales. WCF pide, y EF se lo da. Todo. Propiedad por propiedad. Prácticamente nos estamos trayendo la base de datos entera query a query, sin un solo join que agrupe un poquito las peticiones. Mal.

Lo primero: desactivar el lazy loading en el contexto que utiliza WCF (LazyLoadingEnabled = false). Pero ahora tenemos un problema. Esta configuración produce que la consulta de un objeto no devuelva los objetos relacionados, a menos que se lo especifiquemos manualmente.

Por ejemplo, imaginemos que nuestra entidad Reports tiene relacionados unos Users que deben estar disponibles al realizar la consulta LINQ anterior. Entonces esos users no se cargarán a menos que hagamos algo como lo siguiente:

myObjectContext.LazyLoadingEnabled = false;

var result = myObjectContext.Reports
.Include("Users")
.OrderBy(r => r.Title);

Este eager loading que estamos haciendo utilizando el método Include generará una query SQL con un join entre la tabla de Reports y la de Users, que es lo que buscábamos desde el principio. Vuelta a los clásicos. Y el Entity Framework Profiler contento, puesto que habremos reducido drásticamente la carga de queries.

Bibliografía para insomnes:

EF – Loading related objects

Silverlight: Unable to start debugging. The Silverlight Developer Runtime is not installed

11.4.11 / Comments (0) / by Enric Carrión

Un entorno maravillosamente configurado para desarrollar y debugar sobre Silverlight 4 en Visual Studio 2010 (con las Silverlight Tools 4) te pregunta un día si deseas actualizar Silverlight. Tú, que no tienes un “no” para nadie y menos para una splash screen de actualización, sigues adelante con ello. Después del proceso, dejas de poder ejecutar tus proyectos en Visual Studio a causa de:

Unable to start debugging. The Silverlight Developer Runtime is not installed. Please install a matching version

No panic. La solución consiste simplemente en reinstalar Windows Developer Runtime encima de nuestra instalación y podremos volver a ejecutar y depurar nuestros proyectos.

Reemplazar la página de Access Denied en SharePoint

7.4.11 / Comments (0) / by Enric Carrión

"Have you ever...?" La página de acceso denegado en SharePoint es feucha, y puede ser blanco de las iras de aquellos que han mimado y cuidado hasta el extremo el branding de su portal. De pronto, un usuario accede al sitio, a alguien se le olvida asignar un permiso y... ugh. Qué desagradable.

 

image

image

Y lo bueno (y lo malo) es que por ahora hay dos maneras de evitar esto: controlando las excepciones de acceso denegado por código (SPSecurity.CatchAccessDeniedException) o bien implementando un HttpModule como el siguiente:

using System;
using System.Web;

namespace MyNamespace
{
public class CustomAccessDenied : IHttpModule
{
public void Init(HttpApplication context)
{
context.EndRequest += ContextAcessDenied;
}

public void Dispose()
{

}

static void ContextAcessDenied(object sender, EventArgs e)
{
try
{
var httpApp = sender as HttpApplication;
if (httpApp != null)
{
var context = httpApp.Context;

var httpUrl = context.Request.Url.ToString();

if (httpUrl.ToLower().Contains("/_layouts/accessdenied.aspx"))
{
HttpContext.Current.Server.ClearError();
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Redirect(
"/Anonymous/Pages/CustomAccessDenied.aspx", false);
}
}
}
catch (Exception ex)
{
//...
}
}

}
}

Este HttpModule redirigiría las peticiones que han devuelto con URL '_layouts/accessdenied.aspx' a una página personalizada que debería estar en un subsitio anónimo de nuestra colección de sitios. Ello implica, sí, habilitar la disponibilidad de acceso anónimo en toda la colección.

Este HttpModule sería, de las dos soluciones, la menos intrusiva en el código de nuestro portal. Y que conste que no soy para nada partidario de hinchar los web apps de handlers y módulos, pero reconozco que en esta ocasión el requerimiento lo merece...

 

Edit: cuidado con la opción "Sign as a different user" del menú Welcome. Redirige a /_layouts/accessdenied.aspx?loginasanotheruser=true&Source=... Tenemos que modificar el HttpModule para que deje pasar estas peticiones.

Localized resource for token 'groupExpColl' could not be found

1.4.11 / Comments (0) / by Enric Carrión

Una de dejar el log de ULS como una patena. Si estamos desplegando features con definiciones de lista, podemos advertir en los ficheros de log las siguientes entradas:

Localized resource for token 'groupExpColl' could not be found 
for file with path: "C:\Program Files\Common Files\Microsoft 
Shared\Web Server Extensions\12\Template\Features\
<nuestra_feature>\<nuestra_lista>\schema.xml
Localized resource for token 'filterIframeTitle' could not be found 
for file with path: "C:\Program Files\Common Files\Microsoft 
Shared\Web Server Extensions\12\Template\Features\
<nuestra_feature>\<nuestra_lista>\schema.xml

Esto se debe a que en el schema.xml que estamos desplegando aparecen dos literales de recursos:

  • $Resources:groupExpColl;
  • $Resources:filterIFrameTitle;

que no hacen referencia explícita a ningún fichero de recursos, pero que están localizados en el “core.resx”. Tenemos dos opciones: o reemplazamos estas ocurrencias por $Resources:core,groupExpColl; y $Resources:core,filterIFrameTitle; o, mucho mejor, si especificamos en nuestro feature.xml:

DefaultResourceFile="core"

Este post es una adaptación libre de http://blogs.msdn.com/b/carloshm/archive/2008/03/13/did-you-find-groupexpcoll-error-in-your-logs.aspx