When creating a build definition in TFS, you can set the option to build each check-in, meaning that a team build will get queued up every time a developer checks in a changeset.
When you have multiple developers working on a project, your build drop location might get cluttered. It's even worse if you have different build definitions for various branches of your source tree, e.g. Dev, Main, and Prod.
My colleague @pwalke suggested that we create a CurrentBuild folder that would always contain the most current build for every build definitions. If you browse to the team project's build drop location, you can open the folder and grab the build output.
This was pretty easy to do in the build definition by overriding the AfterDropBuild build target:
<Target Name="AfterDropBuild" Condition="'$(BuildBreak)'!='true'">
<CreateItem Include="$(DropLocation)\$(BuildNumber)\**\*.*">
<Output ItemName="BuildOutput" TaskParameter="Include"/>
</CreateItem>
<RemoveDir Directories="$(DropLocation)\CurrentBuild\$(BuildDefinition)" />
<Copy
SourceFiles="@(BuildOutput)"
DestinationFolder="$(DropLocation)\CurrentBuild\$(BuildDefinition)\%(RecursiveDir)"
SkipUnchangedFiles="false"/>
</Target>
This clears out the directory, and then recursively copies the build output to a subfolder in the CurrentBuild folder.
Update: Added a condition on the build target to verify if the build was successful, thanks @sajo!
I'm a big fan of packaging "run once assets" into the deployment process of a SharePoint WCM site - I believe that this type of content should be deployed using Features until content authors ultimately assume responsibility for maintaining it.
The reason I insist on this is that I think it is critical for developers to always be working with the whole site, and not in a sandbox. Get developers seeing the big picture early on in a SharePoint WCM project, and you're much more likely to avoid integration issues down the line.
So when it came time to provision a hierarchy of sites as part of a custom site definition, I explored different ways to automate this.
One way of implementing custom provisioning logic is by including an implementation of SPWebProvisioningProvider in your site definition. You can tap into SPWebProvisioningProvider's InitializePortal method and execute custom logic, e.g., creating webs, or setting custom navigation properties.
When exploring Andrew Connell's Minimal Publishing Portal site definition (download the code for AC's Professional SharePoint 2007 WCM Development book), I noticed the ProvisionData attribute of a template in the site definition that pointed to a file called PortalConfig.xml. This looked promising, it seemed like a way to define your custom provisioning logic in markup instead of using the SharePoint object model.
I couldn't find any good examples of anybody using this in a site definition, so I fired up Reflector to see how SharePoint was using it when provisioning sites based on the Publishing Portal site definition.
Take a minute to check out the Publishing Portal template - you can find this in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\XML\webtempsps.xml:
<Template Name="BLANKINTERNETCONTAINER" ID="52">
<Configuration ID="0"
Title="Publishing Portal"
Hidden="FALSE"
ImageUrl="/_layouts/1033/images/IPPT.gif"
Description="... omitted ..."
ProvisionAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
ProvisionClass="Microsoft.SharePoint.Publishing.PortalProvisioningProvider"
ProvisionData="xml\\InternetBlank.xml"
RootWebOnly="TRUE"
DisplayCategory="Publishing"
VisibilityFeatureDependency="97A2485F-EF4B-401f-9167-FA4FE177C6F6">
</Configuration>
</Template>
The template defines a ProvisionClass of Microsoft.SharePoint.Publishing.PortalProvisioningProvider (which is simply an implementation of SPWebProvisioningProvider) and points to xml\\InternetBlank.xml in its ProvisionData attribute.
If you examine the Provision method Microsoft.SharePoint.Publishing.PortalProvisioningProvider in Reflector, you can follow the logic that SharePoint uses to provision a Publishing site.
The Provision method calls CreatePortal, which loads and validates InternetBlank.xml against the PortalTemplate.xsd schema. CreatePortal calls CreateChildWebs which recursively creates the site hierarchy as defined in InternetBlank.xml.

InternetBlank.xml can be found at C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\InternetBlank.xml, let's take a look at its contents:
<?xml version="1.0" encoding="utf-8" ?>
<portal xmlns="PortalTemplate.xsd">
<web name="Home"
siteDefinition="BLANKINTERNET#0"
displayName="$Resources:cmscore,IPPT_Portal_Root_DisplayName;"
description="$Resources:cmscore,IPPT_Portal_Root_Description;" >
<webs>
<web name="PressReleases"
siteDefinition="BLANKINTERNET#1"
displayName="$Resources:cmscore,IPPT_Portal_PressRelease_DisplayName;"
description="" />
<web name="Search"
siteDefinition="SRCHCENTERLITE#1"
displayName="$Resources:cmscore,IPPT_Portal_SearchCenterLite_DisplayName;"
description="" />
</webs>
</web>
</portal>
If you've created an out of the box Publishing Portal, this should be familiar - InternetBlank.xml defines the Press Releases and Search Center sites that are created.
There are a couple of possible approaches you can take if you'd like to integrate this functionality into your custom site definition:
- Have your site definition use Microsoft.SharePoint.Publishing.PortalProvisioningProvider instead of writing your own SPWebProvisioningProvider
- Implement some of the logic in Microsoft.SharePoint.Publishing.PortalProvisioningProvider into your own implementation of SPWebProvisioningProvider
The approach you take really depends on your requirements. If you simply need to provision a hierarchy of sites as part of your site definition, go with #1. If you need to create those sites, but also execute more custom logic, go with #2.
I'm planning to explore a third option which involves extending Microsoft.SharePoint.Publishing.PortalProvisioningProvider to account for some more custom logic - we'll leave that for a future post though :)
Let me now demonstrate modifying AC's Minimal Publishing Portal site definition to use Microsoft.SharePoint.Publishing.PortalProvisioningProvider instead of a custom SPWebProvisioningProvider - you can download the code for AC's Professional SharePoint 2007 WCM Development book, the Minimal Publishing Portal site definition is in Chapter 5.
Let's first take a look at the WEBTEMP.PublishingMinimal.xml file for the site definition:
<?xml version="1.0" encoding="utf-8" ?>
<Templates xmlns:ows="Microsoft SharePoint">
<Template Name="PublishingMinimal" ID="10001">
<Configuration ID="0"
Title="Minimal Publishing Site"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/images/PublishingMinimal/Preview.png"
RootWebOnly="false"
SubWebOnly="true" />
<Configuration ID="1"
Title="Minimal Publishing Portal"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/images/PublishingMinimal/Preview.png"
ProvisionAssembly="Chapter05MinimalSiteDefinition, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=c591e70cfdf9ce4f"
ProvisionClass="WROX.ProMossWcm.Chapter05.ProvisioningEngine"
ProvisionData="SiteTemplates\\PublishingMinimal\\XML\\PortalConfig.xml"
RootWebOnly="true"
SubWebOnly="false" />
<Configuration ID="2"
Title="Minimal Publishing Site with Workflow"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/1033/images/PublishingSite.gif"
SubWebOnly="true"
VisibilityFeatureDependency="54A92CA1-4E7C-4B73-B03A-E93955E4E560"
Description="... omitted ... "/>
</Template>
</Templates>
The site definition includes three configurations:
- Minimal Publishing Portal - the "provisioner" for the site definition
- Minimal Publishing Site - a template for a web without workflow
- Minimal Publishing Site with Workflow - a template for a web with workflow
Let's start by pasting the contents of C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\InternetBlank.xml into PortalConfig.xml and editing the siteDefinition property of the root web to provision the appropriate configuration of the PublishingMinimal site definition:
<?xml version="1.0" encoding="utf-8" ?>
<portal xmlns="PortalTemplate.xsd">
<web name="Home"
siteDefinition="PublishingMinimal#0"
displayName="Home"
description="">
<webs>
<web name="PressReleases"
siteDefinition="BLANKINTERNET#1"
displayName="Press Releases"
description="" />
<web name="Search"
siteDefinition="SRCHCENTERLITE#1"
displayName="Search"
description="" />
</webs>
</web>
</portal>
We'll also modify WEBTEMP.PublishingMinimal.xml to use Microsoft.SharePoint.Publishing.PortalProvisioningProvider instead:
<?xml version="1.0" encoding="utf-8" ?>
<Templates xmlns:ows="Microsoft SharePoint">
<Template Name="PublishingMinimal" ID="10001">
<Configuration ID="0"
Title="Minimal Publishing Site"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/images/PublishingMinimal/Preview.png"
RootWebOnly="false"
SubWebOnly="true" />
<Configuration ID="1"
Title="Minimal Publishing Portal"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/images/PublishingMinimal/Preview.png"
ProvisionAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
ProvisionClass="Microsoft.SharePoint.Publishing.PortalProvisioningProvider"
ProvisionData="SiteTemplates\\PublishingMinimal\\XML\\PortalConfig.xml"
RootWebOnly="true"
SubWebOnly="false" />
<Configuration ID="2"
Title="Minimal Publishing Site with Workflow"
DisplayCategory="Publishing"
Hidden="FALSE"
ImageUrl="/_layouts/1033/images/PublishingSite.gif"
SubWebOnly="true"
VisibilityFeatureDependency="54A92CA1-4E7C-4B73-B03A-E93955E4E560"
Description="... omitted ... "/>
</Template>
</Templates>
While doing this, I discovered that I had to ensure that PublishingMinimal#0 and PublishingMinimal#2 also activated the SharePoint PublishingSite and PublishingWeb features. For each configuration in the site definition's ONET.xml, ensure that the features are activated.
The PublishingSite feature is scoped at the Site level and should be activated in <SiteFeatures>:
<SiteFeatures>
<!-- Feature: PublishingSite -->
<Feature ID="f6924d36-2fa8-4f0b-b16d-06b7250180fa" />
The PublishingWeb feature is scoped at the Web level and should be activated in <WebFeatures>:
<WebFeatures>
<!-- Feature: PublishingWeb -->
<Feature ID="94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb" />
That's it!
Go ahead and create a site collection based on the Minimal Publishing Portal site definition, and you'll see the Press Releases and Search sites created automatically.
Creating custom site definitions is painful enough ... Simplifying it slightly by leveraging existing SharePoint Publishing functionality to provision a portal's hierarchy will hopefully make this a little easier.
As I mentioned, I'll be exploring extending the functionality in Microsoft.SharePoint.Publishing.PortalProvisioningProvider in order to still use it, but also inject some custom functionality which is often required during provisioning - stay tuned.