I recently had the need to develop a WebPart for a client that needed to be able to "unlock" a site collection. For any of you that have tried this from within a WebPart running on the SharePoint platform, you know there are quite a few issues that can come up.
There are four different types of locks that can be set, here is a quick mapping of how those locks match up with the SPSite locking properties:
Not locked / none site.writeLocked = false
site.readOnly = false
site.readLocked = falseAdding content prevented / noadditions site.writeLocked = true
site.readOnly = false
site.readLocked = falseRead-only / readonly site.writeLocked = false
site.readOnly = true
site.readLocked = falseNo access / noaccess site.writeLocked = false
site.readOnly = false
site.readLocked = true
Additionally, when a site is locked using any one of the lock options, the site.LockIssue is used to describe the reason for the lock. If the site was locked using the Central Administraion console, the Additional lock information (LockIssue) field is required when locking the site. However, this parameter is not set when locking a site using the command line interface stsadm.
Here are just a few of the error messages I ran into while trying to set the lock options outright in my WebPart:
- Updates are currently disallowed on GET requests. To allow updates on a GET, set the 'AllowUnsafeUpdates' property on SPWeb.
- The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.
- Access to this Web site has been blocked.
- Attempted to perform an unauthorized operation.
- Access denied.
SPSecurity.RunWithElevatedPrivileges(delegate()Next, with no more luck, I tried to initialize a instance of the site class running under the context of my administration account.
{
using (SPSite site = new SPSite(siteUrl))
{
site.AllowUnsafeUpdates = true;
site.ReadLocked = false;
site.ReadOnly = false;
site.WriteLocked = false;
site.LockIssue = "";
}
});
using (SPSite site = new SPSite(siteUrl, SPContext.Current.Web.AllUsers["domain\\adminUsername"].UserToken))After a little bit more investigating, I discovered that in order to perform the unlock procedures that I needed, full blown code impersonation was needed. With a little bit of additional code, I had the access that I needed.
{
site.AllowUnsafeUpdates = true;
site.ReadLocked = false;
site.ReadOnly = false;
site.WriteLocked = false;
site.LockIssue = "";
}
//get global admin access to perform the workThe code to implement the ImpersonateValidUser and UndoImpersonation functions can be found here: http://www.codeproject.com/KB/cs/zetaimpersonator.aspx, a wonderful bit of code from Uwe Keim.
ImpersonateValidUser("domain", "adminUsername", "password");
using (SPSite site = new SPSite(siteUrl))
{
site.WebApplication.FormDigestSettings.Enabled = false;
site.AllowUnsafeUpdates = true;
site.ReadLocked = false;
site.ReadOnly = false;
site.WriteLocked = false;
site.LockIssue = "";
}
UndoImpersonation();
By adding the line site.WebApplication.FormDigestSettings.Enabled = false to the code, we are able circumvent error #2. All of the remaining errors should be taken care of by our code impersonation. However, be warned that if the site has either readOnly or readLocked set to true, you will still have issues accessing values of various site properties. You will need to unlock the site to get the values that you need. If desired, you can then set the locks back to their previous state (assuming you have saved the state somewhere). And yes, you will even run into errors simply trying to save off this state for later use. The specific type of error that gets thrown will tell you everything you need to know about which of the three locks has been set.
When implementing the code, be sure that it is not executing when the page initially loads, otherwise you will run into error #1. Rather run the code during a page post back (i.e. in an event handler to manage the WebPart form submittal).