jueves, 23 de febrero de 2012

Ajustar el tamaño de un IFrame a su contenido

O cómo evitar que aparezcan scrolls repetidos en una aplicación web.
En relación con la entrada “Cómo acceder a una aplicación ASP.NET externa desde Sharepoint” apareció un problema con el visor de páginas (más bien con el IFrame utilizado para exponer la aplicación en Sharepoint) relacionado con las dimensiones del mismo.
En concreto, lo que sucede es que si la página que “asomamos” por el IFrame es más grande que la altura que le hemos asignado al IFrame, aparece una barra de scroll dentro del IFrame. Esto ya de por sí queda un pelín ‘raro’, pero si lo combinamos con que en la página ‘contenedora’ también aparece el scroll, entonces ya queda fatal de la muerte, con dos scrolls “independientes”
NOTA: Lo lamento enormemente, pero en esta ocasión no dispongo de ninguna de captura de pantalla, así que toca todo texto ‘plano’

Solución 1

La más rápida y sencilla, poner al IFrame una altura ‘suficiente’ para que no aparezca el scroll interior. ¿Hay alguien a quien se le ocurra el problema con esta solución?
¿no?
Venga, que es muy fácil: El problema es que si ponemos la altura muy grande (porque sabemos o intuimos que el contenido del IFrame es también grande, lo que conseguimos es que en la página ‘contenedora’ aparezca el scroll vertical… Pero siempre! Imaginad que una de las páginas que asomamos por el IFrame es más pequeña, entonces queda un scroll en la pantalla, pero todo vacío hacia abajo, de verdad no queda muy bien.

Solución 2

[CORRECCIÓN]] Ojo porque el código original publicado puede llevar a un problema de uso de CPU en el equipo cliente. Las correcciones necesarias aparecen en rojo en el código.

¿No estaría bien que el IFrame que utilizamos se ajustase a su contenido? Como podéis imaginar por el título del artículo, esto es precisamente lo que se va a explicar a continuación, y además es bastante sencillo.
NOTA: Una cosa a tener en cuenta. Esta solución se implementa con JavaScript y accede al contenido del IFrame, tenéis que tener en cuenta que si el contenido está en un dominio diferente al de la página contenedora, encontraréis problemas de “Acceso Denegado” salvo que implementéis las instrucciones del artículo mencionado al principio.

Al turrón (para la Solución 2)

En la página donde vamos a poner el IFrame (o mejor aún en un archivo .js) ponemos el código de JavaScript que tenemos a continuación. El archivo .js está disponible para descarga desde aquí.

<script type="text/javascript" language = "javascript" >
    function doIframe(){
        o = document.getElementsByTagName("iframe");
        for(i=0;i<o.length;i++){
            if (/\bautoHeight\b/.test(o[i].className)){
                removeEvent(o[i],'load', doIframe);
                setHeight(o[i]);
                addEvent(o[i],"load", doIframe);
            }
        }
    }
    function setHeight(e){
        if(e.contentDocument){
            e.height = e.contentDocument.body.offsetHeight + 35;
        } else {
            e.height = e.contentWindow.document.body.scrollHeight;
        }
    }
    function addEvent(obj, evType, fn){           
        if(obj.addEventListener)
        {
            obj.addEventListener(evType, fn,false);
            return true;
        } else if (obj.attachEvent){
            var r = obj.attachEvent("on"+evType, fn);
            return r;
        } else {
            return false;
        }
    }
    function removeEvent(obj, evType, fn){
        if(obj.removeEventListener)
        {
            obj.removeEventListener(evType, fn,false);
            return true;
         } else if (obj.detachEvent){
             var r = obj.detachEvent(""on""+evType, fn);
             return r;
         } else {
             return false;
         }
     }
</script>

No voy a entrar en qué hacen las funciones setHeight (creo que es obvio) y addEvent (para ésta, está Google) pero sí que me voy a detener un poco en la función doIframe, ya que es fundamental para el funcionamiento del script. Como podemos ver, esta función se recorre todos los IFrames que existan en la página, y les adjunta el evento “load”, pero solamente a aquellos IFrames que tengan especificada una estilo CSS “autoHeight”. ¿Por qué? Si no ponemos esa condición o similar, el script se ejecutará para todos los IFrames que tenemos en la página, y eso puede producir una sensación de descontrol, es preferible elegir cuáles de los IFrames queremos que se ajusten y cuáles no es necesario.
Con esto no hemos terminado, necesitamos el código de JavaScript que desencadena todo el proceso, se puede incluir en la página o en el mismo archivo .js:

<script type ="text/javascript" language ="javascript" >
    if (document.getElementById && document.createTextNode){
        addEvent(window,'load', doIframe);
    }
</script>

Estas instrucciones son las que consiguen que el proceso se lance al cargar la página en el navegador.
Y ya para terminar un detalle del IFrame, para recordar la condición del estilo css:

<iframe id = "xxx" frameborder = "0" scrolling = "no" runat ="server" class = "IframeCarga autoHeight" visible = "false" src = "/dirección" ></iframe>
Y esto es todo!!!!

miércoles, 15 de febrero de 2012

Error occurred in deployment step 'Recycle IIS Application Pool': The local SharePoint server is not available

Estos días he tenido que pelearme un poco con un servidor MSS2010 de desarrollo debido al error que da el título a este artículo, os pongo en antecedentes:

Situación inicial

Estamos desarrollando una WebPart para MSS 2010, nada fuera de lo común, una WebPart sencillita, cuyos detalles además no vienen al caso.

Por motivos que tampoco vienen al caso, el desarrollo se estaba realizando con un usuario ‘genérico’ mediante escritorio remoto. Todo funcionando de lujo hasta que otro compañero necesita conectar en remoto también con ese usuario, como ya sabréis no puede haber (creo) dos conexiones remotas distintas con el mismo usuario (si se puede ya me avisaréis).

El caso es que ya no se puede desarrollar con el dichoso usuario ‘genérico’… Ningún problema, inicio el escritorio con mi usuario, mapeo el código de TFS y a funcionar…

El problema

El proyecto se abre sin problema, compila de lujo (como era esperado) pero (siempre hay un pero) al ir a realizar el despliegue de la Web Part, de repente mensaje de error:

image

y en los detalles del error en la ventana de “Error List”:

image

Ay ay ay, uy uy uy…

Menos mal que nos queda Google… Revisando por ahí posibles soluciones al problema, resulta que el error viene (lógico por otra parte) por un problema de permisos de mi usuario en la infraestructura Sharepoint del servidor de desarrollo.

La solución

Gracias al siguiente artículo, el problema se soluciona utilizando el comando Add-SPShellAdmin.

¿Cómo lo utilizamos para solucionar el problema?

Iniciamos una consola de administración de Sharepoint y ejecutamos el comando:

Add-SPShellAdmin dominio\usuario

Puede producirse un error de conexión con el servidor local (el mismo que nos da desde Visual Studio) si se produce no nos queda otra que pedir que nos ejecuten el comando con el usuario ‘genérico’

Una vez ejecutado el comando, al intentar realizar el despliegue, puede producirse el siguiente error:

image

Si revisamos el visor de sucesos del servidor, podemos ver que se trata de un acceso denegado con la Base de Datos de contenido del servidor Sharepoint. Para solucionar este problema, tenemos que utilizar el mismo comando anterior, especificando el id de la Base de Datos de Contenido que nos indica en el visor de sucesos.

Averiguar el ID de la Base de Datos de contenido

Ejecutamos el siguiente comando PowerShell:

Get-SPDatabase | Select Name, Id

Localizamos el nombre que hemos obtenido en el Visor de Sucesos, y apuntamos el Id, que utilizaremos en la siguiente instrucción:

Add-SPShellAdmin dominio\usuario –database Id

 

y con esto, a funcionar!!!

jueves, 9 de febrero de 2012

Instalar una aplicación web ASP.NET en un sitio de Sharepoint 2010

Una de las preguntas que vuelve de tiempo en tiempo es ¿Cómo ponemos una aplicación ASP.NET en un sitio web que esté ocupado por Sharepoint?
La respuesta en principio es sencilla, basta con generar un directorio virtual en el sitio web de Sharepoint en el que alojar la aplicación.
Peeeero, es muy probable que nos aparezcan errores relacionados con el archivo web.config de la aplicación, ya que como ya sabemos los archivos web.config se van heredando en la estructura IIS, y el primer web.config que aparece en la estructura es el de Sharepoint, que añade sus entradas (y que normalmente interfieren con nuestra aplicación)
A continuación vamos a ver una serie de posibles problemas que podemos encontrar y una solución ‘sencilla’ para los mismos.
NOTA: Los errores que vamos a ver a continuación, ni son todos los que están, ni están todos los que son. Conforme vayamos encontrando más problemas, los iremos añadiendo a la lista.

Security Exception

Uno de los primeros errores que podemos encontrarnos es este, provocado por la configuración de seguridad del sitio web.
image

Solución

Cuidadito, porque igual esto no podemos / debemos hacerlo a la ligera.
En el web.config de la aplicación indicamos explícitamente el nivel de confianza de la aplicación, como por ejemplo:
<system.web>
….
    <trust level="Full" originUrl="" />
</system.web>

Problemas con la sesión

Otro problema que podemos encontrarnos tiene que ver con las variables de sesión, ya que Sharepoint gestiona las sesiones de una manera específica.

Solución

En el web.config de la aplicación añadimos el módulo ‘estándar’ de gestión de la sesión de .net:
<system.webServer>

    <modules>
    …
        <add name="Session" type="System.Web.SessionState.SessionStateModule" />
    </modules>
</system.webServer>

A continuación configuramos las páginas de la aplicación para habilitar la gestión de la sesión:
<system.web>

    <pages enableSessionState="true">

    </pages>

</system.web>

A funcionar (por el momento)

Cómo acceder a una aplicación ASP.NET externa desde Sharepoint

Situación inicial

No preguntéis por qué, pero surgió la necesidad de acceder desde un servidor Sharepoint 2010 a una aplicación ASP.NET alojada en otro servidor, ambos accesibles desde internet y en distintos dominios.
Pongamos que la ruta de acceso al servidor Sharepoint 2010 era “www.mss2010.es” y la ruta de acceso a la aplicación era “aplicacion.aplicaciones.es”

Solución sencilla

Nada complicado, vamos al servidor Sharepoint, añadimos una Web Part “Visor de Páginas” y configuramos la URL de acceso para que muestre la página de inicio de la aplicación. ¡Listo!

Problema

¿Listo? Noooo. Una vez realizada la operación, podemos observar que la aplicación externa hace (perdón por la expresión) ‘cosas raras’. Por ejemplo:
Al acceder a la aplicación, almacenamos una serie de información en variables de sesión (de nuevo, no preguntéis por qué) que luego se utilizan en distintas páginas de la aplicación. Cuando accedemos a la aplicación directamente desde el navegador web, todo funciona sin problemas, pero al acceder a la aplicación desde el Visor de Páginas de Sharepoint 2010, esas variables de sesión NO se mantenían al navegar por la aplicación; es más, cada página a la que accedemos dentro de ese visor reiniciaba la sesión en la aplicación externa
Después de mirar y remirar por ahí, llegamos a la conclusión de que este funcionamiento es correcto. ¿Por qué? el navegador de internet (en este caso Internet Explorer, y no, por favor no preguntéis por qué) detecta que estamos incluyendo en una página de un dominio (www.mss2010.es) un marco (el visor de páginas) cuya información proviene de otro dominio (aplicacion.aplicaciones.es) esta operación por defecto es considerada como ‘insegura’ en las zonas de sitios de confianza e Internet.

¿Cómo lo solucionamos?

La opción “sencilla” pasaría por configurar los clientes para que tanto el sitio web de Sharepoint como el de la aplicación estuvieran en la zona de Intranet Local…
¿Perdón?¿Hola? estamos en Internet, NO podemos hacer eso!
La segunda posibilidad pasa por poner las dos aplicaciones en el mismo dominio de Internet (reconozco que no lo he probado, porque en este caso no era una posibilidad)
La tercera opción es modificar la aplicación web para que se identifique como ‘segura’ ante el navegador de internet, pero claro, cuidado porque para hacer esto tenemos que poder modificar la aplicación externa. En este caso podíamos hacerlo, así que lo hicimos.

Al turrón

Entiendo que habrá múltiples maneras de hacerlo, pero resultó muy sencillo modificar el archivo global.asax de la aplicación externa para que cuando se inicia la petición a la aplicación, ésta añada un encabezado concreto a la petición HTTP que marque la aplicación como segura. Por ser más ‘puristas’ este es un extracto en inglés explicando un poco más el tema:
Starting in Internet Explorer 6 support for the Platform for Privacy Preferences (P3P) Project was introduced. The P3P standard notes that if a FRAMESET or a parent window references another site inside a FRAME or inside a child window, the child site is considered third party content. Internet Explorer, which uses the default privacy setting of Medium, silently rejects cookies sent from third party sites. So consequently a large percentage of your visitors may end up having an unhappy experience on your site.
You can add a P3P compact policy header to your child content, and you can declare that no malicious actions are performed with the data of the user. If Internet Explorer detects a satisfactory policy, then Internet Explorer permits the cookie to be set.
Al final, el método en el evento “Application_BeginRequest” queda como se ve a continuación (ojo, que está en VB.NET)
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
    HttpContext.Current.Response.AddHeader("p3p", "CP=""CAO PSA OUR""")
End Sub


Una vez añadido el evento y compilada y publicada la aplicación externa, el sistema comienza a funcionar de nuevo…

miércoles, 8 de febrero de 2012

Cómo conectar Visual Studio 2008 con TFS 2010

Este es un mini post con las instrucciones básicas para conseguir conectar Visual Studio 2008 con proyectos en Team Foundation Server 2010.

Al turrón

  1. Instalar el Service Pack 1 de Visual Studio 2008
  2. Instalar la actualización para TFS 2010 (Visual Studio 2008 SP1 Compatibility Update for TFS 2010)
  3. Instalar el Team Explorer (Visual Studio Team System 2008 Team Explorer)

Estos tres primeros pasos son ‘opcionales’ lógicamente si ya tenemos instalado el Service Pack 1, el paso 1 nos lo podremos saltar, así como la instalación del Team Explorer si ya lo tenemos o si la versión de Visual Studio 2008 es la “Team Suite”

Lo último que hay que hacer es añadir manualmente el servidor de TFS. Para esto, en el registro de Windows tendremos que añadir la entrada correspondiente a dicho servidor. ¿Dónde?

  1. Navegamos a la entrada “[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\TeamFoundation\Servers]”
  2. Creamos un nuevo Valor de Cadena y en el nombre ponemos el nombre del servidor, y en el valor ponemos la url de acceso al TFS, como podemos ver en la captura posterior.
  3. Creamos una nueva Clave con el nombre del servidor utilizado en el paso 2.
  4. En la nueva Clave, creamos dos valores DWORD con las características de la tabla que aparece más abajo

Y ya estamos listos para conectar desde Visual Studio al servidor TFS, abriendo la ventana del Team Explorer y seleccionando el servidor entre la lista de disponibles.

Más información:

Registro del servidor TFS (Paso 2)

image

Datos de configuración del servidor (Paso 4)

Nombre Valor
AutoReconnect 1
Offline 0

 

Si tenemos que hacer esta configuración en varios equipos, es conveniente generar un archivo .reg para automatizar la modificación del registro, este sería el contenido con los datos que hemos utilizado:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\TeamFoundation\Servers]
"
ServidorTFS"="http://servidorTFS:8080/tfs/ProjectCollection"

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\TeamFoundation\Servers\ServidorTFS]
"Offline"=dword:00000000
"AutoReconnect"=dword:00000001

Selección del servidor en Team Explorer:

image

miércoles, 1 de febrero de 2012

Visual Studio y el misterio de las plantillas desaparecidas

Hoy al arrancar Visual Studio (2010) me he encontrado con la siguiente sorpresa (muy desagradable, por cierto) al ir a generar un nuevo proyecto:
image
¿Dónde están mis plantillas de proyecto? Sudores fríos por la espalda… menudo comienzo de día ¡Socorro!

Al turrón

Tras el primer momento de pánico, he recordado que Visual Studio proporciona una serie de parámetros de ejecución del IDE (devenv.exe) que permiten por ejemplo resetear las personalizaciones realizadas al Visual Studio, regenerar paquetes instalados, etc.
Investigando un poquillo, he encontrado lo que necesitaba:
  1. Cerramos Visual Studio.
  2. Iniciamos el “Visual Studio Command Prompt (2010)” (Como Administrador en Windows Vista / 7)
  3. Ejecutamos devenv.exe /installvstemplates.
  4. Esperamos a que termine (para asegurarnos, lo mejor es abrir el Task Manager y esperar a que el proceso “devenv.exe *32” desaparezca.
  5. Arrancamos Visual Studio, y
image
Ya está!!! Sencillo ¿verdad? ahora lo único que queda pendiente es averiguar por qué de buenas a primeras habían desaparecido prácticamente todas las pantallas de proyectos (tengo un par de componentes sospechosos, pero si llego a alguna conclusión la haré pública.
NOTA: Este comando se puede ejecutar mínimo desde la versión de Visual Studio 2005.