So far, in my research I have seen that it is unwise to set AllowUnsafeUpdates on GET request operation to avoid cross site scripting. But, if it is required to allow this,
I use a wrapper class for handling most manipulation of SPWeb objects. This helps me remember to close the web, and it eases the problems of unsafeupdates setting. It is a bit bloated, as I have patched on new constructors and members. but, then again; so is the SPWeb class.
Usage:
using (WebWrapper wrapper = new WebWrapper("http://localhost"))
{
wrapper.AllowUnsafeUpdates();
//Do work on wrapper.
}
The class definition:
using System;
using System.Collections.Specialized;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
namespace Skaar.SharePoint.Customization
{
///
/// A wrapper for a object.
/// Closes web object on Dispose if applicable.
///
[Serializable]
[DebuggerDisplay("{Uri} Unsafe:{AllowUnsafeUpdatesSetting} Update:{UpdatePending}")]
public sealed class WebWrapper : IDisposable, IDeserializationCallback, IEquatable
{
[NonSerialized] private bool unsafeUpdatesSetting;
[NonSerialized] private SPWeb web;
///
/// Determines if the inner web object should be closed.
///
[NonSerialized] private bool webShouldBeClosed;
///
/// This value is used in serialization to restore .
///
private string webUrl;
///
/// Creates a new wrapper object.
///
/// A web that should be closed/disposed when done.
public WebWrapper(SPWeb web) : this(web, true)
{
}
///
/// Creates a new wrapper object.
///
/// An inner web object
/// If true, the web object is closed in the method.
public WebWrapper(SPWeb web, bool webShouldBeClosed)
{
setWeb(web, webShouldBeClosed);
}
///
/// Creates a new wrapper object.
///
/// The address to a web.
public WebWrapper(Uri webAddress)
{
using (SPSite site = new SPSite(webAddress.ToString()))
{
string relativeUrl = renderWebRootRelativeUrl(webAddress);
if (relativeUrl == null)
{
setWeb(site.OpenWeb(), true);
}
else
{
setWeb(site.OpenWeb(relativeUrl), true);
}
}
}
private string renderWebRootRelativeUrl(Uri address)
{
for (int i = 0; i < address.Segments.Length; i++)
{
string segment = address.Segments[i];
if (string.Equals(segment, "_layouts/"))
{
string newUrl=string.Join(null, address.Segments, 0, i).Trim('/');
return newUrl;
}
}
return null;
}
///
/// If true, will be called in .
///
public bool UpdatePending { get; private set; }
///
/// The setting of the inner web ( )
///
public bool AllowUnsafeUpdatesSetting
{
get { return Web.AllowUnsafeUpdates; }
}
///
/// The inner object.
///
/// Exception is thrown if is true.
public SPWeb Web
{
get
{
if(IsDisposed)
{
throw new ObjectDisposedException("Web wrapper is disposed.");
}
return web;
}
}
///
/// The address of the wrapped as a object.
///
public Uri Uri
{
get { return new Uri(Web.Url); }
}
///
/// The address of the wrapped as a object.
///
public Uri GetUri(SPUrlZone zone)
{
return Site.WebApplication.GetResponseUri(zone, Uri.AbsolutePath);
}
///
/// Creates a wrapper around the context web.
/// The web will not be closed when wrapper is disposed. Returns null if context is unavailable.
///
public static WebWrapper Context
{
get
{
return SPContext.Current==null?null:new WebWrapper(SPContext.Current.Web, false);
}
}
///
/// This is a static property wrapping of
/// the method, using
/// the current web as
/// parameter.
/// Returns null if context is unavailable.
///
public static WebWrapper CloneOfContext
{
get
{
if (SPContext.Current != null)
{
SPWeb contextWeb = SPContext.Current.Web;
return CloneOf(contextWeb);
}
return null;
}
}
///
/// Returns the property of the object.
///
public bool Exists
{
get { return Web != null && Web.Exists; }
}
///
/// Gets the object of .
///
/// This object should not be closed by user code.
public SPSite Site
{
get { return web.Site; }
}
///
/// Gets the owner defined in .
///
public SPUser Owner
{
get
{
return Site.Owner;
}
}
///
/// Returns a context of the inner .
///
public SPContext ContextOfWeb
{
get { return SPContext.GetContext(web); }
}
///
/// Gets the language of .
///
public CultureInfo Locale
{
get { return Web.Locale; }
}
///
/// Gets the language of the root web.
///
public CultureInfo LocaleOfRoot
{
get
{
using (WebWrapper root = Root)
{
return root.Locale;
}
}
}
///
/// Returns a new wrapping the root of this.
///
public WebWrapper Root
{
get
{
if (webShouldBeClosed)
using (SPSite site = Site)
{
return new WebWrapper(site.RootWeb);
}
return new WebWrapper(Site.RootWeb);
}
}
///
/// A wrapper for .
///
public string Title
{
get { return Web.Title; }
set { Web.Title = value; }
}
///
/// A wrapper for .
///
public Guid ID
{
get { return Web.ID; }
}
#region Web Properties
[NonSerialized] private bool updatePropertiesPending;
///
/// A wrapper method to object's indexer.
///
/// The key to use when fetching property value.
/// A string containing the value.
public string GetProperty(string key)
{
return Web.Properties[key];
}
///
/// Sets the value in the object's . Creates a new key, or updates an existing as needed.
///
/// The key to use when storing the property value.
/// The value to set in the key.
/// The property is set to true.
public void SetProperty(string key, string value)
{
if (!Web.Properties.ContainsKey(key))
{
Web.Properties.Add(key, value);
}
else
{
Web.Properties[key] = value;
}
updatePropertiesPending = true;
}
#endregion
#region IDeserializationCallback Members
///
///Runs when the entire object graph has been deserialized.
///
///
///The object that initiated the callback. The functionality for this parameter is not currently implemented.
public void OnDeserialization(object sender)
{
using (SPSite site = new SPSite(webUrl))
{
setWeb(site.OpenWeb(), true);
}
}
#endregion
#region IDisposable Members
///
///Closes inner web object if appropriate.
///
///2
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool isDisposing)
{
if (IsDisposed) return;
if (isDisposing)
{
doDisposeOfWeb();
IsDisposed = true;
}
}
#endregion
///
/// Value is true if method has been called. Object is not in a usable state.
///
internal bool IsDisposed
{
get; private set;
}
#region IEquatable Members
///
/// This tests whether the two objects wraps the same web. It may however be two different instances of the same web.
///
/// Another wrapper object.
/// True if equals, false otherwise.
public bool Equals(WebWrapper other)
{
if (other == null)
{
return false;
}
return Uri.Equals(other.Uri);
}
#endregion
///
/// Reopens the inner object. May be used when web object needs to be rereferenced in a new security context.
///
public void ReOpen()
{
bool unsafeSetting = AllowUnsafeUpdatesSetting;
using (SPSite site = new SPSite(Web.Url))
{
SPWeb newWeb = site.OpenWeb();
doDisposeOfWeb();
web = newWeb;
web.AllowUnsafeUpdates = unsafeSetting;
unsafeUpdatesSetting = false;
webShouldBeClosed = true;
}
}
private void doDisposeOfWeb()
{
if (Web == null) return;
Update(true);
if (webShouldBeClosed)
{
Web.Close();
}
else if (Web.Exists)
{
Web.AllowUnsafeUpdates = unsafeUpdatesSetting;
}
web = null;
}
///
/// Calls on the object.
///
public void Update()
{
Update(false);
}
///
/// Sets to true .
///
public void SetUpdatePending()
{
UpdatePending = true;
}
///
/// Calls on the object.
/// If true, update will depend on state of the property.
///
public void Update(bool onlyIfPending)
{
if (onlyIfPending)
{
if (updatePropertiesPending)
{
Web.Properties.Update();
updatePropertiesPending = false;
}
if (UpdatePending)
{
Web.Update();
UpdatePending = false;
}
}
else
{
Web.Update();
UpdatePending = false;
}
}
///
/// Returns the list from with equal to .
///
/// The of an existing list.
/// The first list found with the given title, or null, if no list is found.
public SPList GetList(string title)
{
foreach (SPList list in Web.Lists)
{
if (list.Title == title)
{
return list;
}
}
return null;
}
///
/// A wrapper method to the object's indexer.
///
/// The id of the list to return.
/// The list with the supplied id.
public SPList GetList(Guid id)
{
return Web.Lists[id];
}
private void setWeb(SPWeb innerWeb, bool shouldBeClosed)
{
if (innerWeb == null || !innerWeb.Exists)
{
throw new ArgumentException("Web does not exist", "innerWeb");
}
web = innerWeb;
webShouldBeClosed = shouldBeClosed;
unsafeUpdatesSetting = innerWeb.AllowUnsafeUpdates;
AllowUnsafeUpdates();
webUrl = web.Url;
}
///
/// Creates a new object using the
/// url of the parameter and wraps it
/// in a new wrapper object. The web will be
/// closed when the wrapper is disposed.
/// The cloning is done using the , thus no security context is transferred to the new web.
///
/// Use this to create a clone of the context web.
/// The web to clone.
/// A new wrapper object.
public static WebWrapper CloneOf(SPWeb web)
{
using (SPSite site = new SPSite(web.Url))
{
return new WebWrapper(site.OpenWeb());
}
}
///
/// Creates a new object using the
/// of the parameter and wraps it
/// in a new wrapper object. The web will be
/// closed when the wrapper is disposed.
///
/// Use this to create a clone of the context web.
/// The wrapper to clone.
/// A new wrapper object.
public static WebWrapper CloneOf(WebWrapper web)
{
return CloneOf(web.Web);
}
///
/// Sets the AllowUnsafeUpdates property to true on the
/// wrapped web object.
///
/// The setting is resat back in the dispose method, unless the
/// web itself is closed.
///
///
public void AllowUnsafeUpdates()
{
Web.AllowUnsafeUpdates = true;
}
///
/// Returns the url of the inner web.
///
/// A value that equals property.
public override string ToString()
{
return webUrl;
}
///
/// Returns a new object wrapping a new copy of the inner object.
/// The cloning is done using the , thus no security context is transferred to the new web.
///
/// The static method is used on the property.
/// A new wrapper.
public WebWrapper Clone()
{
return CloneOf(Web);
}
///
/// Implicitly wraps the web object in a object.
///
/// The web to wrap.
/// A new wrapper object. The original web may be accessed through the property.
public static implicit operator WebWrapper(SPWeb web)
{
return new WebWrapper(web, false);
}
///
/// Explicitly extracts the value from the .
///
/// The object wrapping the to extract.
/// The inner of .
/// The returned object should be properly disposed after use.
public static explicit operator SPWeb(WebWrapper wrapper)
{
wrapper.DoNotDisposeInnerWeb();
return wrapper.Web;
}
///
/// Wrapper method for on object.
///
/// A site relative uri to the list.
/// A list if found.
public SPList GetList(Uri uri)
{
return web.GetList(uri.ToString());
}
///
/// Wrapper method for on object.
///
/// The results of the query,
public DataTable GetSiteData(SPSiteDataQuery query)
{
return Web.GetSiteData(query);
}
///
/// Creates a new as a sub web to this.
///
/// The proposed local url of the new web. The nearest available is selected.
/// The title of the new web.
/// The description of the new web.
/// The language of the new web. If the language is not supported, the language of this is used.
/// The site template to use.
/// The new web wrapped in a new object.
[DebuggerStepThrough]
//debugger step through is to prevent this method to break when debugging, as it throws exceptions by [poor] design.
public WebWrapper CreateSubWeb(string url, string name, string description, uint language,
string template)
{
SPWeb newWeb;
try
{
newWeb = Web.Webs.Add(findSuitableWebUrl(url), name, description, language, template, true, false);
}
catch (SPException err)
{
if (err.ErrorCode == -2130575266)
{
//language not supported. Fallback to parent web language
newWeb = Web.Webs.Add(findSuitableWebUrl(url), name, description, Web.Language, template, true,
false);
}
else
throw;
}
return new WebWrapper(newWeb);
}
private string findSuitableWebUrl(string proposedName)
{
StringCollection names = new StringCollection();
names.AddRange(Web.Webs.Names);
int suffixIndex = 0;
const int maxIterations = 100000;
string name = proposedName;
while (names.Contains(name) && suffixIndex < maxIterations)
{
name = string.Format("{0}_{1}", proposedName, suffixIndex++);
}
return name;
}
///
/// Calling this method will inhibit the default behaviour of closing the web on disposal.
///
/// Use with caution.
internal void DoNotDisposeInnerWeb()
{
webShouldBeClosed = false;
}
}
}