Tutorial: Control de eventos de bibliotecas de documentos

Windows SharePoint Services proporciona eventos de la biblioteca de documentos que permiten a los programadores construir sobre la plataforma de SharePoint. Los programadores pueden crear ensamblados de código administrado que definen controladores para estos eventos y, a continuación, enlazar los controladores a bibliotecas de documentos. Los controladores de eventos pueden llamar al modelo de objetos de SharePoint para obtener acceso a las bases de datos de configuración y contenido directamente, o pueden invocar servicios externos, usando Windows SharePoint Services como interfaz de usuario para otros sistemas.

Nota

En este tutorial se describe un procedimiento para que el control de eventos de Windows SharePoint Services 2.0 sea compatible con versiones anteriores.

Requisitos previos

En la tabla siguiente se describen los eventos para bibliotecas de documentos proporcionados por Windows SharePoint Services.

Evento Descripción

Deshacer desprotección

Se deshacen los cambios realizados en un documento desprotegido.

Proteger

Se protege un documento en la biblioteca.

Desproteger

Se desprotege un documento de la biblioteca.

Copiar

Se copia un documento en la biblioteca.

Eliminar

Se elimina un documento de la biblioteca.

Insertar

Se guarda un nuevo documento en la biblioteca.

Mover o Cambiar nombre

Se mueve un documento o se cambia su nombre.

Actualizar

Se edita un documento existente o el valor de una columna personalizada en la biblioteca.

En el contexto de Windows SharePoint Services, un controlador de eventos es una clase de Microsoft .NET que implementa la interfaz de IListEventSink, cuyo único método, OnEvent, se usa dentro del controlador. El objeto SPListEvent contiene información acerca de un suceso que se produce y es posible devolver el tipo de evento mediante la propiedad Type. Use la propiedad Site para tener acceso al modelo de objetos del espacio de nombre Microsoft.SharePoint dentro del controlador.

Debe instalar el ensamblado administrado que define un controlador de eventos en la memoria caché de ensamblados global. En una configuración de granja de servidores, debe instalar este ensamblado administrado en la memoria caché de ensamblados global de cada servidor cliente web.

Para implementar un controlador de eventos en un servidor, el control de eventos debe habilitarse en la página Configuración general del servidor virtual en la Administración central de SharePoint.

Sólo los usuarios con permisos completos para una biblioteca de documentos pueden agregar controladores de eventos.

Configuración de eventos

Los metadatos de lista de una biblioteca de documentos enlazan la clase de un controlador de eventos con una biblioteca por medio de las siguientes propiedades. Los metadatos se pueden establecer en la página Configuración avanzada de biblioteca de documentos de la biblioteca de documentos mediante código que implementa las propiedades EventSinkAssembly, EventSinkClass y EventSinkData de la clase SPList, o a través de definiciones incluidas en plantillas de sitio cliente. En la siguiente tabla se resumen los parámetros de configuración de eventos.

Opción de configuración Tipo Descripción

Assembly Name

String

Nombre seguro del archivo de ensamblado de controlador de eventos que se encuentra en la memoria caché de ensamblados global.

Class Name

String

Nombre completo, con distinción entre mayúsculas y minúsculas, de la clase dentro del ensamblado.

Properties

String

Cadena arbitraria de propiedades personalizadas para uso del controlador de eventos. La longitud de la cadena no puede superar los 255 caracteres.

Después de instalar el ensamblado de controlador de eventos, en la página Configuración avanzada de biblioteca de documentos de la biblioteca de documentos, especifique el nombre seguro del ensamblado en el cuadro Nombre del ensamblado. El nombre seguro debe tener el formato nombre_de_ensamblado, Version=versión, Culture=referencia_cultural, PublicKeyToken=token_de_clave_pública. Puede obtener estos valores si se desplaza hasta %windir%\assembly en el Explorador de Windows. Como ejemplo, el nombre seguro para el ensamblado Microsoft.SharePoint es similar a Microsoft.SharePoint, Version=12.0.0.0, Culture=Neutral, PublicKeyToken=71e9bce111e9429c.

El valor que se especifique en el cuadro Nombre de clase debe ser el nombre completo, con distinción entre mayúsculas y minúsculas, de una clase definida en el ensamblado específico que implementa la interfaz de IListEventSink. Por ejemplo, el nombre completo de la clase en el ejemplo siguiente sería myNamespace.myClass.

Namespace myNamespace

    Public Class myClass

        Inherits Microsoft.SharePoint.IListEventSink

        Public Sub OnEvent(evt As Microsoft.SharePoint.SPListEvent)

            'Add an announcement
            AddAnnouncement(evt)

        End Sub 'OnEvent

        Public Sub AddAnnouncement(evt As 
           Microsoft.SharePoint.SPListEvent)
        .
        .
        .
        End Sub 'AddAnnouncement

    End Class 'myClass

End Namespace 'myNamespace
namespace myNamespace
{
    public class myClass : Microsoft.SharePoint.IListEventSink
    {
        public void OnEvent(Microsoft.SharePoint.SPListEvent evt)
        {
            //Add an announcement
            AddAnnouncement(evt);
        }

        public void AddAnnouncement
           (Microsoft.SharePoint.SPListEvent evt)
        {
            .
            .
            .
        }
    }
}

Escribir un valor en el cuadro Propiedades es opcional. Este valor puede contener hasta 255 caracteres para uso del controlador de eventos.

Eventos en relación con otros eventos

Un controlador de eventos que se ejecuta en el contexto de Windows SharePoint Services no puede generar otros eventos. Para programar efectos secundarios para un evento, debe proporcionar el código dentro del controlador de eventos. Se llama al controlador de eventos de forma asincrónica desde la solicitud real.

Se crea una instancia nueva de una clase de controlador de eventos para cada evento. Por consiguiente, no se puede almacenar el estado en la clase de controlador de eventos y esperar que se mantenga entre distintos eventos. Sin embargo, se puede crear una clase base para un controlador de eventos o para varios controladores de eventos que almacene en caché el estado, de forma que el mismo objeto receptor pueda usarse para todos los eventos en una biblioteca de documentos.

Almacenamiento en caché de credenciales

Los controladores de eventos se ejecutan en el contexto de la identidad del grupo de aplicaciones de Internet Information Services (IIS), una cuenta que normalmente no tiene ningún permiso en Windows SharePoint Services. Si el controlador necesita interactuar con Windows SharePoint Services a través del modelo de objetos, se deben establecer y almacenar en la memoria caché las credenciales a través de la suplantación de un usuario. No se admite el acceso al modelo de objetos en un controlador de eventos sin suplantación. Para obtener información, consulte Impersonating and Reverting de la Guía del programador de .NET Framework en MSDN.

Contexto de seguridad

El subproceso del controlador de eventos se ejecuta dentro la misma cuenta de agrupación de aplicaciones que la aplicación web de SharePoint. Sin embargo, si el controlador de eventos debe ejecutarse en una cuenta diferente para manipular Windows SharePoint Services o un servicio externo, el programador debe mantener sus propias credenciales y administrar su propio contexto de seguridad.

Excepciones y errores

Se producen excepciones para los controladores de eventos usados en Windows SharePoint Services en las condiciones siguientes, para las que se crea una entrada del registro de sucesos de Microsoft Windows NT:

  • No se puede cargar el ensamblado para el controlador.

  • No se encuentra la clase que define el controlador.

  • La clase no implementa la interfaz de IlistEventSink.

  • El método OnEvent genera una excepción.

Nota

Cuando se reinicia IIS, cada proceso de trabajo de IIS finaliza sólo después de que todos sus elementos de trabajos en cola, incluidos las solicitudes HTTP y los eventos de biblioteca de documentos, completan sus tareas.

Definiciones y plantillas

Los programadores pueden definir ensamblados de controlador de eventos dentro de definiciones de lista, lo que proporciona un medio útil para implementar soluciones, como bibliotecas de documentos habilitadas por flujo de trabajo en las que los eventos ya están enlazados con los controladores adecuados. Un controlador de eventos se puede enlazar a una biblioteca de documentos mediante los atributos EventSinkAssembly, EventSinkClass y EventSinkData del elemento List en el archivo Schema.xml para el tipo de lista. Cuando un usuario final crea una biblioteca mediante la interfaz de usuario (UI) a partir de una definición de lista que especifica un controlador de eventos, los parámetros de evento de la lista se establecen automáticamente en los valores especificados en la definición. Cuando un usuario final guarda una biblioteca de documentos como una plantilla de lista, los parámetros de eventos especificados a través de la UI se conservan en la plantilla.

Para obtener información acerca de las definiciones y las plantillas para sitios o listas, consulte Trabajar con plantillas y definiciones.

Ejemplo de controlador de eventos

La siguiente tarea de programación crea un controlador de eventos que elimina la versión más antigua de un archivo en una biblioteca de documentos cuando el número de versiones para el archivo alcanza un número especificado. Para establecer credenciales para el controlador, un método devuelve un token al controlador de eventos para la suplantación de un usuario.

Procedimiento

Para crear un controlador de eventos de biblioteca de documentos

  1. En el menú Archivo de Visual Studio 2005, elija Nuevo y, a continuación, haga clic en Proyecto.

  2. En el cuadro de diálogo Nuevo proyecto, seleccione Proyectos de Visual Basic o Proyectos de Visual C# en el cuadro Tipos de proyecto.

  3. En el cuadro Plantillas, seleccione Biblioteca de clases.

  4. En el cuadro Nombre, escriba el nombre del proyecto, que en esta tarea es "DocLibHandler".

  5. En el cuadro Ubicación, escriba la ruta de acceso para el proyecto y, a continuación, haga clic en Aceptar.

  6. En el Explorador de soluciones, haga clic con el botón secundario del mouse en el nodo Referencias y haga clic en Agregar referencia en el menú contextual.

  7. En la ficha .NET del cuadro de diálogo Agregar referencia, seleccione Windows SharePoint Services en la lista de componentes, haga clic en Seleccionar y, a continuación, haga clic en Aceptar.

  8. Agregue las directivas siguientes en el archivo Class1.vb o Class1.cs del proyecto DocLibHandler después de la directiva System que se incluye de forma predeterminada.

    Imports System.Security.Principal
    Imports System.Runtime.InteropServices
    Imports Microsoft.SharePoint
    
    using System.Security.Principal;
    using System.Runtime.InteropServices;
    using Microsoft.SharePoint;
    
  9. Cambie el espacio de nombres y el nombre de clase en el archivo Class1.vb o Class1.cs para que sea DocLibEventHandler y DocVersionHandler, de la manera siguiente.

    Namespace DocLibEventHandler
    
       Public Class DocVersionHandler
          Implements IlistEventSink
    
    namespace DocLibEventHandler
    {
        public class DocVersionHandler : IListEventSink
        {
    
  10. Agregue el método OnEvent de la interfaz de IListEventSink a la clase DocVersionHandler, de la siguiente manera.

    Private maxVersions As Integer = 5
    
    Sub IListEventSink.OnEvent(listEvent 
        As Microsoft.SharePoint.SPListEvent)
    
        ' TODO: ***** PROVIDE YOUR IMPLEMENTATION HERE ****
        ' Get the credentials (User_Alias, Domain, Password) that this 
        ' event sink instance should run as and initialize the wic 
        ' object by calling the CreateIdentity method
    
        Dim wic As WindowsImpersonationContext 
            = CreateIdentity(User_Alias, Domain, Password).Impersonate()
    
        If listEvent.Type = SPListEventType.Update OrElse listEvent.Type 
            = SPListEventType.CheckIn Then
    
            Dim site As SPWeb = listEvent.Site.OpenWeb()
            Dim file As SPFile = site.GetFile(listEvent.UrlAfter)
            Dim nDocVersions As Integer = file.Versions.Count
    
            While maxVersions > 0 AndAlso nDocVersions > maxVersions - 1
    
                file.Versions.Delete(0)
                nDocVersions = file.Versions.Count
    
            End While
    
        End If
    
        wic.Undo()
    
    End Sub 'IListEventSink.OnEvent
    
    private int maxVersions = 5;
    
    void IListEventSink.OnEvent
        (Microsoft.SharePoint.SPListEvent listEvent)
    {
        /* TODO: ***** PROVIDE YOUR IMPLEMENTATION HERE ****
        Get the credentials (User_Alias, Domain, Password) that this 
        event sink instance should run as and initialize the wic object 
        by calling the CreateIdentity method*/
    
        WindowsImpersonationContext wic 
           = CreateIdentity(User_Alias, Domain, Password).Impersonate();
    
        if ((listEvent.Type == SPListEventType.Update) ||
            (listEvent.Type == SPListEventType.CheckIn))
        {
            SPWeb site = listEvent.Site.OpenWeb();
            SPFile file = site.GetFile(listEvent.UrlAfter);
            int nDocVersions = file.Versions.Count;
    
            while ((maxVersions > 0) && (nDocVersions 
                > (maxVersions - 1)))
            {
                file.Versions.Delete(0);
                nDocVersions = file.Versions.Count;
            }
        }
    
        wic.Undo();
    }
    
  11. El método OnEvent llama al método siguiente para suplantar una cuenta con permisos administrativos.

    Importante

    No pase credenciales en texto no cifrado, porque conllevaría un riesgo de seguridad. Para suplantar a un usuario de forma más segura, puede devolver la información mediante programación con funciones proporcionadas por el sistema operativo para obtener los datos secretos del usuario, como las funciones CryptProtectData y CryptUnprotectData de la API de protección de datos (DPAPI).

    Incluya el siguiente método dentro de la clase DocVersionHandler.

    Protected Shared Function CreateIdentity(User As String, Domain 
        As String, Password As String) As WindowsIdentity
    
        ' The Windows NT user token.
        Dim tokenHandle As New IntPtr(0)
    
        Const LOGON32_PROVIDER_DEFAULT As Integer = 0
        Const LOGON32_LOGON_NETWORK As Integer = 3
    
        tokenHandle = IntPtr.Zero
    
        ' Call LogonUser to obtain a handle to an access token.
        Dim returnValue As Boolean = LogonUser(User, Domain, Password, 
           LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, tokenHandle)
    
        If False = returnValue Then
    
            Dim ret As Integer = Marshal.GetLastWin32Error()
            Throw New Exception("LogonUser failed with 
                error code: " + ret)
    
        End If
    
        System.Diagnostics.Debug.WriteLine(("Created user token: 
            " + tokenHandle))
    
        'The WindowsIdentity class makes a new copy of the token.
        'It also handles calling CloseHandle for the copy.
        Dim id As New WindowsIdentity(tokenHandle)
        CloseHandle(tokenHandle)
        Return id
    
    End Function 'CreateIdentity
    <DllImport("advapi32.dll", SetLastError := True)>  _
        Private Shared Function LogonUser(lpszUsername As String, 
                lpszDomain As String, _
            lpszPassword As String, dwLogonType As Integer, 
                dwLogonProvider As Integer, _
            ByRef phToken As IntPtr) As Boolean
    
    <DllImport("kernel32.dll", CharSet := CharSet.Auto)>  _
        Private Shared Declare Auto Function CloseHandle Lib 
            "kernel32.dll" (handle As IntPtr) As Boolean
    
    protected static WindowsIdentity CreateIdentity(string User, 
        string Domain, string Password)
    {
        // The Windows NT user token.
        IntPtr tokenHandle = new IntPtr(0);
    
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_NETWORK = 3;
    
        tokenHandle = IntPtr.Zero;
    
        // Call LogonUser to obtain a handle to an access token.
        bool returnValue = LogonUser(User, Domain, Password, 
        LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
        ref tokenHandle);
    
        if (false == returnValue)
        {
            int ret = Marshal.GetLastWin32Error();
            throw new Exception("LogonUser failed with 
                error code: " +  ret);
        }
    
        System.Diagnostics.Debug.WriteLine("Created user token: 
            " + tokenHandle);
    
        //The WindowsIdentity class makes a new copy of the token.
        //It also handles calling CloseHandle for the copy.
        WindowsIdentity id = new WindowsIdentity(tokenHandle);
        CloseHandle(tokenHandle);
        return id;
    }
    
    [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);
    
  12. Para crear un nombre seguro para el ensamblado, vaya al directorio unidad_local:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin (unidad_local:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin en Visual Studio .NET versión 7) a través de un símbolo del sistema y escriba sn -k unidad_local:\DocLibHandler.snk, que crea un archivo de clave para el ensamblado.

  13. Agregue los siguientes atributos para el archivo de clave y la versión de ensamblado en el archivo AssemblyInfo del proyecto y reemplace los atributos proporcionados de forma predeterminada.

    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyKeyFile("Local_Drive:\\DocLibHandler.snk")]
    
  14. En el menú Generar, haga clic en Generar solución.

  15. Para instalar la solución en la memoria caché de ensamblados global, arrastre la biblioteca de vínculos dinámicos (DLL) del ensamblado hasta el directorio unidad_local:\WINDOWS\assembly en el Explorador de Windows.

  16. En la página Configuración general del servidor virtual en la Administración central de SharePoint, seleccione Activado en la sección Controladores de eventos para habilitar uso de controladores de eventos en el servidor virtual.

  17. Desde una vista de la biblioteca de documentos a la que desee adjuntar el ensamblado de controlador de eventos, haga clic en Modificar configuración y columnas y, a continuación, haga clic en Cambiar configuración avanzada en la página para personalizar la biblioteca de documentos.

  18. En la página Configuración avanzada de biblioteca de documentos, escriba el nombre seguro para el ensamblado en el cuadro Nombre del ensamblado (por ejemplo, DocLibHandler, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=0fafac2a0cc92888). Para obtener los valores usados en el nombre seguro, haga clic con el botón secundario del mouse en el archivo DLL del ensamblado en el directorio unidad_local:\WINDOWS\assembly y, a continuación, haga clic en Propiedades en el menú contextual.

  19. Escriba el nombre completo de la clase, con distinción entre mayúsculas y minúsculas, en el cuadro Nombre de clase, que en este ejemplo es DocLibEventHandler.DocVersionHandler.

  20. Escriba iisreset en una línea de comandos para restablecer IIS.

Pasos siguientes

Para implementar el controlador en una biblioteca de documentos, el control de versiones debe habilitarse en la página Configuración de biblioteca de documentos de la biblioteca.