Sharepoint - How to update custom master page and page layout, which is already deployed

I have resolved the same issue using the my post.

In Feature.xml add a reference to the Feature receiver class.

<?xml version="1.0" encoding="utf-8"?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="{35867BDA-82DC-418c-9005-79CCC363E3CD}"
         Title="blah"
         Description="blah"
         Scope="Site"
         Hidden="FALSE"
         Version="1.0.0.0"
         ReceiverAssembly="TestSPSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f5afeac9a8e5e1b0"
         ReceiverClass="TestSPSolution.UpdateFiles">
  <ElementManifests>
    <ElementManifest Location="ProvisionedFiles.xml" />
  </ElementManifests>
</Feature>

Feature Receiver class will contain the following code and your master pages and page layouts will be updated like a charm :)

using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.Globalization;
using System.Xml;
using System.Collections;
using System.Linq;

namespace TestSPSolution
{
    public class UpdateFiles : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            if (properties != null)
            {
                using (SPSite currentSite = (SPSite)properties.Feature.Parent)
                {
                    using (var web = currentSite.OpenWeb())
                    {
                        var ElementDefinitions = properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture);

                        foreach (SPElementDefinition ElementDefinition in ElementDefinitions)
                        {
                            if (ElementDefinition.ElementType == "Module")
                            {
                                Helper.UpdateFilesInModule(ElementDefinition, web);
                            }
                        }
                    }

                }
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            //// throw new NotImplementedException();
        }

        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
            ////throw new NotImplementedException();
        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
            //// throw new NotImplementedException();
        }
    }

    internal static class Helper
    {
        internal static void UpdateFilesInModule(SPElementDefinition elementDefinition, SPWeb web)
        {
            XElement xml = elementDefinition.XmlDefinition.ToXElement();
            XNamespace xmlns = "http://schemas.microsoft.com/sharepoint/";
            string featureDir = elementDefinition.FeatureDefinition.RootDirectory;
            Module module = (from m in xml.DescendantsAndSelf()
                             select new Module
                             {
                                 ProvisioningUrl = m.Attribute("Url").Value,
                                 PhysicalPath = featureDir,
                                 Files = (from f in m.Elements(xmlns.GetName("File"))
                                          select new Module.File
                                          {

                                              FilePath = (m.Attribute("Path") == null) ? string.Empty : Path.Combine(featureDir, m.Attribute("Path").Value),
                                              Name = f.Attribute("Url").Value,
                                              Properties = (from p in f.Elements(xmlns.GetName("Property"))
                                                            select p).ToDictionary(
                                                              n => n.Attribute("Name").Value,
                                                              v => v.Attribute("Value").Value)
                                          }).ToArray()
                             }).First();

            if (module == null)
            {
                return;
            }

            foreach (Module.File file in module.Files)
            {

                string physicalPath = string.IsNullOrEmpty(file.FilePath) ? Path.Combine(module.PhysicalPath, file.Name) : Path.Combine(file.FilePath, file.Name);
                string virtualPath = string.Concat(web.Url, "/", module.ProvisioningUrl, "/", file.Name);

                if (File.Exists(physicalPath))
                {
                    using (StreamReader sreader = new StreamReader(physicalPath))
                    {
                        if (!CheckOutStatus(web.GetFile(virtualPath)))
                        {
                            web.GetFile(virtualPath).CheckOut();
                        }
                        SPFile spFile = web.Files.Add(virtualPath, sreader.BaseStream, new Hashtable(file.Properties), true);
                        spFile.CheckIn("Updated", SPCheckinType.MajorCheckIn);
                        if (CheckContentApproval(spFile.Item))
                        {
                            spFile.Approve("Updated");
                        }

                        spFile.Update();
                    }
                }
            }

        }

        private static bool CheckOutStatus(SPFile file)
        {
            if (file.CheckOutStatus != SPFile.SPCheckOutStatus.None)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static bool CheckContentApproval(SPListItem listitem)
        {
            bool isContentApprovalEnabled = listitem.ParentList.EnableModeration;

            return isContentApprovalEnabled;
        }

        public static XElement ToXElement(this XmlNode node)
        {
            XDocument xDoc = new XDocument();

            using (XmlWriter xmlWriter = xDoc.CreateWriter())

                node.WriteTo(xmlWriter);

            return xDoc.Root;

        }
    }

    public class Module
    {
        public string ProvisioningUrl { get; set; }
        public string PhysicalPath { get; set; }
        public Module.File[] Files { get; set; }

        public class File
        {
            public string FilePath { get; set; }
            public string Name { get; set; }
            public Dictionary<string, string> Properties { get; set; }
        }
    }
}

Update: 2013-11-23 I have created a sample project with feature receiver code, sample page layouts and a master page. The solution (sample project) is tested with SharePoint 2013 also.

For SharePoint 2013, The complete Visual Studio 2013 solution (source code) can be downloaded from Technet

For SharePoint 2010, you may need to use SharePoint 2010 project template, add modules (for master pages and page layouts) and feature event receiver (copy paste the code in the feature event receiver)

enter image description here


As long as you dont customize (unghost) your page layout, updating through declarative feature should work fine.

By any chance did you create your page layout in SharePoint designer? If you did, a little known "feature" is that even though you provision your page layouts with GhostableInLibrary they will be unghosted and hence you will only be able to take a programmatic approach.

You can validate if this is the issue by checking in the page directive of your page layout for attributes inserted by SharePoint Designer:

<%@ Page .... meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>

If you have these attributes in your page layout, SharePoint will unghost them no matter how you provision...


You need to update your page layout programmatically. You have two options:

  1. Overwrite existing page layout: Updating a SharePoint master page / Page Layout via a solution (WSP)
  2. Deploy another page layout and replace all of its usages: Programmatically Update Page Layouts