Tuesday, August 12, 2008

Creating a Custom SharePoint MasterPage using a Feature

Creating a custom MasterPage is a very common requirement especially for those who want to migrate from ASP.NET 2.0 to SharePoint, without altering the look and feel they already had previously.

I have searched a lot about how to accomplish this, and found many articles and blog posts but they are all incomplete, they are missing the complete guide. I will try to be as direct as possible, and include screen shots that I hope they would help.

Anyway, lets start and get our hands dirty...


  1. Navigate to the following folder: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\
    Copy the PageLayouts folder and paste it in the same directory but Change the folder name to another one. To easily follow up with me through this post, lets call it CustomMasterPages.

     

    Add the masterpage you want in the masterpage folder, the CSS file into the styles folder, and all images you need into the images folder.
  2. Open the CustomMasterPages folder. Now we have to Edit the Feature.xml that has the main Registry info about the new feature you want to create (in our case, the custom MasterPage). So open the feature.xml file using your favorite text editor.
    Delete what the file has, and paste the following:

    <!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
    <!-- _LocalBinding -->
    <Feature  Id="77596cae-c12d-4451-9d65-53b66b2fd5aa"
              Title="CS Masterpage"
              Description="My own Custom MasterPage"
              Version="12.0.0.0"
              Scope="Site"
              Hidden="False"
              DefaultResourceFile="core"
              xmlns="http://schemas.microsoft.com/sharepoint/">
        <ElementManifests>
            <ElementManifest Location="ProvisionedFiles.xml"/>
        </ElementManifests>
    </Feature>

    You have to change only the following fields, but I advice you to leave everything as it is for this Demo, just as a good kick off :)
    ID: A GUID to identify the feature. you can go to http://www.newguid.com/ to generate a new GUID (you will find it in the upper banner) and paste it right there.
    Title: the Feature name (Custom MasterPage name).
    Description: Feature's description.
    The Element Manifest is considered as the backbone of the feature; you have to specify its location, which is by default named as ProvisionedFiles.xml located at the same level with the feature.xml. Save the file and close it.

  3. Now open the ProvisionedFiles.xml file and let's see what we have got there.
    You simply link EVERYTHING you have in the CustomMasterPages folder, and create an entry for it in the ProvisionedFiles.xml

    <!-- _lcid="1033" _version="12.0.4407" _dal="1" -->
    <!-- _LocalBinding -->
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Module Name="OSGMasterPages" Url="_catalogs/masterpage" Path="MasterPages" RootWebOnly="TRUE">
    <!--the MasterPage file that you have to put in the CustomMasterPage/MasterPages folder -->
        <File Url="MasterPage.master" Type="GhostableInLibrary">
        <Property Name="ContentType" Value="My Sample Master Page" />
    <!-- a preview image for the MasterPage file. Put the image in the CustomMasterPage/en-us folder -->
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/BlackVertical.png, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/BlackVertical.png" />
    <!-- MasterPage Description -->
    <Property Name="MasterPageDescription" Value="This is my sample master page for use with collaboration or publishing sites." />
        </File>
    </Module> <!-- a preview image for the MasterPage file. Put the image in the CustomMasterPage/en-us folder -->
    <Module Name="PublishingLayoutsPreviewImages" Url="_catalogs/masterpage" IncludeFolders="??-??" Path="" RootWebOnly="TRUE">
        <File Url="BlackVertical.png" Name="Preview Images/BlackVertical.png" Type="GhostableInLibrary">
        </File>
    </Module>
    <!-- The place where we can add the registry info of the images used in the MasterPage-->
    <Module Name="Images" Url="Style Library/Images" Path="Images" RootWebOnly="TRUE">
    <!-- All images that you added previously in the CustomMasterPage\images folder, each will have its own entry as shown below, with the same syntax except the Url, and name. Please take care that they are case sensitive-->
    <File Url="1_Duane.jpg" Name="1_Duane.jpg" Type="GhostableInLibrary"/>
    </Module> <!-- Styles, the name of the css file located in the CustomMasterPage\styles folder-->
    <Module Name="OSGStyles" Url="Style Library" Path="Styles" RootWebOnly="TRUE">
            <File Url="CS MasterPage.css" Type="GhostableInLibrary" />
    </Module> </Elements>

    Now we are done with the 2 most important pages.

  4. One important thing you must take care of, is that the masterpage design is not the same as what you used to do in ASP.NET 2.0. Its not an HTML page with some ContentPlaceHolders with random IDs and placed anywhere like before. SharePoint's MasterPages are way different; there are a number of predefined ContentPlaceHolders with specific ids (you can't change them, or they wont be recognized, and the MOSS will hit you with a silly frustrating error page!). These ContentPlaceHolders are recognized by the MOSS according to their IDs. Have a look at the complete list below:



    yet, you dont have to place ALL of these ContentPlaceHolders into your new customized MasterPage. there are some of these which are essential, and others not. The following is a minimal MasterPage that you cant remove anything from it, or it wont work at all. You can always use it as a start for designing your masterpages.. i.e NEVER START THE MASTERPAGE DESIGN FROM SCRATCH! or you will end up banging your head against the wall!

    <%-- Identifies this page as a .master page written in Microsoft Visual C# and registers tag prefixes, namespaces, assemblies, and controls. --%>
    <%@ Master language="C#" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <%@ Import Namespace="Microsoft.SharePoint" %>
    <%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register TagPrefix="wssuc" TagName="Welcome" src="~/_controltemplates/Welcome.ascx" %>
    <%@ Register TagPrefix="wssuc" TagName="DesignModeConsole" src="~/_controltemplates/DesignModeConsole.ascx" %>
    <%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>
    <%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>
    <%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>
    <%-- Uses the Microsoft Office namespace and schema. --%>
    <html>
      <WebPartPages:SPWebPartManager runat="server"/>
      <SharePoint:RobotsMetaTag runat="server"/>   <%-- The head section includes a content placeholder for the page title and links to CSS and ECMAScript (JScript, JavaScript) files that run on the server. --%>
      <head runat="server">
        <asp:ContentPlaceHolder runat="server" id="head">
          <title>
            <asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server" />
          </title>
        </asp:ContentPlaceHolder>
        <Sharepoint:CssLink runat="server"/>
        <asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server" />
      </head>
      <%-- When loading the body of the .master page, SharePoint Server 2007 also loads the SpBodyOnLoadWrapper class. This class handles .js calls for the master page. --%>
      <body onload="javascript:_spBodyOnLoadWrapper();">
        <%-- The SPWebPartManager manages all of the Web part controls, functionality, and events that occur on a Web page. --%>
        <form runat="server" onsubmit="return _spFormOnSubmitWrapper();">
          <wssuc:Welcome id="explitLogout" runat="server"/>
          <PublishingSiteAction:SiteActionMenu runat="server"/> 
          <PublishingWebControls:AuthoringContainer id="authoringcontrols" runat="server">
            <PublishingConsole:Console runat="server" />
          </PublishingWebControls:AuthoringContainer>
          <%-- The PlaceHolderMain content placeholder defines where to place the page content for all the content from the page layout. The page layout can overwrite any content placeholder from the master page. Example: The PlaceHolderLeftNavBar can overwrite the left navigation bar. --%>
          <asp:ContentPlaceHolder id="PlaceHolderMain" runat="server" />
            <asp:Panel visible="false" runat="server">
            <%-- These ContentPlaceHolders ensure all default SharePoint Server pages render with this master page. If the system master page is set to any default master page, the only content placeholders required are those that are overridden by your page layouts. --%>
                <asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderPageTitleInTitleArea"  runat="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderPageImage" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderBodyLeftBorder" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderNavSpacer" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderTitleLeftBorder" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderTitleAreaSeparator" runat="server"/>
                <asp:ContentPlaceHolder ID="PlaceHolderMiniConsole" runat="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderCalendarNavigator" runat ="server" />
                <asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat ="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat ="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderBodyAreaClass" runat ="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderTitleAreaClass" runat ="server"/>
                <asp:ContentPlaceHolder id="PlaceHolderBodyRightMargin" runat="server" />
            </asp:Panel>
        </form>
      </body>
    </html>

    So to start, you can get whatever HTML you have in the old MasterPage and paste it in here, just below the </asp:Panel>. Then arrange the ContentPlaceHolders wherever you want into your HTML code.
    Another point you must take care of; the images and background's source urls. of course, we added the images previously into the CustomMasterPages\images folder, so we will replace the old source urls you had in the html code into something like:
    background="/Style%20Library/images/bg1.jpg">
    and for sure, that would be the same case in the CSS file.

  5. Now we are ready to install the feature!
    open the command prompt (Start> Run), then type:
    cd C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN
    Now type the following
    stsadm -o installfeature -filename CustomMasterPages\feature.xml
    We are installing the feature that we are providing its feature.xml file (the xml file's path from the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES folder).
  6. Activate the feature for your site.
    open the Internet Browser, and type in the webapplication's URL.. something like http://ahmed-ig:35193 and concatenate /_layouts/settings.aspx to the previous part.
    i.e: http://ahmed-ig:35193/_layouts/settings.aspx

    under the "Site Collection Administration" column, click on "Site collection features"

     

    locate the Feature you have just made. (you will find its name as you named it in the feature.xml file)

     

    Now Press on the Activate Button to activate the Feature.

  7. Apply the MasterPage into your site
    Now get back to the url you opened in Step #7
    and under the "Look and Feel" column, click on "Master page".
    Choose the Site Master Page as the customized MasterPage you have created.
    and leave the System Master Page as it is.
    Now you have to upload the CSS file you are using in your customized MasterPage file.

     

And Hopefully, that's it! Hope it works smoothly without any problems :)