martes, 24 de febrero de 2009

El problema no es empezar, es continuar!!

Los blogs forman parte de lo que se llama la Web 2.0, los blogs están de moda, tener uno “mola”, cuentas tu vida, tus historias e incluso la cosas de otros. Los blogs tecnológicos se han convertido en una herramienta imprescindible en poco tiempo. Aportan la información y experiencia que no encontramos de los fabricantes y especificaciones. Los blogs están abriendo el conocimiento y la experiencia “seguro que hay alguien que ya le ha pasado antes o lo ha hecho antes”.

Microsoft por ejemplo se dio cuenta que las comunidades eran imprescindibles para conseguir el soporte que no eran capaces de prestar y otros ya habían conseguido hace tiempo “había que estar ahí”. Las comunidades relacionadas con tecnología Microsoft se han multiplicado, los MVP’s y los evangelistas están haciendo una tarea didáctica muy importante, y están teniendo éxito porque no se guardan información e intentan que todo el mundo participe.

Últimamente se están estilando los blogs corporativos, consisten en blogs promocionados por las empresas para publicitar a sus profesionales (y así misma). El problema viene cuando escribir se convierte en una obligación. Un blog es difícil de mantener, es necesario dedicarle mucho tiempo. Se necesita tiempo en escribirlo y prepararlo. Sobre todo cuando después de haberse pasado la euforia de la primera entrada dejamos de escribir o ya no encontramos tiempo ni ganas. Entonces el blog se convierte en una obligación más que en una idea o actitud.

Mi consejo es que os animéis a escribir un blog, es una actividad muy gratificante, nos sirve para clarificar las ideas y ampliar los conocimientos.

Si estáis empezando podéis visitar el post Check Point For Aspiring Blogger.

¿Sobre qué podemos escribir?

  • Post rápidos: podemos escribir acerca de una noticia u otra entrada muy completa. No es bueno escribir muchas de este tipo.
  • Post detallados: consiste en hablar acerca de un tema de forma muy detallada. ´Son útiles pero difíciles de preparar.
  • Step by step: consiste en un paso a paso. La gente lo agradece mucho
  • Post resolutivos: hablan de como resolver un problema que nos hemos encontrado.

domingo, 22 de febrero de 2009

Infopath Form Services y Office Forms Server

Continuando con el anterior post “Empezando con Infopath”, vamos a subir un poco más de nivel para ver en que versiones de sharepoint podremos utilizar Infopath Form services. En la siguiente imagen podemos ver las funcionalidades por versión de sharepoint:




Fijaros que Infopath Form services solo está disponible para la versión Enterprise de MOSS, con lo que si tenemos la versión Standard o wss3, ¿no podemos utilizar Infopath Form Services?. La respuesta, es que “sí podemos”, pero en función de que versión tengas de sharepoint habrá una solución distinta.


¿Cómo uso Infopath Form Services si tengo WSS3?

Lo que ha hecho Microsoft para este caso es sacar el componente de Infoptah Form services en un paquete que lo ha denominado “Microsoft Office Forms Server”. Form Server está basado en WSS3 por lo que no tendremos ningún problema a la hora de montarlo en nuestra granja de Wss3.

El instalador de Form server instala todos los binarios de Wss3 + los necesarios para infopath forms services.

Office Form server

Office Form server es el nombre que se ha dado a sacar Infopath Form services de MOSS y meterlo en un paquete aparte junto con Wss3.

Algo parecido se ha hecho por ejemplo con Search Server o Project Server.



La forma de licenciar Office Form server es la siguiente:

Office Forms Server

- Se licencia por servidor.

- Se necesita una CAL por cada cliente que que accede a Office Forms Server 2007


Office Forms Server for Internet Sites

- Se licencia por servidor.

- Únicamente para extranet e internet sites

- Incluye toda la funcionalidad de Microsoft Office Forms Server 2007.


De manera que tendremos que adquirir una licencia por servidor de la granja que tenga habilitadas las funcionalidades de Infopath. Si nuestra ganja wss3 está formada por dos frontales deberíamos adquirir dos licencias de servidor de Form server. Si solo tubiéramos una licencia de Form server también podríamos hacer que funcionara en uno de los nodos pero de forma muy limitada y perdiendo la alta disponibilidad.


¿Cómo uso Infopath Form Services si tengo MOSS 2007 Standard?

Este caso es distinto al de wss3 ya que la instalación de MOSS 2007 lleva todos los binarios y ficheros de la Enterprise y es a la hora de introducir la licencia cuando se habilitan o deshabilitan las funcionalidades, además que siempre podemos pasar a la Enterprise.

Para este caso Microsoft recomienda que habilites las características Enterprise y adquieras una licencia CAL de Enterprise para aquellos usuarios que usen solo las funcionalidades Enterprise.

Por lo que tendremos que ser nosotros los que limitemos los usuarios para no tener que adquirir demasiadas CAL de Enterprise.

Server and client access licensing. For each server, or each server in a farm, a valid server license must be purchased. For each client accessing Office SharePoint Server 2007, a Standard CAL must be purchased. To enable the enterprise features of Office SharePoint Server 2007, an Enterprise CAL, in addition to the Standard CAL, must be purchased. Once the enterprise features are enabled on a server, clients accessing that server, or farm of servers, should have a valid Enterprise CAL in addition to their Standard CAL when accessing the enterprise functionality.

De modo que para que los usuarios puedan utilizar Infopath Form Services en MOSS tendremos que adquirir una CAL Standard + CAL de Enterprise por cada usuario que lo utilice.

Podéis encontrar más información de licenciamiento en:

Microsoft Office SharePoint Server 2007 and Related Technologies pricing

Microsoft Office SharePoint Server 2007 frequently asked questions

o consultando con vuestro soporte preventa o comercial de Microsoft.


¿Puedo montar Office Form Server sobre una granja MOSS Standard?

Técnicamente se puede, aunque no es la mejor solución técnica y económica, ya que el coste de licencias es muy similar y casi no sale rentable.

En caso que ya tengas adquirida las licencias de Form Server puedes agregar un nuevo nodo con Form Server a la granja pero con muchas limitaciones:

  • No pueden coexistir en un mismo equipo Office Form Server y MOSS 2007, por lo que tendremos que montarlo en nuevo nodo.
  • Como las licencias de Form Server van por servidor (y CAL de usuario) deberíamos limitar los accesos a la funcionalidad de Infopath services solo a aquellos nodos que tengan adquirida la licencia.
  • No podremos utilizar las funcionalidades de MOSS (publicación, colaboración etc.) en el nodo que tenga instalado Form Server, ya que éste solo tendrá los binarios de Wss3 y puede que no sea capaz de procesar las peticiones a determinados sitios con estas características. Tendremos que limitar los accesos a este servidor solo para los nuevos sitios que utilicen Infopath Forms Services y los usuarios que tengamos CAL de Form server adquirida.


Como resumen, si tenemos MOSS 2007 Standard es mejor pasar a la versión Enterprise de MOSS en lugar de adquirir Office Form Server.

Podéis encontrar más información técnica en:

Microsoft Office Forms Server TechNet TechCenter

Planning and architecture for Office Forms Server 2007

Empezando con Infopath

Infopath es la solución que nos proporciona Microsoft para crear formularios de una forma rápida y ‘sencilla’. Tenemos dos formas de trabajar en modo cliente con los formularios: desde el cliente office de Infopath y desde un entorno web.

La idea de infopath es proporcionar una herramiena para diseñar formularios sin necesidad de realizar un desarrollo a medida o desplegar una gran cantidad de componentes. Infopath además permite manejar datos de entrada procedentes de varios orígenes de datos distintos: de una bbdd, un servicio web, un xml y una lista de sharepoint. Los datos que introducimos en el formulario de infopath se guardan en formato xml o son enviados a un destino configurado en tiempo de diseño.

Sharepoint tiene la capacidad de integrar los formularios infopath de forma ‘nativa’ de dos formas: mediante librerías de formularios y mediante Infopath Form services.

Las librerías de formularios consisten en librerías de documentos con una plantilla de infopath asociada, de forma que la edición de los elementos se maneja mediante el formulario Infopath y los datos manejados se almacenan en formato xml en la librería.

Infopath Form services consiste en un componente instalado en el servidor encargado de renderizar en Html los formularios diseñados en Infopath. Esto evita la necesidad de tener instalado el cliente Infopath para trabajar con los formularios. Para manejar los formularios con Infopath Form services tendremos que subirlos a Sharepoint desde la administración central e indicar en que sitios podremos manejarlos. En el post “MOSS: Publicación de formularios Infopath…el otro camino!” podéis encontrar como publicar un formulario.

Una vez configurados podremos visualizarlos en nuestros sitios con el webpart XmlFormView, si no lo tenéis habilitado podéis consultarlo en “Embedding InfoPath Form in SharePoint Page”.

viernes, 20 de febrero de 2009

SPDisposeCheck con Visual Studio

En el post Using SPDisposeCheck in Visual Studio podemos encontrar un ejemplo de como integrar SPDisposeCheck con Visual Studio, de forma que una vez compilada nuestra librería podáis validarla sin tener que abrir una consola de comandos. Bastante cómodo para los que no nos gusta escribir sobre una pantalla negra.


Para hacerlo, abriremos VStudio > Herramientas > Herramientas externas … sobre el diálogo que nos aparezca indicaremos los datos del exe de SPDisposeCheck y los argumentos de ejecución.

A continuación completaremos los siguientes valores como indica el post original:


Title: SPDispose Check
Command: C:\Program Files\Microsoft\SharePoint Dispose Check\SPDisposeCheck.exe (Ruta completa del exe de SPDisposeCheck )
Arguements: $(TargetName)$(TargetExt)


Initial Directory: $(TargetDir)
Use Output Window: Lo marcamos



Una vez completado, aplicamos y ya tendremos disponible en el menú de herramientas una nueva opción con el título “SPDispose Check”.

Al ejecutar la opción veremos como nos aparece en la venta Output el resultado de SPDisposeCheck.


Espero que os haya servido.

System.IO.IOException device is not ready

Al añadir un nuevo nodo a la granja se producía la excepción “System.IO.IOException: el dispositivo no está listo” o “System.IO.IOException: device is not ready” en el wizard de configuración:

 

Exception: System.IO.IOException: The device is not ready.

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, DirectorySecurity dirSecurity)

at System.IO.DirectoryInfo.Create(DirectorySecurity directorySecurity)

at Microsoft.SharePoint.Administration.SPProvisioningAssistant.CreateDirectory(DirectoryInfo di, Boolean secureAdminAccess)

at Microsoft.SharePoint.Administration.SPServer.CreateDirectory(String path, Boolean secureAdminAccess)

at Microsoft.SharePoint.Administration.SPServer.CreateDirectory(String path)

at Microsoft.SharePoint.Administration.SPUsageSettings.EnsureLogFileDirectories(SPFarm farm)

at Microsoft.SharePoint.Administration.SPWebApplication.Provision()

at Microsoft.SharePoint.Administration.SPWebServiceInstance.Provision()

at Microsoft.SharePoint.Administration.SPFarm.Join()

at Microsoft.SharePoint.PostSetupConfiguration.ConfigurationDatabaseTask.CreateOrConnectConfigDb()

at Microsoft.SharePoint.PostSetupConfiguration.ConfigurationDatabaseTask.Run()

at Microsoft.SharePoint.PostSetupConfiguration.TaskThread.ExecuteTask

 

El problema consiste en que al agregar el nuevo nodo a la granja, sharepoint intenta configurar el nuevo nodo con los paths de logs y análisis de uso configurados en la consola de administración. De manera que si especificamos en la administración central una unidad de disco que no existe en el nuevo nodo lanzará una excepción al no poder crear el directorio.

La solución consiste en especificar un path en los logs y y análisis de uso que dispongan todos los nodos de la granja. En mi caso, tenía configurado que los logs se almacenaran en la unidad “D:\” y el nuevo nodo solo tenía la unidad “C:\”, bastó con indicar la ruta estándar y volver a configurar.

Antes de volver a configurar tuve que desacoplarlo de la granja ya que se queda a medias, y eliminar a mano los sitios web en el iis creados al extender los web applications.

 

La solución original la podéis encontrar en System.IO.IOException Error in the Configuration Wizard.

Espero que os sirva.

miércoles, 18 de febrero de 2009

Error fatal CManagedResources.GetScopeId

Sharepoint nunca te deja de sorprender, cada día me pregunto “¿qué me pasará hoy?”.

Hoy lo hizo con un bonito pantallazo:


El error es nítido!!, resulta que estaba haciendo una aplicación de consola que validaba que estuvieran construidos correctamente una serie de elementos, y para ello utilizaba FullTextSqlQuery para algunas búsquedas. Que sorpresa la mía que al indicar el scope en la consulta me apareció este mensajito.

El problema era que estaba utilizando la dll Microsoft.Sharepoint.Search sobre un MOSS, simplemente cambiando la referencia por la de Microsoft.Office.Server.Search todo se solucionó. En mi código solo tuve que cambiar el using y la llamada al constructor del FullTextSqlQuery.

Espero que os sirva.

[Sharepoint] HRESULT: 0x80040D1B

Este error me ocurría al intentar acceder a la configuración de búsquedas del SSP en MOSS.  Al crear un dominio en mi máquina virtual pasaron todos los usuarios locales a ser usuarios del dominio, por lo que los servicios estaban configurados con la referencia antigua. Aún cambiando las identidades de todos los pool de aplicaciones y los servicios windows me seguía dando el error.

La solución consistió en entrar en la administración central > Operaciones > Servicios del servidor, seleccionar “Office Sharepoint Server Search” y cambiar las credenciales del servicio con un usuario del dominio y un iisreset.

Espero que os sirva.

 

Los errores que me daban eran:

Event Type:    Error
Event Source:    Office SharePoint Server
Event Category:    Servicios compartidos de Office Server
Event ID:    6481
Date:        18/02/2009
Time:        11:27:21
User:        N/A
Computer:    W2K3R2
Description:
No se pudo ejecutar el trabajo del servidor de aplicaciones para la instancia de servicio Microsoft.Office.Server.Search.Administration.SearchServiceInstance (b4b27e04-22fb-4be0-86f3-2dbb418adbc1).

Motivo: Exception from HRESULT: 0x80040D1B

Detalles de soporte técnico:
System.Runtime.InteropServices.COMException (0x80040D1B): Exception from HRESULT: 0x80040D1B
   at Microsoft.Office.Server.Search.Administration.SearchServiceInstance.SynchronizeDefaultContentSource(IDictionary applications)
   at Microsoft.Office.Server.Search.Administration.SearchServiceInstance.Synchronize()
   at Microsoft.Office.Server.Administration.ApplicationServerJob.ProvisionLocalSharedServiceInstances(Boolean isAdministrationServiceJob)

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 

 

Event Type:    Error
Event Source:    Office SharePoint Server
Event Category:    Servicios compartidos de Office Server
Event ID:    6482
Date:        18/02/2009
Time:        11:27:36
User:        N/A
Computer:    W2K3R2
Description:
No se pudo ejecutar el trabajo de administración del servidor de aplicaciones para la instancia de servicio Microsoft.Office.Server.Search.Administration.SearchAdminSharedWebServiceInstance (75449392-d46b-4c1a-9604-8997018cdb3d).

Motivo: El nombre de cuenta especificado no es válido.
Parameter name: account

Detalles de soporte técnico:
System.ArgumentException: El nombre de cuenta especificado no es válido.
Parameter name: account ---> System.Security.Principal.IdentityNotMappedException: Some or all identity references could not be translated.
   at System.Security.Principal.NTAccount.Translate(IdentityReferenceCollection sourceAccounts, Type targetType, Boolean forceSuccess)
   at System.Security.Principal.NTAccount.Translate(Type targetType)
   at Microsoft.Office.Server.Utilities.WindowsSecurity.ValidateAccount(NTAccount account, Boolean throwIfInvalid)
   --- End of inner exception stack trace ---
   at Microsoft.Office.Server.Utilities.WindowsSecurity.ValidateAccount(NTAccount account, Boolean throwIfInvalid)
   at Microsoft.Office.Server.Administration.SharedAccessRule.Validate()
   at Microsoft.Office.Server.Administration.SharedComponentSecurity.SetAccessRule(SharedAccessRule accessRule)
   at Microsoft.Office.Server.Administration.SharedResourceProvider.GetApplicationSecurity()
   at Microsoft.Office.Server.Administration.SharedWebServiceInstance.ProvisionSharedResourceProviderWebConfigSettings(SharedResourceProvider srp)
   at Microsoft.Office.Server.Administration.SharedWebServiceInstance.Synchronize()
   at Microsoft.Office.Server.Administration.ApplicationServerJob.ProvisionLocalSharedServiceInstances(Boolean isAdministrationServiceJob)

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

jueves, 12 de febrero de 2009

[Sharepoint] Introducción al desarrollo

Los últimos meses nos hemos centrado en preparar una serie de post para ayudar a los desarrolladores iniciados en Sahrepoint para darles un empujón y una guía básica.

Os resumo los post más relevantes:

 

¿Por donde empezar?

Quick reference Sharepoint

Referencia con enlaces imprescindibles para empezar a desarrollar en un entorno Sharepoint.

Best Practice en el desarrollo con Sharepoint

Referencias a post con best practices para el desarrollo con WSS3 y MOSS2007.

 

Herramientas

Herramientas para el desarrollo en Sharepoint

Lista con referencias a varias de las herramientas más utilizadas y recomendables para trabajar con Sharepoint.

Introducción a las Visual extension for Sharepoint

Introducción sobre como hacer un proyecto con VSeWSS.

Habilitar intellisense para Xml de definición de Sharepoint

 

Personalización y WebParts

Modificar la apariencia de nuestro sitio

Edición rápida desde Sharepoint Designer

Cómo realizar cambios en las páginas de sharepoint sin desarrollo.

WebPart de contenido con formato

Cómo definir nuestro propio WebPart de contenido con un formato diferente

Page Templates con ayuda del Sharepoint Designer

Más allá del Hola Mundo

Ejemplo de WebPart sin utilizar el Render.write.

Ajax sobre Sharepoint

Cómo montar ajax sobre una aplicación sharepoint.

Implementación de Contentypes

Cómo implementar contentypes en una solución

Capturando eventos en Sharepoint

Opciones del QuickLaunch de Wss3 mediante SPNavitaionProvider

Cómo modificar el Quicklaunch.

Definir Lookup Site Columns desde una Feature

 

Seguridad

Cambiar las credenciales de usuario en Sharepoint

Asignación CAS de Sharepoint por contexto de usuario

 

Administración

Operaciones con Stsadm

Listado con las opciones de stsadm.

Ejecutar de manera inmediata de un job con stsadm

Añadir y desplegar una solución en Sharepoint

Security Provider con ADAM en MOSS y WSS3

MOSS and Hyper-V

Post de Enrique blanco sobre las ventajas de montar MOSS sobre Hyper-V

PowerShell & SharePoint I

Post de Álvaro Arias acerca de como utiliza powershell con Sharepoint.

 

Hasta aquí la temática sobre la “Introducción al desarrollo con Sharepoint”, ahora nos centraremos en temas un poco más avanzados como: Search Server, Infopath, Excel services, etc..

miércoles, 11 de febrero de 2009

Best Practice en el desarrollo con Sharepoint

Hay una cosa que tenemos que tener clara y es que Sharepoint no deja de ser un producto más o menos cerrado sobre el que podemos hacer nuestras adaptaciones y personalizaciones. Y como cualquier producto debemos conocer tanto sus funcionalidades como sus entrañas para poder hacer soluciones lo más estables posibles. Programar con el modelo de objetos de Sharepoint no es cómo programar una aplicación de asp.net corriente, tiene sus “cositas” que hacen que te tires horas delante de tu máquina virtual para hacer algo que para el usuario es una tontería.

Después de este rollo, lo que quiero dejaros hoy es una serie de enlaces sobre “mejores prácticas de programación” sobre Sharepoint, os aconsejo que le dediquéis en algún momento media hora a revisar este tipo de cosas.

 

Recursos

Best Practices Resource Center for SharePoint Server 2007

Centro de recursos de Microsoft para las B&P sobre Sharepoint.

Microsoft Best Practices Analyzer for WSS3 and MOSS2007

Herramienta que analiza nuestro granaja y compone una serie de informes acerca de como mejorar el rendimiento respecto a la estructura y configuración.

General

Best Practices: Common Coding Issues When Using the SharePoint Object Model

Documento con algunas recomendaciones en el desarrollo.

Best practices

Contiene varios enlaces a artículos en technet acerca de funcionalidades de MOSS.

Best practices for developing accessible Web sites (white paper)

B&P sobre accesibilidad en MOSS.

InfoPath Forms Services best practices

Algunas recomendaciones o comentarios acerca de Infopath.

ForeFront en Sharepoint

¿Pero alguien lo ha instalado?

Liberación de recursos

Best Practices: Using Disposable Windows SharePoint Services Objects

B&P sobre como liberar correctamente los objetos de Sharepoint.

SharePoint 2007 and WSS 3.0 Dispose Patterns by Example

Post con ejemplos acerca de la liberación. Tiene ejemplos que incluso no hablan en la referencia de MSDN.

SPDisposeCheck

Posta de Álvaro Arias acerca de la herramienta SPDisposeCheck para detectar posibles errores de liberación.

Libros

Microsoft Office SharePoint Server 2007 Best Practices

miércoles, 4 de febrero de 2009

Cambiar las credenciales de usuario en Sharepoint

En ocasiones puede plantearse el caso en el que necesitemos manejar los objetos de Sharepoint pero con una identidad distinta a la del usuario logado o el del administrador.

Dentro del contexto de Sharepoint disponemos de varios métodos para cambiar las credenciales:

RunWithElevatedPrivileges

Requiere de Impersonate=true

SPSecurity.RunWithElevatedPrivileges( delegate() {

….

} );

 

WindowsImpersonationContext

Nos permite ejecutar nuestro código con las credenciales de otro usuario. En este ejemplo con el usuario configurado en el pool de aplicaciones:

using (WindowsImpersonationContext wic = WindowsIdentity.Impersonate(IntPtr.Zero))

{

………….

}

 

SHAREPOINT\\system

Consiste en capturar el Token del usuario “system” para conectar de nuevo con ese token.

El usuario “SHAREPOINT\\system” es un usuario especial, utilizaremos este caso cuando nos encontremos en un EventReceiver ya que no podremos utilizar RunWithElevatedPrivileges.

using (SPWeb webOrigUser = properties.OpenWeb())
{
    SPUserToken token = webOrigUser.AllUsers["SHAREPOINT\\system"].UserToken;
    using (SPSite site = new SPSite(properties.SiteId, token))
    {
        using (SPWeb currentWeb = site.OpenWeb(properties.RelativeWebUrl))
        {
            try
            {
                DisableEventFiring();

                SPList sourceList = currentWeb.Lists[properties.ListId];
                SPListItem itemAdded = sourceList.GetItemById(properties.ListItem.ID);
                ……

            }
            catch (Exception ex1)
            {
                properties.ErrorMessage = ex1.Message;
                properties.Status = SPEventReceiverStatus.CancelWithError;
            }
            finally
            {
                EnableEventFiring();
            }

        }
    }
}

 

Fuera del contexto de Sharepoint

Para poder ejecutar el modelo de objetos de Sharepoint fuera del contexto de sharepoint, es decir en un proceso distinto, ya sea un servicio web en otro workerprocess o una aplicación de consola.

En este caso nuestro código se ejecutará con las credenciales del usuario que maneje el proceso.

Este caso lo utilizaremos cuando tengamos que hacer integraciones con sistemas que no pueden migrarse o aplicaciones para migrar contenido.

Para utilizar este método el usuario con el que hagamos logon además de tener acceso al site debe tener el privilegio “Actuar como parte del sistema operativo”, en mi caso le he dado permisos de administrador de la máquina directamente. Si no lo hacemos nos permitirá conectar pero nos dará una excepción al acceder a los elementos de las listas.

Podéis consultar más ejemplos: http://support.microsoft.com/kb/306158/es

En mi caso he creado una aplicación de consola que muestra el contenidos de una lista:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.SharePoint;

namespace ConsoleApplication1
{
    class Program
    {

        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_NETWORK = 3;

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(String lpszUsername, String
        lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private extern static bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

 

        static void Main(string[] args)
        {
            WindowsImpersonationContext context = impersonateValidUser("miusuario", "midominio", "password");
            try
            {
                using (SPSite site = new SPSite("http://localhost/))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        Console.WriteLine(web.Lists["milista"].Title);
                        foreach (SPListItem item in web.Lists["milista"].Items)
                            Console.WriteLine(item.Title);
                    }
                }
            }
            finally
            {
                context.Undo();
            }

            Console.ReadLine();
        }

        private static WindowsImpersonationContext impersonateValidUser(String userName, String domain, String password)
        {
            WindowsIdentity tempWindowsIdentity;
            WindowsImpersonationContext impersonationContext;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                if (LogonUser(userName, domain, password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref token))
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return impersonationContext;;
                        }
                    }
                }
            }
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);
            return null;
        }

    }
}

martes, 3 de febrero de 2009

Qué pasa con los foros de Sharepoint en MSDN y TechNet?

Recientemente Microsoft ha actualizado la plataforma de sus foros de MSDN y Technet a la versión 3 “http://social.technet.microsoft.com/Forums/en-US/categories/

Los contenidos de todos los foros de la antigua plataforma ya se han migrado a la nueva, el problema viene con el foro de sharepoint en español. Resulta que si intentas acceder al foro de sharepoint en español para crear un nuevo mensaje o ver la lista de hilos nos lleva a una página de error no encontrada “404”. La ruta raiz del foro debería ser “http://social.technet.microsoft.com/Forums/es-ES/mosses/threads/”, de echo los hilos ya creados aparecen como “http://social.technet.microsoft.com/forums/es-ES/mosses/thread/{GUID}/”.

Si queremos agregar un nuevo hilo en español podremos hacerlo, pero tendrá que ser en foros en inglés, lo cual no creo que resulte muy útil.

Me da la sensación que todavía no han terminado con la migración así que tocará esperar un tiempo. Al menos podremos seguir consultando el contenido antiguo desde la opción de búsqueda del foro o google.

lunes, 2 de febrero de 2009

Más allá del Hola Mundo

Vamos a ver si conseguimos ampliar la documentación acerca de como crear un WebPart que no sea el típico “Hola Mundo”. En el mundo real nos vamos encontrar con multitud de situaciones en las que los ejemplos se quedan cortos.

Continuando con la temática de introducción veremos como crear un WebPart de forma “sencilla” sin utilizar el Response.write.

Crearemos un WebPart que nos permita buscar sobre una lista basada en el contentype comentado en el anterior post “Implementación de Contentypes”.

Podéis descargaros el ejemplo completo en: http://blogs.renacimiento.com/mcortes/Documentos/WPVistaEmpleado.zip

 

Empezaremos creando un proyecto del tipo WebPart con las extensiones “VseWSS”.

Para mostrar la parte de interfaz en lugar de utilizar el Response.write cargaremos en tiempo de ejecución un control de usuario mediante el uso de “LoadControl”. Con el control de usuario podremos diseñar nuestra interfaz de forma sencilla con una vista típica de un fichero ascx.

Cargaremos el control desde la función “CreateChildControls” del Webpaprt. En la función LoadControl tendremos que indicarle la ruta donde del fichero ascx, utilizaremos el directorio “CONTROLTEMPLATES” de sharepoint para alojar nuestro fichero. En este directorio podremos almacenar controles de usuario ascx sin tener que registrarlos como “safemode” en el webconfig. Además podremos acceder desde la ruta virtual "_controltemplates”.

 

public class WebPart1 : System.Web.UI.WebControls.WebParts.WebPart
    {
        public WebPart1()
        {
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            string urlUserControl = @"/_controltemplates/UCVistaEmpleado.ascx";
            Control userControl = this.Page.LoadControl(urlUserControl);
            userControl.ID = "UCVistaEmpleado";

            this.Controls.Add(userControl);
        }
    }

 

Añadiremos entonces a nuestro proyecto un elemento del tipo “Template” y sobre el crearemos la subcarpeta “CONTROLTEMPLATES” y agregaremos un fichero con la extensión ascx y escribiremos la interfaz que deseemos. Para darle funcionalidad a este control tendremos que indicarle al fichero ascx la clase codebehind que la manejará.

Añadiremos una clase que herede de “UserControl” e indicaremos esta clase en el atributo “Inherits” de la directiva “Page”.

Para poder utilizar controles o elementos de sharepoint también registraremos la dll Microsoft.SharePoint y aquellas que necesitemos.

 

<%@ Control Language="C#" Inherits="WPVistaEmpleado.UCVistaEmpleado, WPVistaEmpleado, Version=1.0.0.0,Culture=neutral,PublicKeyToken=37ea3d63d16eb294" compilationMode="Auto" %>
<%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

 

Para conocer el publicKeytoken sin tener que desplegar el webpart utilizaremos la opción “Package” y consultaremos el publicKeytoken generado en el fichero webpart en el directorio bin.

Ahora agregaremos un SPGridView para mostrar los empleados disponibles y una serie de controles de filtro.

Para manejar los controles desde la clase manejadora definiremos las variables necesarias y las inicializaremos en el evento OnInit.

 

protected SPGridView ListaDeEmpleados;
protected TextBox txtNumeroEmpleado;
protected Button btnBuscar;

protected override void OnInit(EventArgs e)
{
            base.OnInit(e);

            txtNumeroEmpleado = (TextBox)this.TemplateControl.FindControl("txtNumeroEmpleado");
            btnBuscar = (Button)this.TemplateControl.FindControl("btnBuscar");
            btnBuscar.Click += new EventHandler(btnBuscar_Click);
            ListaDeEmpleados = (SPGridView)this.TemplateControl.FindControl("ListaDeEmpleados");
}

 

Fijaros que para capturar los eventos los hemos registrado por código ya que el LoadControl no lo hará por nosotros. Si queremos ahorrarnos este proceso podremos plantearnos introducir nuestro control en un UpdatePanel de Ajax de manera que sea éste el que enganche los eventos definidos en el ascx con nuestra clase.

 

Una vez hemos terminado nuestro control, solo tenemos que desplegarlo y agregarlo en nuestra página de contenidos.

 

Podéis descargaros el ejemplo completo en: http://blogs.renacimiento.com/mcortes/Documentos/WPVistaEmpleado.zip

domingo, 1 de febrero de 2009

Capturando eventos en Sharepoint

Siguiendo con la temática acerca de dar una introducción al desarrollo en Sharepoint, veremos los mecanismos que disponemos para capturar algunos de lo eventos producidos en los elementos de sharpeoint.

Los desarrolladores disponemos de los Event Receivers, que consisten en artefactos configurados para capturar eventos acera de modificaciones producidas en listas (o bibliotecas), elementos de lista y features.

Todos los event receivers deben heredar de una clase base y sobreescribir los oventos a capturar. Para registrar estos eventos se tendrá que realizar o bien por programación o bien mediante una feature. Aunque podremos utilizar herramientas como “Event handler explorer” que nos permitirá asociar nuestros event receiver’s sobre librerías o documentos ya creadas sin tener que desplegar features ni escribir código.

SPItemEventReceiver

Será la clase base que utilizaremos para capturar los eventos relacionados con modificaciones sobre los elementos de una lista o biblioteca. Si utilizamos las extensiones VSEWSS podremos añadir un elemento del tipo “Event Receiver” a nuestro proyecto, esto nos creará los ficheros y definiciones para sobreescribir los eventos que necesitemos.

Para capturar los eventos sobreescribiremos los métodos que nos proporciona esta clase:

public virtual void ContextEvent(SPItemEventProperties properties);
public virtual void ItemAdded(SPItemEventProperties properties);
public virtual void ItemAdding(SPItemEventProperties properties);
public virtual void ItemAttachmentAdded(SPItemEventProperties properties);
public virtual void ItemAttachmentAdding(SPItemEventProperties properties);
public virtual void ItemAttachmentDeleted(SPItemEventProperties properties);
public virtual void ItemAttachmentDeleting(SPItemEventProperties properties);
public virtual void ItemCheckedIn(SPItemEventProperties properties);
public virtual void ItemCheckedOut(SPItemEventProperties properties);
public virtual void ItemCheckingIn(SPItemEventProperties properties);
public virtual void ItemCheckingOut(SPItemEventProperties properties);
public virtual void ItemDeleted(SPItemEventProperties properties);
public virtual void ItemDeleting(SPItemEventProperties properties);
public virtual void ItemFileConverted(SPItemEventProperties properties);
public virtual void ItemFileMoved(SPItemEventProperties properties);
public virtual void ItemFileMoving(SPItemEventProperties properties);
public virtual void ItemUncheckedOut(SPItemEventProperties properties);
public virtual void ItemUncheckingOut(SPItemEventProperties properties);
public virtual void ItemUpdated(SPItemEventProperties properties);
public virtual void ItemUpdating(SPItemEventProperties properties);

Los tres métodos básicos que utilizaremos serán: ItemAdding, ItemUpdating, ItemDeleting 

ItemAdding: Se llama antes de que el elemento se añada a la lista. Lo utilizaremos para validar la entrada. Aunque lo recomendable es utilizar Workflows para manejar la funcionalidad, es preferible manejar este evento para impedir entradas de datos no deseadas.

ItemUpdating: Lo utilizaremos para controlar los cambios producidos sobre un elemento. Podremos impedir el cambio de determinadas columnas con el uso de las propiedades BeginProperties y AfterProperties.

ItemDeleting: Podremos impedir el borrado de los elementos que no cumplan con las especificaciones funcionales.

 

En el siguiente ejemplo veremos como manejar los eventos sobre una lista con el contentype “Empleado” de ejemplo utilizado en el post “Implementación de Contentypes”:

public class EventReceiver1ItemEventReceiver : SPItemEventReceiver
{

        public override void ItemAdding(SPItemEventProperties properties)
        {
            using (SPWeb currentWeb = properties.OpenWeb())
            {
                string numeroEmpeladoInternalName = currentWeb.Lists[properties.ListId].Fields["Numero Empleado"].InternalName;
                if (!string.IsNullOrEmpty((string)properties.AfterProperties[numeroEmpeladoInternalName]))
                {
                    properties.ErrorMessage = "El código de empleado es autonumérico";
                    properties.Status = SPEventReceiverStatus.CancelWithError;
                }
            }
        }

        public override void  ItemAdded(SPItemEventProperties properties)
        {
            DisableEventFiring();
            try{
                properties.ListItem["Numero Empleado"] = DateTime.Now.ToString("yyyyMMddhhmmss");
                properties.ListItem.Update();
            }
            finally
            {
                EnableEventFiring();
            }
        }   

        public override void ItemUpdating(SPItemEventProperties properties)
        {
            string numeroEmpeladoInternalName = properties.ListItem.ParentList.Fields["Numero Empleado"].InternalName;

            if(string.IsNullOrEmpty((string)properties.AfterProperties[numeroEmpeladoInternalName]))
            {
                properties.ErrorMessage = "El código de empleado no es editable";
                properties.Status = SPEventReceiverStatus.CancelWithError;
            }

            if (properties.ListItem["Numero Empleado"].ToString() != properties.AfterProperties[numeroEmpeladoInternalName].ToString())
            {
                properties.ErrorMessage = "El código de empleado no es editable";
                properties.Status = SPEventReceiverStatus.CancelWithError;
            }
        }

        public override void ItemDeleting(SPItemEventProperties properties)
        {
            properties.ErrorMessage = "No se pueden eliminar empleados de la lista";
            properties.Status = SPEventReceiverStatus.CancelWithError;
        }  
    }

 

Destacamos del ejemplo al menos dos cosas:

  • Para acceder a las propiedades de la lista hemos tenido que utilizar el método “OpenWeb” de SPItemEventProperties, ya que aunque podríamos haber utilizado properties.ListItem.ParentList , en el evento ItemAdding la propiedad  es null ya que todavía no se ha creado el elemento.
  • Para cancelar el evento utilizaremos la propiedad “Status” y para mostrar un mensaje con el motivo “ErrorMessage”.
  • Para utilizar las propiedad AfterProperties tendremos que utilizar el “internal name” de la columna en la lista !!!, suele ser un error habitual el utilizar el display name.
  • El método DiableEventFiring para desactivar el event receiver al modificar el elemento. Para volverlo a activar hemos utilizado EnableEventFiring dentro de un bloque try-finally.

 

SPListEventReceiver

Podremos capturar los eventos producidos al realizar modificaciones sobre la estructura de una instancia de una lista o biblioteca. Es decir, podremos manejar los eventos producidos al añadir una nueva columna, …

Los métodos que podremos sobreescribir corresponden con:

public virtual void FieldAdded(SPListEventProperties properties);
public virtual void FieldAdding(SPListEventProperties properties);
public virtual void FieldDeleted(SPListEventProperties properties);
public virtual void FieldDeleting(SPListEventProperties properties);
public virtual void FieldUpdated(SPListEventProperties properties);
public virtual void FieldUpdating(SPListEventProperties properties);

 

Este tipo de eventos los utilizaremos sobre todo con CustomFields que necesiten de refrescar su definición al asignarse a una lista.

 

SPFeatureReceiver

Con esta clase capturaremos los eventos producidos sobre una feature. Es muy útil cuando queremos provisionar el site donde se haya activa la feature.

public abstract void FeatureActivated(SPFeatureReceiverProperties properties);
public abstract void FeatureDeactivating(SPFeatureReceiverProperties properties);
public abstract void FeatureInstalled(SPFeatureReceiverProperties properties);
public abstract void FeatureUninstalling(SPFeatureReceiverProperties properties);

 

Por ejemplo:

 

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
            try
            {
                SPWeb web = (SPWeb)(properties.Feature.Parent);

                // Provisión de seguridad
                ProvisionSecurity(web);

                // Provisión de formularios a la lista
                ProvisionForms(web);

                // Provisión de navegación
                UpdateQuickLaunch(web);

                // Enlaza el WF con la lista
                ProvisionWorkflow(web);
            }
            catch { }
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
            try
            {
                SPWeb web = (properties.Feature.Parent as SPSite).RootWeb;

                // Provisión de navegación
                DeleteQuickLaunch(web);
            }
            catch { }
}