In SharePoint you can easily configure automatic deletion for unused site collections.
This helps you to control the number of unused sites on your server and to decrease
overall growth of your database, but with this feature comes a big risk. When
a site is automatically deleted all content and data are permanently removed.
It is recommended by Microsoft to archive sites before they are deleted automatically…
This is a good tip, but how you can archive your site easily? Well, Microsoft
recommendation is to develop a tool. So... let me show you a small trick how
to achieve this.
You are probably familiar with the “Site collection quotas and locks” feature (http://youradminsite:port/_admin/sitequota.aspx),
by using it you can prevent access to content entirely. Anybody who attempts
to access the site will receive an access denied message (403 FORBIDDEN).This
feature gave me the idea to create a sort of buffer. Let’s say we specify to
delete sites approximately after 200 days, then, 100 days before we actually
delete it we set it to “No Access”. This means that unused sites are set to no
access after 100 days and if nobody “complains”, SharePoint will delete them
automatically after 200 days. In this case it gives you a buffer of 100 days
before we permanently delete a site.
Let’s build our "buffer"
Timer Job
First, we will create a Timer Job that checks each site collection when the content
was last changed. When a site left unused for some period of days – like 30 days,
we set the site to “No Access”
public class ManageUnusedSitesJob : SPJobDefinition
{
public ManageUnusedSitesJob() : base() { }
public ManageUnusedSitesJob(SPWebApplication webApp)
: base(Constant.JobName, webApp, null, SPJobLockType.ContentDatabase)
{
this.Title = Constant.JobName;
}
public override void Execute(Guid targetInstanceId)
{
string pbValue = Convert.ToString(WebApplication.Properties[Constant.PropertyBagName]);
foreach (SPSite site in this.WebApplication.Sites)
{
try
{
if (WebApplication.SendUnusedSiteCollectionNotifications)
{
int unusedSiteNotificationPeriod = int.Parse(pbValue);
int compareToToday = site.LastContentModifiedDate.CompareTo(DateTime.Now) * (-1);
if (compareToToday >= unusedSiteNotificationPeriod)
{
site.WriteLocked = false;
site.ReadOnly = false;
site.LockIssue = "The site was disabled on " + DateTime.Now.ToString(); ;
site.ReadLocked = true;
}
}
}
finally
{
site.RootWeb.Dispose();
site.Dispose();
}
}
}
}
Application Page
To help SharePoint administrators to configure the archive settings, we will create
an application page within Central Administration; the content of ManageUnusedSites.aspx
is as follows:
<%@ Page Debug="true" Language="C#" MasterPageFile="~/_admin/admin.master"
Inherits="Havivi.ManageUnusedSites.ManageUnusedSitesPage, Havivi.ManageUnusedSites,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3ff87e15cca2a19" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@ Register TagPrefix="wssuc" TagName="InputFormSection"
Src="~/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="~/_controltemplates/ButtonSection.ascx"
%>
<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderPageTitleInTitlearea"
runat="server">
Manage Unused Sites
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderPageDescription" runat="server">
With this tool you can archive sites before they are deleted (automatically). - by
Alon Havivi
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<table width="100%" class="propertysheet" cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="ms-error">
<asp:Literal ID="ErrorMessageLiteral" runat="server" EnableViewState="false" />
</td>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td>
<!-- web application selector -->
<wssuc:InputFormSection runat="server" Title="<%$Resources:spadmin, multipages_webapplication_title%>"
Description="<%$Resources:spadmin, multipages_webapplication_desc%>">
<template_inputformcontrols>
<td>
<SharePoint:WebApplicationSelector id="WebAppSelector" runat="server" OnContextChange="WebAppSelector_OnContextChange" />
</td>
</template_inputformcontrols>
</wssuc:InputFormSection>
</tr>
<tr valign="top">
<td>
<wssuc:InputFormSection runat="server" Title="Archiving Settings" Description="You can specify whether to archive the site collection automatically if use
is not confirmed.">
<template_inputformcontrols>
<asp:CheckBox runat="server" OnCheckedChanged="automaticallyArchive_OnChange" AutoPostBack="true" ID="automaticallyArchive" Text="Automatically archive the site collection" />
</td>
</tr>
<tr>
<td>
</template_inputformcontrols> </wssuc:InputFormSection>
<!-- job runtime schedule -->
<wssuc:InputFormSection runat="server" title="">
<template_description></template_description>
<template_inputformcontrols>
Archive site after <asp:DropDownList id="IntervalDaysDropDownList" runat="server" >
<asp:ListItem>0</asp:ListItem>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>6</asp:ListItem>
<asp:ListItem>7</asp:ListItem>
<asp:ListItem>8</asp:ListItem>
<asp:ListItem>9</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>15</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>60</asp:ListItem>
<asp:ListItem>90</asp:ListItem>
<asp:ListItem>120</asp:ListItem>
<asp:ListItem>150</asp:ListItem>
<asp:ListItem>200</asp:ListItem>
<asp:ListItem>250</asp:ListItem>
<asp:ListItem>300</asp:ListItem>
<asp:ListItem>350</asp:ListItem>
</asp:DropDownList> days not in use.
</template_inputformcontrols>
</wssuc:InputFormSection>
<wssuc:ButtonSection runat="server">
<template_buttons>
<asp:Button id="btnOk" runat="server" class="ms-ButtonHeightWidth" Text="OK" OnClick="btnOk_OnClick" />
</template_buttons>
</wssuc:ButtonSection>
</td>
</tr>
</table>
</asp:Content>
When viewed in the browser, the ManageUnusedSites.aspx page appears like this:

The next step is to build the code behind file for this page.
Code Behind
namespace Havivi.ManageUnusedSites
{
public class ManageUnusedSitesPage : GlobalAdminPageBase
{
protected WebApplicationSelector WebAppSelector;
protected DropDownList IntervalDaysDropDownList;
protected CheckBox automaticallyArchive;
protected Literal ErrorMessageLiteral;
protected override void OnLoad(EventArgs e)
{
SPContext.Current.Web.AllowUnsafeUpdates = true;
}
protected void WebAppSelector_OnContextChange(object sender, EventArgs e)
{
SPWebApplication webApplication = this.WebAppSelector.CurrentItem;
if (webApplication.Properties[Constant.PropertyBagName] != null)
IntervalDaysDropDownList.SelectedValue = webApplication.Properties[Constant.PropertyBagName].ToString();
bool checkedArchive = false;
foreach (SPJobDefinition job in this.WebAppSelector.CurrentItem.JobDefinitions)
{
if (job.Title == Constant.JobName)
{
checkedArchive = true;
}
}
automaticallyArchive.Checked = checkedArchive;
if (!automaticallyArchive.Checked)
{
IntervalDaysDropDownList.SelectedValue = "0";
IntervalDaysDropDownList.Enabled = false;
}
else
{
IntervalDaysDropDownList.Enabled = true;
}
}
protected void automaticallyArchive_OnChange(object sender, EventArgs e)
{
if (!automaticallyArchive.Checked)
{
IntervalDaysDropDownList.SelectedValue = "0";
IntervalDaysDropDownList.Enabled = false;
}
else
{
IntervalDaysDropDownList.Enabled = true;
}
}
protected void btnOk_OnClick(object sender, EventArgs e)
{
foreach (SPJobDefinition job in this.WebAppSelector.CurrentItem.JobDefinitions)
{
if (job.Title == Constant.JobName)
job.Delete();
}
if (automaticallyArchive.Checked)
{
SPWebApplication webApplication = this.WebAppSelector.CurrentItem;
if (webApplication.Properties[Constant.PropertyBagName] != null)
{
webApplication.Properties[Constant.PropertyBagName] = IntervalDaysDropDownList.SelectedValue;
webApplication.Update();
}
else
{
webApplication.Properties.Add(Constant.PropertyBagName, IntervalDaysDropDownList.SelectedValue);
webApplication.Update();
}
SPDailySchedule schedule = new SPDailySchedule();
schedule.BeginHour = 1;
schedule.BeginMinute = 0;
schedule.BeginMinute = 0;
ManageUnusedSitesJob manageunusedSites = new ManageUnusedSitesJob(this.WebAppSelector.CurrentItem);
manageunusedSites.Schedule = schedule;
manageunusedSites.Update();
}
}
}
internal static class Constant
{
internal static string ManageUnusedSitesSettingsId
{
get { return "ManageUnusedSitesJobs"; }
}
internal static string JobName
{
get { return "Manage Unused Sites Job"; }
}
internal static string PropertyBagName
{
get { return "_unusedSiteNotificationPeriod"; }
}
}
}
Feature
To add a new Manage Unused Sites link to the Application Management page in Central
Administration. We do this by using a Feature, as follows:
<?xml version="1.0" encoding="utf-8"?>
<Feature Id="c3850019-cabf-4d76-870b-4317bee01ba6"
Title="Manage Unused Sites"
Description="With this tool you can archive sites before they are deleted (automatically).
- by Alon Havivi"
Version="12.0.0.0"
Hidden="FALSE"
Scope="Farm"
ImageUrl="Eggheadcafe/eggheadcafe.jpg"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="elements.xml"/>
</ElementManifests>
</Feature>
The element manifest file elements.xml does add a new link to the Application Management
page in Central Administration:
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction Id="6DB61470-0AC8-4fb8-A641-7B141789B1F6"
GroupId="SiteManagement"
Location="Microsoft.SharePoint.Administration.ApplicationManagement"
Sequence="102"
Title="Manage Unused Sites"
Description="Description">
<UrlAction Url="_admin/Eggheadcafe/ManageUnusedSites.aspx" />
</CustomAction>
</Elements>
Summary
By combining these two features:
- Site use confirmation and deletion
- Site collection quotas and locks
We can create a sort of an archive tool that gives us a time buffer before we delete
our SharePoint site permanently.
Download
The downloadable source code and the WSP solution can be download from here:
- ManageUnusedSites.sln (source code)
- ManageUnusedSites.wsp (wsp solution)