I was recently asked to help out with a problem where we had to add a new field to every document library in our SharePoint 2007 farm.
We covered new sites using Feature stapling to add the new field to the document library via the GLOBAL template, but we needed to make sure all existing sites had the new field also. I could feel a console application coming on…
Having not done anything like this before, I needed to work out what I actually needed to do.
Figure 1.0
Figure 1.0 above was my (very) high level outline of what I needed to do. Obviously I needed to break this down further, put some logic behind it all and because it needed to be run on multiple web applications, I wanted it to accept some parameters. All quite new stuff for me.
I normally like to break things like this down into small chunks, so the first thing I wanted to do was open the selected top-level site collection and list all the webs and sub-webs.
Initially I just wanted to list out the top-level site collections, so I tried out this snippet;
public static void AddColumntoList()
{
using (SPSite sites = new SPSite(“http://sitename”))
{
foreach (SPSite site in sites.WebApplication.Sites)
{
Console.WriteLine(site.Url);
}
}
}
There is no doubt a better way of doing this, or at least a more efficient one, but this worked for me. So now we have a list of all of the top-level site collections to work with. The Url is just one of the properties available from the SPSite site object.
// All site collections in parent web application
foreach (SPSite site in sites.WebApplication.Sites)
{
Console.WriteLine(site.Url);
// All web sites in site collections
foreach (SPWeb web in site.AllWebs)
{
if (web.ParentWeb == null)
{
Console.WriteLine(" " + web.Title);
}
else
{
// Indent web sites
Console.WriteLine(" " + web.Title + " (a subsite of " + web.ParentWeb + ")");
}
}
}
Because site.AllWebs in the foreach statement above gets all webs and sub-webs in one go, I’ve added in a check to see if web.ParentWeb is null, this looks for the sub-webs and then indents them when they are displayed through the console.
So, where next? We need to identify the document libraries that are in each web.
// Display all lists within each web site
SPListCollection listcollection = web.Lists;
for (int i = 0; i < listcollection.Count; i++)
{
if (list.BaseTemplate.ToString() == "DocumentLibrary")
{
SPList list = listcollection[i];
Console.WriteLine(" " + list.Title);
}
}
In the above code you can see that we have built a list collection for the current web and then, in this example, checked to see if the base template used is a document library. Finally we write out the name of the list.
Because I don’t want to add the field to internal document libraries, I can add in an additional if clause before the SPList statement.
if
(list.Title == "Site Collection Documents" || list.Title == "Site Collection Images" || list.Title == "Form Templates" || list.Title == "Style Library" || list.Title == "Reporting Templates")The result of this if clause is to do nothing at this stage, the Console.WriteLine would be in the else part of the statement.
The next step is to check to see if the field already exists;
if (list.Fields.ContainsField("Confidentiality Level") == false)
{
.....
}
The final part of the process is to actually add the new field :-)
// Add Confidentiality Category field
string fieldName = list.Fields.Add("Confidentiality Level", SPFieldType.Choice, true);
SPFieldChoice categoryField = (SPFieldChoice)list.Fields["Confidentiality Level"];
categoryField.Choices.Add("Public");
categoryField.Choices.Add("Internal");
categoryField.Choices.Add("Confidential");
categoryField.Choices.Add("Highly Confidential");
categoryField.DefaultValue = "Highly Confidential";
categoryField.Update();
Console.WriteLine(" *** Confidentiality Level has been added to " + list.Title + " ***");
That’s pretty much it. I have added some extra coding around it all to add in two parameters, the site collection to be updated and a switch. I wanted to be able to execute the application and go through the whole process without actually performing the update. I have added a /norun switch which will do just that. The alternative is the /run switch. The final switch is /backout, which removes the field, using the following code;
list.Fields[
"Confidentiality Level"].Delete();There is, of course, an if clause around it! So there it is – I have tested the full code on my virtual environment and everything works just fine.
For those of you who would like the full code, I’ve attached the C# file here – SiteSpider.cs – I hope you find it useful! Please do post any changes you make here, especially if you can make the code more efficient!
Comments