Provisioning Publishing Pages using Features declarative markup
If you’re following me on Twitter you probably know that since a couple of weeks I’m working on a brand new Web Content Management (WCM) solution based on Microsoft Office SharePoint Server (MOSS) 2007. One of the things you do in every WCM solution is creating your custom branding and including it in your WSP package.
You can provision all kinds of assets using Modules in Element Manifest. If you want to provision a Page Layout for example, you could use the following XML snippet (copied from the PublishingLayouts Feature):
<File Url="ArticleLeft.aspx" Type="GhostableInLibrary"> <Property Name="Title" Value="$Resources:cmscore,PageLayout_ArticleLeft_Title;" /> <Property Name="MasterPageDescription" Value="$Resources:cmscore,PageLayout_ArticleLeft_Description;" /> <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" /> <Property Name="PublishingPreviewImage" Value="~SiteCollection/.../Preview Images/ArticleLeft.png..." /> <Property Name="PublishingAssociatedContentType" Value=";#Content Type;#0x010100C568DB52D9D0A14D9B2FDCC9666...;#"/> </File>
While writing the provisioning script for the custom branding we have made, I have stumbled upon the following question: how is a Page Layout different than a Publishing Page? They are both documents (SPListItem + SPFile) are both stored in a Document Library and are both associated with a Content Type. Could you possibly provision a Publishing Page exactly the same way as you provision Page Layouts and Master Pages?
Proof of concept
To check it out I have decided to create a simple proof of concept. Almost immediately I have stumbled upon a challenge. While provisioning a Page Layout or a Master Page you have to point to an .aspx file which contains the contents. But what are the physical contents of a Publishing Page? The actual content is being stored within the underlying list item and the presentation is being done using a reference to the Page Layout. So what does the SPListItem.File contain?
Using a couple of lines of code I have extracted the contents of the File property of a Publishing Page which I have created using the ordinary Site Actions > Create Page menu. This is what I’ve found inside:
For the purpose of the proof of concept I have decided to simplify the Template Page (as I call the .aspx file used to provision Publishing Page) and have brought the markup down to (the Page directive has been broken into multiple lines for brevity. In your .aspx Template Page it should be in a single line):
<%@ Page Inherits="Microsoft.SharePoint.Publishing.TemplateRedirectionPage, Microsoft.SharePoint.Publishing,Version=188.8.131.52,Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Reference VirtualPath="~TemplatePageUrl" %> <%@ Reference VirtualPath="~masterurl/custom.master" %>
To check whether it works I have created a simple Feature:
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Id="E5EB4D84-43C2-4093-870B-B341CB60EA1F" Title="Deploy Publishing Page" Description="" Scope="Web" Hidden="False" Version="184.108.40.206"> <ElementManifests> <ElementManifest Location="Elements.xml" /> <ElementFile Location="TemplatePage.aspx"/> </ElementManifests> </Feature>
and an Elements.xml file to provision the contents (some lines were broken for brevity):
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Module Name="Pages" Url="$Resources:cmscore,List_Pages_UrlName;"> <File Name="Page1.aspx" Url="Page1.aspx" Path="TemplatePage.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE"> <Property Name="Title" Value="Lipsum"/> <Property Name="PublishingPageLayout" Value="~SiteCollection/_catalogs/masterpage/MyLayout.aspx, /_catalogs/masterpage/MyLayout.aspx"/> <Property Name="ContentType" Value="My page CT" /> <Property Name="ContentTypeId" Value="0x010100C568DB52D9D0A14D9B2FDCC96666E9F200794..." /> <Property Name="PublishingPageContent" Value="Lorem <strong>ipsum</strong> <em>test add</em>" /> </File> </Module> </Elements>
After installing and activating the Feature it has properly created an instance of a Publishing Page! Cool!
So why is it so cool?
Personally I see the added value for this feature for at least two purposes.
Provisioning configuration/content in a structured and repeatable way
Speaking for myself: the customers I had a change to interact with would dislike the idea of buying a solution and getting a box of building blocks instead. In fact they expect a preconfigured environment where they can start publishing content in.
You could argue whether it’s right or wrong to do that and if it was better to let the customer do the job. Personally I compare it to the Windows and Linux experience from the old days. While you had to do everything manually on Linux, Windows presented you with a “nice” installer and after the installation was completed you had a ready-to-use operating system.
If you wanted to provide your customers with a preconfigured environment “the-old-way” you would probably use custom code to create and configure the Publishing Pages programmatically. The downside is that mixing the configuration with code isn’t a particularly good idea and might get difficult to keep overview after some time. Being able to provision the same content and configuration in a declarative way is easier to read and maintain in my opinion and is less prone to errors.
Provisioning test content
How many times did you have a customer who wanted to start entering some test content in the very first releases and then was annoyed with having to start from scratch after each new release. I face this in almost every single project. So is it once again about educating your customers or is it just common-sense?
Having test content is not just a gesture towards your customers in my opinion. Imagine having test content covering your use-cases, like testing paging, filters, etc in different scenarios. Wouldn’t it be cool to have a set of Features which would provision test content upon activation?
Using the approach I presented above you are able to create Features with test content in an intuitive way. I’m quite sure that having test content during your development process will help you test your stuff. And after the first release you could even include some of the test content create by your customer!
Provisioning Publishing Pages using Features has unfortunately some limitations. So far I have found two.
Content Type must be bound to the Pages Library
The very first thing I’ve found that if you’re using some custom Content Type to base your Publishing Page on, be sure that it’s been bound to the Pages Library before provisioning that Publishing Page using a Feature.
You will not experience any problems while creating a Publishing Page using Site Actions > Create Page. The first you do it, SharePoint checks whether the particular Content Type is available within the Pages Library and attaches it if necessary. When using Features however you have to take care of the plumbing yourself, meaning you have to create a Feature which will associate the Content Type with the Pages Library either declarative or using custom code.
When I provisioned a Publishing Page using Feature XML using Content Type that wasn’t associated with the Pages Library that while the page has been created without any errors it was of wrong Content Type. So far I didn’t find out how SharePoint determines which Content Type to apply if the one you chose isn’t available. The fact is that you want your Content Type to be associated with the Pages Library before provisioning any Publishing Pages using Feature XML.
It’s for initial provisioning only
Using Features you can only provision new Publishing Pages. Any changes made to existing Publishing Pages won’t be provisioned. As far as I can tell this has to do with the IgnoreIfAlreadyExists attribute of the File element. If set to FALSE Feature activation will fail if the Publishing Page already exists. If set to TRUE however, the Feature activation process will skip processing all elements that already exist.
As far as I know the only exception to this is provisioning Web Parts using the AllUsersWebPart element. Each time you activate the Feature, a new instance of the Web Part will be added to the Publishing Page. At least that’s the case when provisioning Page Layouts.
How reliable is it?
So far I haven’t heard of anyone provisioning Publishing Pages using Features XML. I doubt that I’m the only one who has ever thought about it, so that brings me to the following question: is it a supported approach? As long as you’re using it for provisioning test content it doesn’t matter that much but if you would like to provision configuration of your site with it, it would be great to know the official position of Microsoft on this one.
The approach I have described in this article uses nothing else than the features available in a standard SharePoint installation. There is no reflection being applied and even no custom code at all. You might assume it’s not unsupported but it would be great if someone could confirm it.
I really love the idea of being able to provision configuration and content using nothing else than Feature XML. In fact I’m so impressed by it that I’m working on a tool which will help you export the existing Publishing Pages to Feature Manifest, so all you’ll have to do is to create a Feature XML – sounds cool doesn’t it? I want the tool to work both in the context of the current Publishing Page (a custom button on the Page Editing Toolbar) and as a custom STSADM extension which will allow you to export multiple Publishing Pages at once.
I really hope I will be able to finish the tool soon. It will become available on CodePlex once it’s ready for testing. Stay tuned for updates and don’t hesitate to let me know if you have any feedback or experience with provisioning Publishing Pages using Feature XML.