Welcome to MSDN Blogs Sign in | Join | Help

Recycle Bin

With v2 of Sharepoint, restoring deleted documents was a much sought after elixir. And to that effect were made a number of attempts at coaxing the product to allow us to retain copies of the deleted items. Once such simplistic approach was documented and well presented by Maxin V Karpov and Eric Schoonover in the February 2005 issue of MSDN. The online version can be found here.

Recently me as well as a couple of my colleagues received calls from customers, seeking clarifications on how to implement the sample presented in the article. This called for a defininte reimplementation,  and what an experience it was! Here are the code samples from that implementation.

I will start with the classes that will help implement the Upload and Delete Event Handlers. This is spread over two classes: BaseEventSink(which is an extended version of the file that gets downloaded from the event handler tool kit) and the EventSinkData class

/*=====================================================================

  File:      BaseEventSink.cs

  Summary:   Base class for cached event sinks.

---------------------------------------------------------------------

using System;

using System.Xml;

using System.Runtime.InteropServices;

using System.Security.Principal;

using System.IO;

using System.Diagnostics;

using System.Text;

using System.Collections;

using System.Collections.Specialized;

using Microsoft.SharePoint;

using RecycleBin.SharePoint.Configuration;

using Microsoft.SharePoint.Administration;

 

 

namespace RecycleBin.SharePoint.EventSink

{

    /// <summary>

    /// Base class for cached event sinks.

    /// Handles impersonation and caching event sink classes to handle multiple events on

    /// the same list.

    /// </summary>

      public class BaseEventSink : Microsoft.SharePoint.IListEventSink

      {

           

            public BaseEventSink()

            {

           

            }

 

            /// <summary>

            /// Implementation of the OnEvent method. Windows SharePoint Services will

            /// call this method when an event occurs.

            /// </summary>

            /// <param name="listEvent">The SPListEvent object that describes the event which occured.</param>

            public virtual void OnEvent(Microsoft.SharePoint.SPListEvent listEvent)

            {

                  WriteToFile("Event has been fired, and entered the custom event handler.");

                  WriteToFile("List Event dump:- " + listEvent.ToString());

                  if (listEvent == null)

                  {

                        throw new ArgumentNullException("listEvent", "list event object cannot be null");

                  }

           

                  WriteToFile("Getting the cachedEventSink");

                  BaseEventSink sink = GetCachedEventSink(listEvent);

                  WriteToFile("Got the Cached Event Sink");

 

                  WriteToFile("Starting the impersonation.");

                  WindowsImpersonationContext wip = null;

 

                  //Ensure each sink instance handles only one event at a time.

                  lock(sink)

                  {

                        try

                        {  

                              //Make sure the sink class has the current SPListEvent object

                              sink.EventInfo = listEvent;

                             

                              //Impersonate the appropriate user

                              WriteToFile("Impersonating the administrator");

                              WindowsIdentity id = sink.HandlerIdentity;

                              if (id != null)

                                    wip = id.Impersonate();

 

                              WriteToFile("Impersonation successful");

                              WriteToFile("Transferring the control to the actual event handler.");

                              sink.HandleEvent();

                   

                        }

                        finally

                        {

                              //Cleanup

                              if (wip != null)

                                    wip.Undo();

                              if (m_web != null)

                                    m_web.Close();

                                   

                              //Null out cached SPWeb and SPList objects so that

                              //fresh ones are obtained for the next event.

                              m_web = null;

                              m_list = null;

                        }

                  }

            }

 

            /// <summary>

            /// HandleEvent is called when an event occurs. Child classes should perform the main

            /// event handling actions here.

            /// </summary>

            protected virtual void HandleEvent()

            {

                 

                  //                if (admin.IsCurrentUserGlobalAdmin())                

            {

                  WriteToFile("Entered the EventHandler");

                  WriteToFile("Event fired is of type:- " + EventInfo.Type.ToString());

                  switch(EventInfo.Type)

                  {

                        case SPListEventType.Delete:

                              WriteToFile("Delete event fired.");

                              WriteToFile("Transferring to the Delete handler.");

                              DeleteHandler();

                              break;

                        case SPListEventType.Move:

                              //                            if(!Include()){break;}

                              WriteToFile("Move Event fired.");

                              WriteToFile("Transferring to the Move handler.");

                              MoveHandler();

                              break;

                        case SPListEventType.Update:

                        case SPListEventType.Insert:

                        case SPListEventType.Copy:

                              //                            if(!Include()){break;}

                              WriteToFile("Update/Insert/Copy event fired.");

                              WriteToFile("Transferring to the Update/Insert/Copy handler.");

                              UpdateInsertCopyHandler();

                              break;

                  }

            }

            }

       

            /// <summary>

            /// The SPListEvent object that describes the current event.

            /// </summary>

            protected virtual SPListEvent EventInfo

            {

                  get

                  {

                        return m_ListEvent;

                  }

                  set

                  {

                        m_ListEvent = value;

                        m_web = null;

                        m_list = null;

                        m_fileUrl = null;

                        m_sinkData = null;

                  }

            }

       

            /// <summary>

            /// The SPWeb object for the web site that contains the list

            /// that the event occured on.

            /// </summary>

            protected virtual SPWeb EventWeb

            {

                  get

                  {

                        if (m_web == null)

                        {

                              WriteToFile("Getting the web context.");

                              m_web = m_ListEvent.Site.OpenWeb();

                 

                        }

                        return m_web;

                  }

            }

 

            /// <summary>

            /// The SPList object for the list the event occured on.

            /// </summary>

            protected virtual SPList EventList

            {

                  get

                  {

                        if (m_list == null)

                        {

                              m_list = EventWeb.Lists[EventInfo.ListID];

                        }

                        return m_list;

                  }

            }

 

            /// <summary>

            /// The url of the file the event occured on.

            /// Equals UrlAfter if UrlAfter is not empty. Otherwise equals

            /// UrlBefore.

            /// </summary>

            protected string EventFileUrl

            {

                  get

                  {

                        if (m_fileUrl == null)

                        {

                              string webUrl = m_ListEvent.WebUrl == null ? "<NULL>" : m_ListEvent.WebUrl;

                              string webRelUrl = m_ListEvent.UrlAfter;

                              if (webRelUrl == null || webRelUrl.Length == 0)

                              {

                                    webRelUrl = (m_ListEvent.UrlBefore == null || m_ListEvent.UrlBefore.Length == 0 ?

                                          "<NULL>" : m_ListEvent.UrlBefore);

                              }   

                              m_fileUrl = String.Format("{0}/{1}", webUrl, webRelUrl);

                        }

                        return m_fileUrl;

                  }

            }

       

            protected string Data

            {

                  get

                  {

                        if (m_sinkData == null)

                        {

                              m_sinkData = m_ListEvent.SinkData;

                        }

                        return m_sinkData;

                  }

            }

 

            # region Custom Code

 

            /// <summary>

            /// Converts XML string retrieved from SinkData property into an object

            /// </summary>

            protected EventSinkData Configuration

            {

                  get

                  {

                        try

                        {

                              m_ConfigData = EventSinkData.GetInstance(Data);

                              return m_ConfigData;

                        }

                        catch(Exception ex)

                        {

                              PublishException(ex.ToString());

                        }

                        return null;

                  }

            }//Configuration

 

            /// <summary>

            /// Makes sure that the proper folder structure is in place for copying, moving or inserting a file in to the mirror

            /// document library or the Recycle Bin document library.

            /// </summary>

            /// <param name="web">

            /// Web where the folder structure needs to be built or ensured

            /// </param>

            /// <param name="finalUrl">

            /// Url containing the structure that needs to be built, must contain the trailing "/" if it is not a link to a file.

            /// If it is a link to a file the file name will be stripped and the folder structure ensured.

            /// </param>

            /// <returns>

            /// File path that has been built

            /// </returns>

            protected  static string EnsureParentFolder(SPWeb web, string finalUrl)

            {

                  finalUrl = web.GetFile(finalUrl).Url;

                  int x = finalUrl.LastIndexOf("/");

                  string epf = String.Empty;

                  if(x <= -1)

                        return epf;

                  epf = finalUrl.Substring(0, x);

                  SPFolder folder = web.GetFolder(epf);

                  if(folder.Exists)

                        return epf;

                  SPFolder curFolder = web.RootFolder;

                  string [] folders = epf.Split('/');

                  for(int i = 0; i < folders.Length; i ++)

                  {

                        curFolder = curFolder.SubFolders.Add(folders[i]);

                  }

                  return epf;

            }//EnsureParentFolder

 

            protected  static void PublishException(string errors)

            {

                  if(errors.Length > 0)

                  {

                        if(!EventLog.SourceExists(SOURCENAME))

                              EventLog.CreateEventSource(SOURCENAME, LOGNAME);

                        EventLog el = new EventLog();

                        el.Source = LOGNAME;

                        el.WriteEntry(errors, EventLogEntryType.Error);

                  }

            }

 

            # endregion

 

 

            # region Windows Impersonation Code

 

            /// <summary>

            /// The WindowsIdentity class of the identity the Event Sink should impersonate.

            /// </summary>

            protected virtual WindowsIdentity HandlerIdentity

            {

                  get

                  {

                        m_Identity = CreateIdentity(Configuration.UserName,

                                    Configuration.Domain,

                                    Configuration.Password);

                                         

                              return m_Identity;

                       

                  }

     

     

      }

 

        /// <summary>

        /// Helper method to handle creating a WindowsIdentity object from a username / domain / password.

        /// </summary>

        /// <param name="User">The username of the account to impersonate.</param>

        /// <param name="Domain">The domain of the account to impersonate.</param>

        /// <param name="Password">The password of the account to impersonate.</param>

        /// <returns></returns>

        public 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_CLEARTEXT = 3;

 

            tokenHandle = IntPtr.Zero;

 

            // Call LogonUser to obtain a handle to an access token.

            bool returnValue = LogonUser(User, Domain, Password,

                LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT,

                ref tokenHandle);

            if (false == returnValue)

            {

                int ret = Marshal.GetLastWin32Error();

                throw new Exception("LogonUser failed with error code: " +  ret);

            }

 

 

            //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;

        }

 

            # endregion

 

        /// <summary>

        /// Get the cached event sink object to handle this event.

        /// </summary>

        /// <returns>The cached IListEventSink object that should handle the current event.</returns>

        private BaseEventSink GetCachedEventSink(SPListEvent evt)

        {

            BaseEventSink sink = null;

           

            //Syncrhonize both reads and writes to the cache.

            //Even though the Hashtable class is threadsafe for reads, we don't want to have

            //multiple sink instances per list. Otherwise, if many events occur on a list

            //right off the bat, you could get many instances that all have to initialize

            //themselves.

            lock(SinkCache.SyncRoot)

            {

                sink = SinkCache[evt.ListID] as BaseEventSink;

           

                //The cached sink is only fit if it is the same type and has the same data

                //as the current sink.

                if (sink == null ||

                    ! sink.GetType().Equals(this.GetType()) ||

                    evt.SinkData != sink.Data)

                {

                    //Update the cache, throw away the old sink if it exists.

                    SinkCache[evt.ListID] = this;

                    sink = this;

                }

            }

            return sink;

        }

 

 

            # region Win32 API calls

       

        [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);

            # endregion

 

        protected SPListEvent m_ListEvent;

        private SPWeb m_web;

        private SPList m_list;

        private string m_fileUrl;

        private string m_sinkData;

 

            // Custom variables start

            private EventSinkData m_ConfigData;

            protected static Hashtable SinkCache = new Hashtable();

 

            private const string SOURCENAME = "Recycle Bin";

            private const string LOGNAME = "Recyclebin.EventSink";

 

            private string strMirror = "New";

            private string strRecycle = "Recycle Bin";

            private WindowsIdentity m_Identity = null;

            private SPGlobalAdmin admin = new SPGlobalAdmin();

            private WindowsIdentity id;

//          private EventSinkData e = new EventSinkData();

 

            // Custom variables end

       

 

            private void UpdateInsertCopyHandler()

            {    

                  WriteToFile("Entered the UpdateInsertCopy handler.");

                  string fileLocation = EventInfo.UrlAfter;

                  WriteToFile("Got the before location:- " + fileLocation);

//                string newFileLoc = String.Concat(strMirror, "/", fileLocation.Remove(0,5));

                  string newFileLoc = String.Concat(Configuration.MirrorLib, "/", fileLocation.Remove(0,Configuration.MainLib.Length+1));

                  WriteToFile("Got the new location:- " + newFileLoc);

 

 

                  SPFile miscFile = EventWeb.GetFile(fileLocation);

                 

                  try

                  {

                        //EnsureParentFolder(EventWeb, newFileLoc);

//                      miscFile.CopyTo(newFileLoc, true);

//                      SPFolder newFolder = EventWeb.GetFolder(strMirror);

                        WriteToFile("Getting the folder context.");

                        SPFolder newFolder = EventWeb.GetFolder(Configuration.MirrorLib);

                        WriteToFile("Preparing to copy the file.");

                        Byte[] byteArray  = miscFile.OpenBinary();

                        WriteToFile("Copying the file.");

                        newFolder.Files.Add(miscFile.Name,byteArray);

                        WriteToFile("Copy done.");

 

                  }

                  catch(Exception ex)

                  {

                        WriteToFile("Exception occurred:- " + ex.ToString());

                        PublishException(ex.ToString());

                  }

            }//UpdateInsertCopyHandler

 

            private void DeleteHandler()

            {    

                 

                  WriteToFile("Entered Delete handler.");

                  SPFile delFile = EventWeb.GetFile(String.Concat(Configuration.MirrorLib, "/", EventInfo.UrlBefore.Remove(0,Configuration.MainLib.Length+1)));

                  WriteToFile("Got the old address. " + String.Concat(Configuration.MirrorLib, "/", EventInfo.UrlBefore.Remove(0,Configuration.MainLib.Length+1)));

                  string newFileLoc = String.Concat(Configuration.RecycleBinLib, delFile.Url.Remove(0,Configuration.MirrorLib.Length));

                  WriteToFile("New address:- " + newFileLoc);

 

                  try

                  {

//                      EnsureParentFolder(EventWeb, newFileLoc);

//                      delFile.MoveTo(newFileLoc, true);

                        WriteToFile("Getting the folder context.");

                        SPFolder newFolder = EventWeb.GetFolder(Configuration.RecycleBinLib);

                        WriteToFile("Preparing to copy.");

                        Byte[] byteArray  = delFile.OpenBinary();

                        WriteToFile("Copying the file.");

                        newFolder.Files.Add(delFile.Name,byteArray,true);

                        WriteToFile("Copying complete.");

                  }

                  catch(Exception ex)

                  {

                        WriteToFile("Exception occurred:- " + ex.ToString());

                        PublishException(ex.ToString());

                  }

            }//DeleteHandler

 

            private void MoveHandler()

            {

                  string mirrorName = strMirror;

 

//                SPFile moveFile = EventWeb.GetFile(String.Concat(mirrorName, "/", EventInfo.UrlBefore.Remove(0,5)));

                  SPFile moveFile = EventWeb.GetFile(String.Concat(Configuration.MirrorLib, "/", EventInfo.UrlBefore.Remove(0,Configuration.MirrorLib.Length-1)));

 

                 

//                string newFileLoc = String.Concat(mirrorName, "/", EventInfo.UrlAfter);

                  string newFileLoc = String.Concat(Configuration.MirrorLib, "/", EventInfo.UrlAfter);

                  try

                  {

                        EnsureParentFolder(EventWeb, newFileLoc);

                        moveFile.MoveTo(newFileLoc, true);

                  }

                  catch(Exception ex)

                  {

                        PublishException(ex.ToString());

                  }

                 

            }//MoveHandler

 

            // Logging

            private     static void WriteToFile(String input)

            {

                  try

                  {

                        string filePath=@"C:\sharepoint\Log.txt";

                        FileInfo logFile = new FileInfo(filePath);

                        if(logFile.Exists)

                        {

                              if (logFile.Length >= 100000)

                                    File.Delete(filePath);

                        }

 

                        FileStream fs =   new   FileStream(filePath,FileMode.OpenOrCreate, FileAccess.ReadWrite);

 

                        StreamWriter w = new StreamWriter(fs);

                        w.BaseStream.Seek(0,SeekOrigin.End);

 

                        w.WriteLine(input);

                        w.WriteLine("--------------------------------------------------------------");

                        w.Flush();

                        w.Close();

                  }

                  catch(System.Exception ex)

                  {

                        PublishException(ex.ToString());

                  }

            }

    }

}

 

Continued in the next one due to the length of the post.........

Published Monday, October 17, 2005 2:14 AM by Harshawardhan Chiplonkar
Filed under:

Comments

# re: Recycle Bin

It is very useful to me  pls send modifications to this.
Tuesday, July 04, 2006 5:49 AM by satyasiva

# SharePoint 2003 Recycle Bin Links

I was ego surfing today and came upon the following links that related to the "Add A Recycle Bin To Windows SharePoint Services For Easy Document Recovery" artcile that I contributed to last year. http://blogs.msdn.com/harsh/archive/2005/10/17/481621.aspxGreat

Monday, February 05, 2007 6:58 PM by Capital Design
Anonymous comments are disabled
 
Page view tracker