Recycle Bin.. Continued
Now that we have handled the Upload and Delete events, let us handle the Restore event. Implemented in a class library which contains similar classes as the earlier library. Again we have a BaseEventSink class which is extended from the default available with the event handler toolkit 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;
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("The event has been fired. 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 cached event sink.");
BaseEventSink sink = GetCachedEventSink(listEvent);
WriteToFile("Got the cached event sink.");
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 completed.");
WriteToFile("Now passing control to the 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 void HandleEvent()
{
WriteToFile("Entered the actual event handler.");
WriteToFile("The event is of type:- " + EventInfo.Type.ToString());
switch (EventInfo.Type)
{
case SPListEventType.Update:
case SPListEventType.Insert:
UpdateInsertHandler();
break;
}
}
/// <summary>
/// Used To restore files from Recycle Bin into the document library of origin
/// </summary>
private void UpdateInsertHandler()
{
WriteToFile("Entered the UpdateInserthandler.");
string fileLocation = EventInfo.UrlAfter;
WriteToFile("Got the file location:- " + fileLocation);
SPFile miscFile = EventWeb.GetFile(fileLocation);
// string newFileLoc = fileLocation.Remove(0, strRecycle.Length);
string newFileLoc = fileLocation.Remove(0, Configuration.RecycleBinLib.Length+1);
WriteToFile("Getting the new location:- " + newFileLoc);
try
{
// miscFile.MoveTo(string.Concat(strMain,"/",newFileLoc),true);
WriteToFile("Moving the file to the new location.");
miscFile.MoveTo(string.Concat(Configuration.MainLib,"/",newFileLoc),true);
WriteToFile("Move completed.");
}
catch(Exception ex)
{
PublishException(ex.ToString());
}
}//UpdateInsertHandler
/// <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)
{
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 strMain = "TEST";
// private string strRecycle = "Recycle Bin";
private WindowsIdentity m_Identity = null;
// Custom variables end
// 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());
}
}
}
}