Tuesday, November 9, 2010

Creating Web Templates From Publishing Enabled Sites in SharePoint 2010

Creating web templates from sites using SharePoint 2010 and Visual Studio 2010 is a fairly straightforward process, so long as you don’t try to do it with a site that has publishing enabled. The basic steps are:

  • Save your site as a template with content included (Site Actions –> Site Settings –> Save Site as Template)
  • Download the saved template from the template gallery
  • In Visual Studio 2010, create a new project of type “Import SharePoint Solution Package
  • Run through the wizard, selecting your saved template as the wsp file when prompted.
  • Right click / Deploy

By default, that will create a sandboxed solution that you can then use to create sites within the site collection where the solution was deployed. With two simple changes, you can make it a farm solution that can then be used to create new site collections as well:

  • Change the Visual Studio solution to a farm solution – either chose Farm solution when creating the project or set the project’s Sandboxed Solution property to False after the project has been created
  • Change the scope of the Web Template Feature (Feature3 by default) to Farm.

Now, your deployed web template will show up in the site template gallery when creating any site or site collection.

Now, what if you want to perform this on a site with publishing enabled? You’ll hit several road blocks…

What do you mean publishing enabled?

First things first – to enable publishing on a site, you need to enable two features, one on the site collection, and one on the web. At the site collection scope (Site Actions –> Site Settings –> Site Collection Features), enable the SharePoint Server Publishing Infrastructure feature. At the web scope (Site Actions –> Site Settings –> Manage Site Features), enable the SharePoint Server Publishing feature.

At this point, modify the site the way you would like it to be for new sites based on the web template. That can include a custom master page, custom page layouts, new publishing pages, web parts, custom content types – anything that you want will (in theory) be included in your web template project.

Roadblock 1: Where did Save site as template go?

The first thing you’ll notice when you go to save your template is that the option for saving the site as a template is no longer there – it gets removed when you enable publishing. There may be a good reason for this – in MOSS for example, you ran the risk of a template getting deployed in another site collection not necessarily having all the content types you would need for the template to work properly. With Web Templates, though, it appears all that content gets stored with the template, so I'm not sure why the feature has been removed.

Before publishing is enabled:

image

After publishing is enabled:

image

There are several ways around this, including writing a feature to re-enable this option on the site settings page, but the easiest thing to do is just enter <site url>/_layouts/savetmpl.aspx in your browser’s address bar – that will take you right to the page that allows you to save your site.

Be sure to click “include content” so everything gets loaded into your SharePoint solution, including content types.

image

image

Once the template is created, click on the solution gallery link and save the template’s WSP file to your local disk.

Now go to Visual Studio and create a new project. The type will be “Import SharePoint Solution Package”. Give it a name and click OK.

image

When prompted for a deployment site, enter a site that you don’t care about. I would create a fresh site collection just for this purpose – the deployment will likely overwrite whatever is there.

image

Next, browse to your saved template file. Click Next.

image

Visual studio will then show you all the things it is about to import from your WSP file. You may be tempted to de-select some of the content, but if you do you’re likely to create errors in your solution that will need to be resolved, and the likely resolution will be to add the content back. Just leave everything selected and click finish.

image

image

Since we made this a Farm solution, we’ll need to modify the scope of the Web Template feature. For my publishing enabled team site, this is Feature 3:

image

You now have a visual studio project that is ready to be deployed and used to create new sites. Or so it would seem…

Roadblock 2: deployment error.

Project –> Right Click –> Deploy. Visual studio will alert you to many deployment conflicts – just let it resolve them automatically. You’re deploying to a site you don’t care about, remember?

image

There will be a lot of errors where SharePoint was unable to delete items to resolve conflicts. Most likely, the definition in the web template is a match for what already exists in SharePoint, namely standard list instances and content types. Nothing to worry about.

What you do need to worry about is the error message after the conflicts are “resolved”:

Error occurred in deployment step 'Add Solution': Dependency feature 'PublishingSite' (id: f6924d36-2fa8-4f0b-b16d-06b7250180fa) is not properly scoped for feature 'PublishingWebTemplate_Feature3' (id: 6fd24c8e-b684-42ab-9bbf-56759dfd5ea2). Its scope 'Site' must be equal to or higher than 'Farm'.

For some reason, when the WSP file was imported and Feature3 was generated, Visual Studio decided to make the SharePoint publishing site a dependency. This really makes no sense because the onet.xml file that was created will activate this feature, so there’s no reason for it to be a dependency.

image

To remove the dependency, double click Feature 3 to load the feature designer, scroll down to the bottom and expand “Feature Activation Dependencies, scroll down again, select the dependency and click “Remove”.

Now that that’s out of the way, right click-deploy again, and you will run into…

Roadblock 3: Deployment Error HRESULT: 0x80070002

File not found. This one has to be a bug. When you deploy, your error list look like this:

image

The warning was mentioned previously – Visual Studio couldn’t resolve all the conflicts. Again, no big deal. The problem is the error. Here, the output window offers no help.

  Deployment conflict resolution for one or more items failed. See the Output Window for details.
  Adding solution 'PublishingWebTemplate.wsp'...
  Deploying solution 'PublishingWebTemplate.wsp'...
Activate Features:
  Activating feature 'Feature1' ...
Error occurred in deployment step 'Activate Features': The system cannot find the file specified. (Exception from HRESULT: 0x80070002)
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========

It took me a while to figure out exactly what is causing this. The answer is included in the SharePoint ULS log files:

Cannot find doc C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Features\PublishingWebTemplate_Feature1\Files\Variation Labels\AllItems.aspx    
Failed to add list view and form pages for list "Variation Labels" in web "http://homedev/sites/test". hr = 0x2ecf41d0    
Failed to create list "Variation Labels" in web "http://homedev/sites/test", HRESULT=0x2ecf41d0. List XML: "<List Title="Variation Labels" Direction="none" Url="Variation Labels" BaseType="0" Type="100" AllowDeletion="FALSE" NoCrawl="TRUE" BrowserFileHandling="Permissive" DisableDeployWithDependentList="TRUE" HiddenList="TRUE" FolderCreation="FALSE" Catalog="FALSE" SendToLocation="|" ImageUrl="/_layouts/images/itgen.png" xmlns:ows="Microsoft SharePoint" xmlns:spctf="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms" xmlns="http://schemas.microsoft.com/sharepoint/"/>"    
Failed to instantiate list 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Features\CustomList\custlist'    
Unknown SPRequest error occurred. More information: 0x80070002    
Leaving Monitored Scope (List Creation: Variation Labels). Execution Time=70.0444709826517    
The element of type 'ListInstance' for feature 'PublishingWebTemplate_Feature1' (id: 14a09d46-89fb-4c97-bc3e-7797944b23d8) threw an exception during activation: The system cannot find the file specified. (Exception from HRESULT: 0x80070002)    
Feature Activation: Threw an exception, attempting to roll back.  Feature 'PublishingWebTemplate_Feature1' (ID: '14a09d46-89fb-4c97-bc3e-7797944b23d8').  Exception: System.IO.FileNotFoundException: The system cannot find the file specified. (Exception from HRESULT: 0x80070002)     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.Activate(SPSite siteParent, SPWeb webParent, SPFeaturePropertyCollection props, Boolean fForce)    

Even armed with this information, it isn’t clear what needs to be done to resolve the issue.

Here’s what you need to do:

  • Expand “Other Imported Files\<templatename>\Files\Variation Labels”
  • Copy “AllItems.aspx” to “List Instances\Variation_Labels”
  • On the copied file, set the deployment type to “ElementFile” and Deployment Location path to “Files\Variation Labels” in the properties window (use the Schema.xml file in the same location as your guide).

Re-deploy. Now, at long last, it should deploy without an error. You can finally create a new site based on your new web template. Without further modification, it will be displayed on the “Custom” tab of templates:

image

Go ahead and create a site base on your new template – that should work now as well, but don’t celebrate yet…

Roadblock 4: Error Creating Publishing Pages

On your new site, try to create a publishing page. Select Site Actions –> More Options, select Page in the left hand column, click Publishing Page for the type, and then click Create. Boom:

image

Since one of the main reasons to have a web template for a publishing enabled site is to be able to, you know, create publishing pages, we’re going to need to figure this out too.

Searching for the correlation id in the log files yields the following:

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.  Parameter name: index

Not very helpful, huh? While I can’t remember the exact steps I went through to troubleshoot this the first time, I can tell you that the answer lies in the way the content types are defined for the publishing pages. If you look at the elements.xml files for the master page gallery, you will find several instances where the PublishingAssociatedContentType property value is not set right. For example, Project Page reads like this:

<Property Name="PublishingAssociatedContentType" Value="Project Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C0055EF50AAFF2E4badA437E4BAE09A30F8" />

It should be this:

<Property Name="PublishingAssociatedContentType" Value=";#Project Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C0055EF50AAFF2E4badA437E4BAE09A30F8;#" Type="string" />

At first glance, you may not even notice the difference, but you’ll note that in the correct string, you’ll find number signs as a delimiter. That’s what’s missing, and that’s what’s causing the crash when you try to create a new publishing page.

I’m not sure what the best way is to provide this information in this format, but you’ll need to find these 5 property elements in your solution (one at a time):

 

<Property Name="PublishingAssociatedContentType" Value="Project Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C0055EF50AAFF2E4badA437E4BAE09A30F8" />

<Property Name="PublishingAssociatedContentType" Value="Enterprise Wiki Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C" />

<Property Name="PublishingAssociatedContentType" Value="Redirect Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900FD0E870BA06948879DBD5F9813CD8799" />

<Property Name="PublishingAssociatedContentType" Value="Article Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D" />

<Property Name="PublishingAssociatedContentType" Value="Welcome Page, 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390064DEA0F50FC8C147B0B6EA0636C4A7D4" />

and replace them with the correct values:

 

<Property Name="PublishingAssociatedContentType" Value=";#Project Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C0055EF50AAFF2E4badA437E4BAE09A30F8;#" Type="string" />

<Property Name="PublishingAssociatedContentType" Value=";#Enterprise Wiki Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39004C1F8B46085B4d22B1CDC3DE08CFFB9C;#" Type="string" />

<Property Name="PublishingAssociatedContentType" Value=";#Redirect Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900FD0E870BA06948879DBD5F9813CD8799;#" Type="string" />

<Property Name="PublishingAssociatedContentType" Value=";#Article Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#" />

<Property Name="PublishingAssociatedContentType" Value=";#Welcome Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390064DEA0F50FC8C147B0B6EA0636C4A7D4;#" Type="string" />

Be sure to replace the correct corresponding Property Element (i.e. Project page for project page). Use the find and replace with a scope of entire solution – some of these appear more than once.

Still with me? Didn’t think so, but I’ll plow on anyway, because that’s the last road block. Right click-deploy, create a new site, create a publishing page, everything should be working now.

Final steps: Customization

At this point, you should have a working web template solution, but there are a few other things you may want to clean up:

  • Edit the elements.xml file under the Web Templates folder to add a Display Category element. This puts your web template under a tab you name rather than the default “Custom” tab
  • While editing the elements file, give your web template a more appropriate Name and Title
  • Depending on your source web site, you may have path length issues. Visual Studio is pretty good about updating references when you rename folders, so you should be able to find folders that you can reduce (drastically in some cases) the length of the folder name to prevent path length issues.
  • Rename your features. Check the contents of the features to see which feature is doing what and rename appropriately.
  • Make your features hidden – there’s no reason for a user to interact with these features, they’re just used to help deploy content when you create sites based on your web template. Hide them to prevent confusion.

NOTE: one other issue I encountered when writing this was an error during deployment from Visual Studio:

Error occurred in deployment step 'Activate Features': Specified data type does not match the current data type of the property.

This doesn’t seem to affect the web template itself, it’s more a case of the site I’m using as a deployment target is in a state that no longer allows for successful activation of the features. I didn’t bother trying to resolve the issue since it didn’t keep me from successfully creating a web template I could use to create new sites, and the site I was using as a deployment target was just a throwaway anyway.

Wednesday, May 5, 2010

LaunchPickerTreeDialog in SharePoint 2010, and the Importance of ScopeToWeb

You know what I hate – when people start a blog, and then go months or years without publishing a topic. And then they come back and say “sorry I was gone, I’ve been busy, blah blah blah it won’t happen again – this time I’m really committed to updating this blog regularly” – whatever. I may take another 15 months off after this post, but here’s something interesting I just discovered about the behavior of the LaunchPickerTreeDialog javascript function included in SharePoint that I think is worth sharing here since it definitely falls under the “voodoo magic” category.

LaunchPickerTreeDialog – what it does

If you’re unfamiliar with LaunchPickerTreeDialog, others have written about it extensively so I won’t go into the details of how it works here. Google is your friend. I will tell you that it prompts the user to select a website, list, or document library using a graphic interface rather than forcing them to type in a URL. It’s well worth your effort to figure it out and use it in your solutions.

What’s New in SharePoint 2010?

So I’m testing some of my custom code on a site collection that was upgraded from SharePoint 2007 to SharePoint 2010, and I discover that when I call LaunchPickerTreeDialog on my site in SharePoint 2010, it is only showing lists and libraries from the current site. In SharePoint 2007, the same code would display all sub-sites as well in the tree view that the dialog box displays. What’s up with that?

image (click to enlarge)

Where’d my sites go? I know I have sub-sites, and I know they showed up in SharePoint 2007:

image (click to enlarge)

My first step towards figuring out what was going on was to load up IE8’s debugger and look at the script tab. The script I'm looking for is in PickerTreeDialog.js – that hasn’t changed from 2007. I put a breakpoint on the first line of code in the LaunchPickerTreeDialog function:

image (click to enlarge)

I was wondering if this was different from the 2007 version, so I did the same thing in my SharePoint 2007 environment. Here’s what that looked like:

image (click to enlarge)

Notice the difference? Aside from the obvious code difference inside the function, the signature changed in the 2010 version: has two new parameters:

  • ScopeToWeb
  • RequireCT

And now, in a blatant attempt to trick the search engines help people find this post, ScopeToWeb, ScopeToWeb, ScopeToWeb. That’s the key. What does this parameter do? What is the purpose? Where is it documented? No clue. But what I do know is that if you pass in an empty string to this parameter, you get your sub-sites. Pass in anything else, you don’t get your sub-sites. Hopefully someone will add in the comments exactly what this parameter is supposed to do, but for now, that’s all you need to know to fix your code.

Of course, if you’re still supporting SharePoint 2007, you can’t just go adding parameters to the LaunchPickerTreeDialog call because you’ll code won’t work in 2007. So you need to do a version check (blah) to make sure you’re only adding this parameter when you’re running on SharePoint 2010. There are several ways to do that – here’s what I chose to do:

   1: if (Microsoft.SharePoint.Administration.SPFarm.Local.BuildVersion.Major >= 14)
   2: {
   3:     // SharePoint 2010 has two new parameters - 
   4:     // ScopeToWeb and RequireCT. ScopeToWeb needs
   5:     // to be empty string to show sub-sites in the tree
   6:     sb.AppendLine("LaunchPickerTreeDialog('CbqPickerSelectListTitle','CbqPickerSelectListText','listsOnly','',serverUrl, '','','','/_layouts/images/smt_icon.gif','',callbackList,'','');");
   7: }
   8: else
   9: {
  10:     sb.AppendLine("LaunchPickerTreeDialog('CbqPickerSelectListTitle','CbqPickerSelectListText',");'listsOnly','',serverUrl, '','','','/_layouts/images/smt_icon.gif','',callbackList);");
  11: }

Wow, do I hate that. Someone rescue me and tell me there’s a better way in the comments. But in the meantime, if you’re running your LaunchPickerTreeDialog in SharePoint 2010 and trying to figure out why you can no longer see your sub-sites in the tree view, now you know – pass in an empty string for the ScopeToWeb parameter.