PageLayouts not updating through module

Trying to deploy an updated page layout through a module to the master page gallery was going wrong horribly. The page layout inside the gallery wasn’t updating while deployment was succesful and nothing was showing up in the logs as of why there wasn’t an update done to the file. The project as example looks like this:

pagelayoutproject

As you can see it’s a simple module which will deploy “testLayout.aspx” to the master page gallery using the following elements.xml:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="PageLayouts" Url="_catalogs/masterpage" RootWebOnly="TRUE">
    <File Path="PageLayouts\testLayout.aspx" Url="PageLayouts/testLayout.aspx" Type="GhostableInLibrary">
      <Property Name="Title" Value="My Custom Page Layout" />
      <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
      <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png" />
      <Property Name="PublishingAssociatedContentType" Value=";#$Resources:cmscore,contenttype_articlepage_name;;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#"/>
    </File>
  </Module>
</Elements>

The page layout was generated through SharePoint Designer and had the following source:

<%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>

<%@ Register TagPrefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:content contentplaceholderid="PlaceHolderPageTitle" runat="server">
	<SharePointWebControls:FieldValue id="PageTitle" FieldName="Title" runat="server"/>
</asp:content>
<asp:content contentplaceholderid="PlaceHolderMain" runat="server">
</asp:content>

Going deeper into the page’s properties it showed that the page has been customized, which was odd since nobody touched the file through SharePoint designer which would cause ghosting.

customizedpagelayout

After some research I’ve found a lot of suggestions to go the code way of manually removing the file and then add the page layout, but in my opinion it should be done through the module without code. I knew it was possible through a simple update of the module so what was going wrong? Easy: SharePoint Designer. If you look closely, the tool adds the following meta tags to the Page tag:

meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document"

For some reason, whenever these tags are there, SharePoint will always ghost your page layouts no matter what. Removing these tags and unghosting the page was sufficient to get back those updates through modules. Phew!

Advertisements

Updating content types using SharePoint Web Services (SPServices)

When you deploy an updated content type which is created from code and have new fields, your changes won’t automatically be pushed to children that inherit from your content type. This is only the case when you have created your content type using code. In the sandbox solution, I’m using the SharePoint Web Services to add new fields to content types that inherit from the updated content type.

Why the web services? With the push from Microsoft to go more and more client side, I found it a challenge to not do something with simply a farm solution or a server-side script. During the development of the web part, I’ve found 2 annoyances by using the SPServices library and jQuery/SharePoint:

  1. SPServices says every method name is unique. This is not true. There’s a method “UpdateContentType” for both the Lists.asmx and the Webs.asmx. Due to this overlooked bug, the call will always be directed to the Webs.asmx web service. I’ve modified the SPServices js file to comment out the WSops.UpdateContentType method for Webs.
  2. jQuery will always put your generated attributes to lower case. The web services are case-sensitive as you can see from the following screenshot (taken from STSSOAP.DLL, decompiled with ILSpy)
    caseSensitive

The web part itself is pretty straight forward. You’ll get a list of content types with update links.

content type updater webpart

When using firebug or another web dev tool, you can track all the calls that are made to the Webs.asmx & Lists.asmx

content type updater console window

I’ve deployed a custom content type to my site, created a list with that content type and then made an update to the site content type. As you can see there’s a column missing from the list content type:

site content type

list content type

After I click on “Update” next to “CustomContentType”, the script will detect a difference between these two and update accordingly:

soap call new fields

And the updated list content type:

updated list content type

All in all I’m pretty happy with the result. You can do a lot with just the web services from SharePoint, but they take some time to get used to. Also the documentation isn’t always great as for instance they mention to use <FieldRefs> while in the source code of the web service itself there’s only a parse of the <Fields> tag.

For the full source, or if you want to download and test the web part itself, you can go to CodePlex.