A while ago I stumbled upon a serious design limitation regarding Content Types and centralized document templates. What then followed was a series of testing, phone calls with Microsoft, finding alternative solutions and deep dive into Office Open XML.
Request from the customer
“We want to use MOSS 2007 to create a collaboration site per project for our 400+ projects. These collaboration sites all use the same Content Types and document templates. We want to centrally manage those document templates so that we don’t need to make the same change 400+ times.”
Approach
Due to sizing we architected a solution with a dozen of Site Collections that would each hold a collection of project sites. We ‘Feature’-ized our Content Types and Site Columns so that they could quickly be activated on all Site Collections and used by the child sites. Document templates would be stored in a central document library and we would link to them in the Content Types on the project sites.
First issue
Linking to document templates really doesn’t play well with the Document Information Panel (DIP). I have blogged about this here:
Centralizing Document Templates in a library- Document Information Panel shows incorrect properties
We proposed a solution where the document templates in the central library would be pushed to the Content Type resource folder on site level. The code to perform the push would have to connect to the Site Collections, copy the template to the resource folder (http://sitecollectionurl/_cts/contenttypename) and link template and Content Type together.
When a Site Content Type is associated with a List it will be a List Content Type inheriting from the Site Content Type and the document template will be copied to the List Content Type resource folder (http://sitecollectionurl/listurl/Forms/contenttypename).
Second issue
Did I tell you that the column values (metadata) have different values based on the project site ? So when a project site is created we automatically update the List Content Type Column default value with the values for that specific project site. Unfortunately this is not supported when working with Office 2007 file formats because they only react on changes to the Site Column.
Consider the following scenario:
1) Set up a document library with a Content Type that has a text column with a default value
2) Upload a new .doc or .docx as Content Type template
TEST 1) Create a new document:
.DOC: the DIP will contain the text column with the default value
.DOCX: the DIP will contain the text column with the default value
3) In SharePoint, modify the default value of the text column
TEST 2) Create a new document:
.DOC: the DIP will contain the text column with the updated default value
.DOCX: the DIP will contain the text column with the original default value
Microsoft confirmed that this is by design.
Third issue
When designing our document templates with Content Controls mapped to our SharePoint fields we didn’t know that internally in the DOCX file it uses a GUID for mapping the Content Control with the SharePoint Metadata XML. For fields (Site Columns) created in the UI or through API this is the SPWeb.ID of where they were created. For fields created declaratively through Features this is the SPList.ID of where their Content Type is associated to.
So some things to notice
- Creating a single document template with Content Controls mapped to your declaratively added Fields cannot be used in two different Document Libraries because the Content Controls lose the connection with Field (because the ID of the List is different and not updated in the Content Control)
- The solution here is to create your fields in the UI or through the API (this could be in a Feature Activating event)
- Copying a document template across Site Collections means different Web ID’s so it also affects fields created in the UI or the API
Finally
In the end we wrote some wrapper classes for Office 2007 file formats using System.IO.Packaging that would manipulate our document templates once they were copied over to a different Site Collection. We also rewrote our Features to create our Fields through the API (SPWeb.Fields.AddFieldAsXml()).
- Remove the SharePoint Metadata XML so that association of the Content Type to a List it would be regenerated automagically
- Loop through every Content Control and find to which Field they were mapped using information in it’s XPath. Then we would update the GUID’s in the Content Control to match the SPWeb.ID
Next time I’ll definitely take these design limitations into account. Lessons learned I’d say !
I would have preferred if Win7 RTM came sooner so that I could avoid migrating from Beta to RC and then from RC to RTM but no point to keep on whining about it :) So I decided to install the 64 bit issue of Windows 7 Release Candidate. I love how smooth those Win7 installs are. Very little interaction is required and the system correctly detects and installs my hardware.
Once up and running I logged in into my Outlook Web Access (Exchange 2007) and noticed a lot of flickering and errors in javascript files. Here’s an example:
Exception Details
-----------------
Date: Wed Jul 22 20:41:09 UTC+0200 2009
Message: Unspecified error.
Url: https://webmail.company.be/owa/8.1.240.5/scripts/premium/cdayvw.js
Line: 181
Tried the compatibility mode, tried the no-addons approach, no luck. However then I noticed there was another link in the Start Menu for Internet Explorer that read “Internet Explorer (64-bit)”. I tried it and poof: OWA behaved as a perfect citizen ! Joy !
If you have MOSS 2007 than you get the Site Manager for advanced management and with it the “Content and Structure Reports” (http://<server>/Reports List).

You can easily display the results of a report through the Site Actions menu. You can also add custom reports since the underlying technology is SPSiteDataQuery and the syntax to use is CAML. For an overview see Vince’s blog post.
If you have issues displaying results then remember you can use the Diagnostic Logs feature in SharePoint to troubleshoot. If there’s an issue the default logging level will pick up the Unexpected entry, but if you need additional info you can configure the logging level in Central Administration. You will need to be looking into the “Site Management” event category.

For example; if you portal has more than 1000 lists then by default the Reports won’t return any results. Use the logs to diagnose the issue:
06/29/2009 20:17:40.34 w3wp.exe (0x0960) 0x0A3C CMS Site Management 622h Unexpected SMReportsData GetQueryResults - Query Execution threw SPException: The query cannot be completed because the number of lists in the query exceeded the allowable limit. For better results, limit the scope of the query to the current site or list or use a custom column index to help reduce the number of lists.
06/29/2009 20:17:40.34 w3wp.exe (0x0960) 0x0A3C CMS Site Management 6oz4 Medium SmtPerf: SMDataSource.PopulateQueryResultsDataSet(SHAREPOINT\system) - Elapsed time= 00:00:00.0500720
06/29/2009 20:17:40.34 w3wp.exe (0x0960) 0x0A3C CMS Site Management 6oz4 Medium SmtPerf: SmtMainPage.OnPreRenderComplete - accumulated(SHAREPOINT\system) - Elapsed time= 00:00:00.1802592
You can override the Lists settings by editing each report item and specifying the CAML List Type as follows: <Lists MaxListLimit=”0” />
(Make sure not to misspell the attribute: info)

Note that this does have a performance impact on your environment: Additional performance and capacity planning factors
Good luck !
Today I got into some code reviewing of an Item Event Receiver using Enterprise Library for data access. The problem occurred when registering the Event Receiver for a SharePoint List using the object model (SPList.EventReceivers.Add)
Exception has been thrown by the target of an invocation.
Here's a simplified view into the code:
public class MyEventReceiver: SPItemEventReceiver
{
MyDataAccess da = new MyDataAccess();
public override void ItemAdded(SPItemEventProperties properties)
{
...
}
}
public class MyDataAccess
{
public MyDataAccess()
{
Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase("MyDB");
}
...
}
Apparently when the Event Receiver is registered it instantiates the MyDataAccess class which in turn calls the EntLib method in its constructor. This is what causes the exception.
A solution is to instantiate the MyDataAccess class in the methods rather than on event receiver initialisation:
public class MyEventReceiver: SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
MyDataAccess da = new MyDataAccess();
...
}
}