SharePoint - Managing Unused or Archive sites automatically

The Managing Unused SharePoint Tool can archive SharePoint sites before they are being automatically deleted. This will allow you to easily restore sites if they contain some critical data.


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)


By Alon Havivi   Popularity  (7038 Views)
Picture
Biography - Alon Havivi

Alon Havivi is a Microsoft Certified Technology Specialist, working as SharePoint Consultant / Developer at e-office. Specialized in SharePoint 2007/2010 and SharePoint Online (Office 365). With more than 10 years of experience in analysis, design and development complex Internet and Intranet portals using the latest Microsoft tools and practices, such as C# .NET 4.0, Silverlight and Windows Azure platform. Besides professional work, I write articles/blog and publish open source projects on CodePlex
View Alon Havivi's professional profile on LinkedIn. View Alon Havivi's projects on CodePlex. View Alon Havivi's articels on Eggheadcafe. Follow Alon Havivi on Twitter Connect with Alon Havivi via Facebook View Alon Havivi's Blog Subscribe to Alon Havivi RSS Feed