In this week I was busy with creating a webservice that automatically would create new sitecollections and subsites for a given webapplication and other parameters. Nothing fancy here right? Well.. building a webservice and hosting it in SharePoint (in my case in the LAYOUTS folder) wasn’t that easy, unless you figure out how to do it ;) The steps you have to go through, are the following :
- Add a reference to your custom assembly in the markup of the webservice page as follows
<%@ WebService Language="C#" CodeBehind="CustomWebService.asmx.cs" Class="zevenseas.SharePoint.WebServices.CustomWebService" %>
<%@ Assembly Name="zevenseas.SharePoint.WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00000000000" %>
- Deploy the webservice to the LAYOUTS folder, download the tool WSSWebServicePackager and make sure you have the Disco tool nearby (I had to download the first VS2005 SDK to get it) and put the created WSDL and Disco .ASPX’s next to the webservice.
- Create a console application, add a web reference to your custom webservice and you can use the custom webservice!
- (optional : every time you modify the webservice you have to repeat step 2 to get it working)
Now, since we have the webservice setup properly we can build something beautiful! I created the following methods:
- CreateNewSitecollection
- CreateNewSite
- UpdateSite
- DeleteSite
During the creation of a site, I make use of a custom sitedefinition that contains some document libraries and a contact list. Now the requirement was to have a lookup column in each document library to the contacts list. Plus some additional columns were required as well. Doing this using a sitedefinition is quite hard so I decided to by it code and I was surprised how easy it was. Check the code below to see how it works :
private void UpdateDocumentLibrary(SPList documentLibrary, List<string> choices)
{
//Here we add the columns to the document library, the first one is a 'simple' choice column
documentLibrary.Fields.Add(fieldInternalNameSoort, SPFieldType.Choice, true);
SPFieldChoice soortField = (SPFieldChoice)documentLibrary.Fields[fieldInternalNameSoort];
foreach (string choice in choices)
{
soortField.Choices.Add(choice);
}
soortField.Update();
//This is the tricky bit, adding a lookup column to an existing list
SPList contactList = documentLibrary.ParentWeb.Lists["Contacts"];
documentLibrary.Fields.AddLookup("Contacts", contactList.ID, false);
SPFieldLookup contactLookUp = (SPFieldLookup)documentLibrary.Fields["Contacts"];
contactLookUp.LookupField = contactList.Fields["Last Name"].Title;
//We update the document library to make sure that the columns are added
documentLibrary.Update();
//Now to add the columns to the default view of the document library we do the following
SPView addSoortColumnTodefaultView = documentLibrary.DefaultView;
addSoortColumnTodefaultView.ViewFields.Add(fieldInternalNameSoort);
addSoortColumnTodefaultView.Update();
SPView addContactsColumnTodefaultView = documentLibrary.DefaultView;
addContactsColumnTodefaultView.ViewFields.Add(fieldInternalNameContact);
addContactsColumnTodefaultView.Update();
}
The thing that I was struggling with the longest was to add the newly created columns to the default view. Thanks to Bryan Friedman I managed to solve it, check out his post on how to do this. Thanks Bryan! ;)
Next thing I wanted to do was adding some custom webparts to the default.aspx page instead of using the sitedefinition. This also was fairly easy.. I did this thing back in the 2003 days but back then you had to add the whole xml definition of the webpart (correct me if I’m wrong btw). But now you can create a reference to a webpart and add this using the limited webpart manager as such :
private bool AddWebParts(SPWeb currentWeb)
{
bool webpartsInPlace = false;
try
{
SPLimitedWebPartManager wm = currentWeb.GetLimitedWebPartManager("default.aspx", System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
//Creating a new instance of a custom navigation webpart
NavigationWebPart navWp = new NavigationWebPart();
navWp.Title = "Site Structure";
//Creating a new instance of a cusotm aggregation webpart
WhatsNewWebpart whatsnewWp = new WhatsNewWebpart();
whatsnewWp.Title = "What's new";
whatsnewWp.ListType = 101;
//Defining some properties of the webpart
if (currentWeb.WebTemplateId == 13000)
{
whatsnewWp.Scope = WhatsNewWebpart.ScopeEnum.Site;
whatsnewWp.ViewSiteColumn = true;
}
else
{
whatsnewWp.Scope = WhatsNewWebpart.ScopeEnum.Web;
}
whatsnewWp.Limit = 10;
whatsnewWp.ViewCreatedColumn = true;
whatsnewWp.ViewListColumn = true;
whatsnewWp.ViewModifiedColumn = true;
//Here we are actually adding the webparts to the site using the webpartzoneID's and the webpartOrder
currentWeb.AllowUnsafeUpdates = true;
wm.AddWebPart(navWp, "Left", 1);
wm.AddWebPart(whatsnewWp, "Left", 2);
currentWeb.Update();
currentWeb.AllowUnsafeUpdates = false;
webpartsInPlace = true;
}
catch (Exception e)
{
}
return webpartsInPlace;
}
Right.. so next on the requirements list was to have the MOSS Search active. Meaning that the searchscopes were not to be only “This site” or “This List” but also “All Sites” and having a redirect to the Searchcenter for the results. Now in the UI this is a simple setting (Site Actions –> Search Settings –> put in the url of SearchCenter –> Done) but doing this programmatically is a different story. In short, there is no method or function you can use that is in the SPWeb or SPSite object. Using reflector I found out that when clicking on the Ok button in the UI the following was happening :
this._rootWeb.AllProperties["SRCH_ENH_FTR_URL"] = this.urlTextBox.Text.Trim();
Not very pretty eh? But if it works.. it works! ;)
Next requirement please! Defining managed paths in code.. hmm.. well this one is pretty funny actually.. the first hit in Google was a blogpost by Hristo Pavlov that was published today describing exactly what I needed :) It seems that on the WebApplication class there is a “Prefixes” collection that holds all the managed paths. To add a managed path you have to do the following :
public bool ModifyWebApplication(SPWebApplication webApplication)
{
bool isModified = false;
try
{
webApplication.Prefixes.Add("blogs", SPPrefixType.WildcardInclusion);
webApplication.Update(true);
isModified = true;
}
catch (Exception error)
{ }
return isModified;
}
So there you go.. creating and configuring sites by only using code is pretty cool and not that hard to do!