Easy SharePoint site branding with the ASP.NET ExpressionBuilder 

Branding a SharePoint site is usually a case of deciding which content is common for different brands and which content is really different. If at this point you find that there really is common content and that the only difference you want to express for that content is styling and a few optical tweaks, you will find that the challenge is then to maintain that in a single place. What you don´t want is duplicating that common information across different sites, with the risk of stray site differences or a lot of extra work for every simple change. The solution that SharePoint offers is content replication, this would then require defining the different brand sites and deploy content from a central 'mother' site.

As an alternative I will propose here to consider branding by defining you own ExpressionBuilder class and using that across pages and of course in the master page. SharePoint offers its own SPUrlExpressionBuilder in the form of $SPUrl. You can find it used in the default publishing templates like this:

 

    <SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/~language/Core Styles/Band.css%>" runat="server"/>

    <SharePoint:CssRegistration name="<% $SPUrl:~sitecollection/Style Library/~language/Core Styles/controls.css %>" runat="server"/>

    <SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/zz1_blue.css%>" runat="server"/>

...

The SharePoint SPUrlExpressionBuilder is documented and interprets the the expression you pass it by replacing ~SiteCollection with the site collection Url and the ~language expression with the current locale for the site. The easiest solution to branding would be introducing your own ~brand expression. That would allow you to simply add your own branding css style:

<SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/MyProduct/~brand/Brand.css%>" runat="server"/>

Or you could reference your branded images like this:

        <asp:Image ID="Logo" runat="server" CssClass="logo" ImageUrl="<%$SPUrl:~SiteCollection/Style Library/MyProduct/Images/~brand/logo.gif%>" ToolTip="Logo " ></asp:Image>

You can trace it to the Microsoft.SharePoint.Publishing dll and you can even reference the Expression builder in the Microsoft.SharePoint.Publishing.WebControls.SPUrlExpressionBuilder. If you then dive into its code with Reflector you will be disappointed by finding that it cannot be extended to interpret your expressions for branding and that it is a sealed piece of code. This leaves no other option than use containment of the .SPUrlExpressionBuilder class and extend it by introducing some extra code. Here is my version:

using System;
using System.Collections.Generic; using System.Text;
using System.Web.Compilation;
using System.Web;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Publishing.WebControls;
using Microsoft.SharePoint.Utilities;
using System.Security.Permissions;
using System.CodeDom;
using System.ComponentModel;
using System.Web.UI;
 
/// <summary>
/// This class allows the brand to be part of a url expression. It is based on the SPUrl expression builder object and extends it with
/// with specific branding for your product.
/// </summary>
[ExpressionPrefix("MySPUrl")]
public class MySPUrlExpressionBuilder: ExpressionBuilder
{
   const string BrandPlaceholder = "~brand"; 
 
    // Contains base class, because it is a sealed class 
   private SPUrlExpressionBuilder builderBase; 
 
   /// <summary> 
   /// Replace placeholders with relevant data 
   /// </summary> 
   /// <param name="expression"></param> 
   /// <returns></returns> 
   public static object EvaluateUrlExpression(string expression) 
  
      string serverRelativeUrlFromPrefixedUrl =   SPUtility.GetServerRelativeUrlFromPrefixedUrl(expression); 
 
      for (int i = serverRelativeUrlFromPrefixedUrl.IndexOf(BrandPlaceholder, 0, StringComparison.OrdinalIgnoreCase); 
i > 0; i = serverRelativeUrlFromPrefixedUrl.IndexOf(BrandPlaceholder, 0, StringComparison.OrdinalIgnoreCase)) 
     
         StringBuilder builder = new StringBuilder(serverRelativeUrlFromPrefixedUrl.Substring(0, i)); 
         int PlaceholderLength = BrandPlaceholder.Length; 
 
         builder.Append(GetBrand()); // You need to find a way to get the brand at run time, maybe from the host headers 
         if ((i + PlaceholderLength) < serverRelativeUrlFromPrefixedUrl.Length) 
        
               builder.Append(serverRelativeUrlFromPrefixedUrl.Substring(i + PlaceholderLength)); 
           
            serverRelativeUrlFromPrefixedUrl = builder.ToString(); 
        
         return serverRelativeUrlFromPrefixedUrl; 
     
 
      /// <summary> 
      /// Default constructur 
      /// </summary> 
      public MySPUrlExpressionBuilder() 
     
         builderBase = new SPUrlExpressionBuilder(); 
     
 
      /// <summary> 
      /// Implements the abstract base method to return the string as built by SPUrl and additional replacements 
      /// </summary> 
      /// <param name="target"></param> 
      /// <param name="entry"></param> 
      /// <param name="parsedData"></param> 
      /// <param name="context"></param> 
      /// <returns></returns> 
      public override object EvaluateExpression(object target, System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) 
  
      string expression = (string)builderBase.EvaluateExpression(target, entry, parsedData, context); 
 
         if (expression != null) 
            return EvaluateUrlExpression(expression.ToString()); 
         return null; 
     
 
      /// <summary> 
      /// Implements the abstract base method to return a reference to a static evaluator method 
      /// </summary> 
      /// <param name="entry"></param> 
      /// <param name="parsedData"></param> 
      /// <param name="context"></param> 
      /// <returns></returns> 
      public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) 
  
      if (entry == null) 
         return null; 
      CodeExpression[] parameters = new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()) }; 
      CodeExpression expression = new       CodeMethodInvokeExpression(new    CodeTypeReferenceExpression(base.GetType()), "EvaluateUrlExpression", parameters); 
 
      if (entry.PropertyInfo == null) 
     
         return expression; 
     
      PropertyDescriptor descriptor = TypeDescriptor.GetProperties(entry.DeclaringType)[entry.PropertyInfo.Name]; 
      return new CodeCastExpression(descriptor.PropertyType, expression); 
  
 
   /// <summary> 
   /// Implements the abstract base method to signify that this class supports an evaluate method 
   /// </summary> 
   public override bool SupportsEvaluate 
  
      get 
     
         return true; 
     

 

If you put this in a dll and turn it into a SharePoint solution, you should be in business. Of course an expression builder needs to be declared in the expressionBuilder section of the web.config of the site, just like the other expression builders:

 

<compilation batch="false" debug="false">

<expressionBuilders>

<add expressionPrefix="MySPUrl" type="MyCompany.MyProduct.MySPUrlExpressionBuilder, MyCompany.MyProduct, …" />

</expressionBuilders>

</compilation>

After this you will finally be able to do easy branding:

<SharePoint:CssRegistration name="<% $MySPUrl:~SiteCollection/Style Library/MyProduct/~brand/Brand.css%>" runat="server"/>

And reference your branded images like this:

        <asp:Image ID="Logo" runat="server" CssClass="logo" ImageUrl="<%$MySPUrl:~SiteCollection/Style Library/MyProduct/Images/~brand/logo.gif%>" ToolTip="Logo " ></asp:Image>

Have your way with the code and have fun!

Posted on 29-09-2008 by Wim The
0 Comments  |  Trackback Url  |  Link to this post
Tags: Sharepoint

Links to this post

Comments

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation