I recently had a requirement to duplicate (at least in part) the publishing architecture of MOSS using WSS. Specifically, the user wanted to be able to deploy web part pages to a specific document library when a new site was provisioned and add web parts to those pages automatically. This allows the user to browse links to web part pages with content editor web parts or other content just as they would with a publishing page in MOSS. Not a bad way to get some publishing features on a tight budget.
As usual, when I’m trying to solve a problem related to WCM (MOSS Publishing), I start at Andrew Connell’s blog then work my way towards the solution from there. AC had a timely post on deploying web parts as part of a file push in a feature, which solved the last bit of my problem, and actually provisioning the files was a no-brainer since we do it all the time with master pages, but how was I going to actually create the list that I needed in the first place?
The answer lies with the <ListInstance> element. When used in a feature, this element allows you to specify a new instance of an existing list type (as opposed to <ListTemplate> which creates a new list type altogether), while the <Module> and < File> elements provide the capability to deploy the actual files. So to provision the list and deploy the web part files, I first created the following feature:
<?xml version=”1.0″ encoding=”utf-8″?>
<Feature Id=”35531D23-885C-4b3e-83D3-F61ED171C2BB” Title=”Portal Site Web Part Pages” Version=”18.104.22.168″ Scope=”Web” Hidden=”FALSE”xmlns=”http://schemas.microsoft.com/sharepoint/”>
<ElementManifest Location=”ListCreation.xml” />
<ElementManifest Location=”WebPartPages.xml” />
The two <ElementManifest> nodes point to the configurations for the list creation and file deployment, respectively. The first, ListCreation.xml, specifies the list to create and specifies the appropriate properties:
<ListInstance Description = “Portal Pages Document Library” FeatureId =”00BFEA71-E717-4E80-AA17-D0C71B360101″ Id = “901” OnQuickLaunch = “TRUE”RootWebOnly = “FALSE” TemplateType = “101” Title = “Portal Pages” Url = “Portal Pages” />
There are several key settings in this file:
FeatureId – This one is a bit tricky. The GUID value for this setting must match the value for theDocumentLibrary feature deployed as part of the site definition. This value can be located in the ONET.XML file (\12\SiteTemplates\STS\XML\ONET.XML if using the default Team Site definition) in the<Configuration> node. The first list provisioned is a document library (Shared Documents) that uses the base DocumentLibrary feature:
<List FeatureId=”00BFEA71-E717-4E80-AA17-D0C71B360101″ Type=”101″Title=”$Resources:core,shareddocuments_Title;”Url=”$Resources:core,shareddocuments_Folder;”QuickLaunchUrl=”$Resources:core,shareddocuments_Folder;
The FeatureId property identifies the base feature used to provision the document library – copy this into the FeatureId of the ListCreation.xml file. As an alternative, you could add a <List> node and create the list directly from within ONET.XML but that would tie the functionality to a specific site definition; by using a feature instead, the functionality can be shared and even deployed to existing site collections.
Id – This is an arbitrary integer that must be unique compared to other list instances using the same base feature. The larger the number, the less chance of a conflict.
RootWebOnly – Setting this to “TRUE” means the list will only be deployed at the site collection root; set it to “FALSE” to deploy to any subweb.
TemplateType – Specifies the type of list, in this case “101” for Document Library.
Title – This is the name of the list that appears on the View Lists page.
URL – This is the actual list name as seen in the browser address field; it can be the same as theTitle property but does not have to be (many people like to avoid spaces in URL’s to make them more user friendly). Bear in mind that Document Libraries are found in the root of the site, unlike Announcements, Contacts, etc., which are usually in the /Lists/ folder.
The second <ElementManifest> node invokes the WebPartPages.xml file which deploys the files and specifies the web parts to include on the page.
<Module Name=”HCPracticePortalWebPartPages” List=”101″ Url=”Portal Pages”>
<File Url=”BlankPortalPage.aspx” Name=”NewPage.aspx” Type=”GhostableInLibrary”>
<AllUsersWebPart WebPartZoneID=”Top” WebPartOrder=”1″><![CDATA[ <WebPart xmlns=”http://schemas.microsoft.com/WebPart/v2″ xmlns:iwp=”http://schemas.microsoft.com/WebPart/v2/Image”><Assembly>Microsoft.SharePoint, Version=22.214.171.124, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><TypeName>Microsoft.SharePoint.WebPartPages.ImageWebPart</TypeName><FrameType>None</FrameType> <Title>$Resources:wp_SiteImage;</Title><iwp:ImageLink>/_layouts/images/homepage.gif</iwp:ImageLink><iwp:AlternativeText>$Resources:core,sitelogo_wss;</iwp:AlternativeText></WebPart>]]>
Just as with Master Page features, the <Module> element specifies the name of the Feature, the list type the files will be deployed to (“101” for a Document Library), and the URL (not the Title – these may be different) of the target list. Within the <Module> node are <File> elements for each file to be created and inserted into the list. There are several important properties for this element:
Url – This is the toughest one to get a handle on. When a file is inserted into a document library, the system does not actually copy the file itself, it creates an instance of that file in the library instead. This is an important distinction – the Url property does not reference the actual file that will be deployed but rather the source file from which the new file will be generated, hence the difference in the Url and Name properties. As this feature deploys a web part page, we first need to locate the source file we want to use and copy it into the feature folder. WSS web part page templates can be found in the \12\TEMPLATE\1033\STS\DOCTEMP\SMARTPGS directory as ‘SPSTD1.aspx’ through ‘SPSTD8.aspx’ (Note that these are the same files that you can select from the ‘Create’ page in a Team Site). Select the one you want, copy it into the feature folder, and rename it to something a bit more descriptive. Then use the new name as the value of the Url property.
Name – This is the final name of the file as it will appear in the new list.
Type – There are two valid types – “Ghostable” and “GhostableInLibrary“. The former is used for deploying files outside of SharePoint lists (such as virtual directories or other file-based locations) and the latter is used to create a list item for the file and insert it into the target list. Note that a relationship to the originating file still exists until the file is edited and re-uploaded (i.e. with SharePoint Designer) – changes to BlankPortalPage.aspx (which I renamed from SPSTD3.aspx) will be reflected in any uncustomized files which use that as a base.
AllUsersWebPart – This specifies the actual web part to deploy and which zone to put it in. The web part markup must be encapsulated in a [CDATA] block. If you don’t already have it, get the markup by exporting a web part from an existing site to your local machine then open the file and copy the XML (the sample above is for a standard Image Web Part – settings will vary depending upon the web part).
When all the properties have been applied, copied the entire folder (including the base .aspx page) into the \12\Features\ folder on each front-end web server. Install the feature using STSADM (activation is not required; the feature will be activated automatically when the site is provisioned). Next, edit the ONET.XML file in your custom site definition and add a feature reference in the<WebFeatures> element of the <Configurations> node, like so:
<!– Portal Web Part Pages –>
<Feature ID=”35531D23-885C-4b3e-83D3-F61ED171C2BB” />
Save the ONET.XML file and perform an IISRESET. The next time a site is created using the specified site definition the list instance will be created, the files automatically deployed, and the web parts added to the correct zones.