zevenseas

Our News

Expanding our operations in other countries more


 

Creating and configuring sites programmatically.. I love it!

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 :

  1. Add a reference to your custom assembly in the markup of the webservice page as follows
  2. <%@ 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" %>
  3. 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.
  4. Create a console application, add a web reference to your custom webservice and you can use the custom webservice!
  5. (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!

Links to this post

Comments

On 16 Nov 2008 04:41, CD

Hi,
I'm building similar funcitonality except right now it's all in code-behind on a feature site. Any chance you'd be willing to post the web service solution? Thks and happy site programming!

On 16 Nov 2008 04:41, Robin

Hi CD, what do you want to see from the solution? ;)

On 16 Nov 2008 04:41, Carsten I Nielsen

Remember to dispose the Webpart manager and the mem leek in the manager.

using (SPLimitedWebPartManager manager = file.GetLimitedWebPartManager(scope))
{
manager.Web.Dispose(); // Do this to avoid memory leeks
}



Name

Url

Email

Comments

CAPTCHA Image Validation



 
 
 

© 2009 Community Kit For SharePoint