Introducing SPUrlExpressionBuilder to resolve SharePoint URL Tokens


Introduction

One of the great things when developing with SharePoint is being able to use URL tokens such as:-

  • ~SiteCollection – this will resolve to the current site collection root
  • ~Site – this will resolve to the current web root

For an extensive list of SharePoint URL tokens, take a look at Namwar Rizvi’s SharePoint Insight blog post, http://sharepoint-insight.com/2008/12/01/list-of-sharepoint-url-tokens/.

If you have done much with creating or modifying Master Pages or Page Layouts then these tokens become very useful to locate resources such as images, XSL or CSS files.

For example, to reference an image which is stored in a SharePoint Publishing web’s Site Collection Images folder you could use something like this:-

~SiteCollection/SiteCollectionImages/myimage.png.

Recently, I have been working with multiple language user interfaces and wanted to be able to display a different image based on the language. One of the steps to do this was to work out how to process these URL Tokens.

Solution

This is where the SPUrlExpressionBuilder class comes to the rescue. This class is part of the Microsoft.SharePoint.Publishing.WebControls assembly.

To evaluate a string with a url token like ~SiteCollectionUrl/Images/MyImage.png you can use the following SPUrlExpressionBuilder class’s method EvaluateUrlExpression().

string imagePath = SPUrlExpressionBuilder.EvaluateUrlExpression("~SiteCollection/Images/myimage.png").ToString();

 

Creating List Instance: Object Reference not set to an instance of an object


Introduction

This one is a bit of an old issue but some of my posts are just to remind me of a time that I got stuck in the past. Also if it helps someone else then great.

It is possible to create a feature which creates an instance of a SharePoint List when its activated. However, sometimes you only want a list to be created on the Root Web of a site collection.

Issue

Well the XML for specifying a ListInstance as a feature has an attribute called RootWebOnly which I thought I could use to only allow the ListInstance to be created on the RootWeb.

Unfortunately when you do that you get a message like the following in your SharePoint ULS Logs when the feature is activated:-

Feature Activation: Threw an exception, attempting to roll back. Feature ‘FeatureName’(ID: ‘xxxxxxxx-xxxx-xxxx-xxxxxxxx). Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SharePoint.SPListInstanceElement.ElementActivated(SPFeaturePropertyCollection props, SPSqlCommand sqlcmdAppendOnly, SPWebApplication webApp, SPSite site, SPWeb web, Boolean fForce)
at Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionListInstances(SPFeaturePropertyCollection props, SPSite site, SPWeb web, Boolean fForce)
at Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionElements(SPFeaturePropertyCollection props, SPWebApplication webapp, SPSite site, SPWeb web, Boolean fForce)
at Microsoft.SharePoint.SPFeature.ProvisionElements(SPFeaturePropertyCollection props, SPWebApplication webapp, SPSite site, SPWeb web, Boolean fForce)
at Microsoft.SharePoint.SPFeature.Activate(SPSite siteParent, SPWeb webParent, SPFeaturePropertyCollection props, Boolean fForce)

The offending Feature ListInstance element is below:-

 1: <?xmlversion="1.0" encoding="utf-8"?>
 2: <Elements
 3: xmlns="http://schemas.microsoft.com/sharepoint/">
 4: <ListInstance
 5: Id="AListId"
 6: Title="A List"
 7: Description="A List 
 8: Description”
 9: Url="Lists/ListUrlHere"
 10: OnQuickLaunch="FALSE"
 11: RootWebOnly=”TRUE”
 12: FeatureId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
 13: TemplateType="45100"></ListInstance>
 14: </Elements>

Solution

To fix the feature activation remove the RootWebOnly=”TRUE” from your ListInstance element manifest file and instead move the RootWebOnly=”TRUE” into your List’s schema.xml.

To ensure that the list is only provisioned at the root web of the site collection change the Feature.xml to use Scope=”Site”

Update:

Thanks to Dennis who picked up my mistake in this post which stated that the RootWebOnly attribute should be moved to the feature.xml.