54

I have a feature which deploying custom page layout. It's done by this code:

<Module Name="MasterPageModule" Url ="_catalogs/masterpage">
 <File Path="MasterPageModule\defaultTemplate.aspx" Url="defaultTemplate.aspx" Type="GhostableInLibrary">
  <Property Name="Title" Value="My Custom Page Layout" />
  <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
  <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
  <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/ArticleLinks.png, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/ArticleLinks.png" />
  <Property Name="PublishingAssociatedContentType" Value=";#Welcome Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390064DEA0F50FC8C147B0B6EA0636C4A7D4;#" />
 </File>

It's working fine but how to update and deploy it again when I change something in layout? If I set IgnoreIfAlreadyExists="FALSE" Visual Studio shows me this message:

Error occurred in deployment step 'Activate Features': A file specified in the modules section of this template already exists.

When I set IgnoreIfAlreadyExists="TRUE" layout is not updated. And also I can't delete this layout by Sharepoint Designer because "This item cannot be delete because it is still referenced by other pages". Yes, it's definitely referenced by some pages but in development i need to update it many times.

Can some Sharepoint master advise me something?

1
  • When i use your code, i am unable to read module content with XDocument or other method. I can have the attribute of the module node but not the child (file node"). Do you know why ?
    – user5043
    Commented Sep 29, 2011 at 9:23

7 Answers 7

42

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

3
  • 1
    Excellent !!!! just a thing i had to change : in method UpdateFilesInModule(), i had to use : Name = f.Attribute("Name").Value, instead of : Name = f.Attribute("Url").Value, Because some of my image files are deployed in "Preview Images" library : <File Url="PageLayoutHomeSite.png" IgnoreIfAlreadyExists="TRUE" Name="Preview Images/PageLayoutHomeSite.png" Type="GhostableInLibrary"> </File>
    – user7396
    Commented Mar 16, 2012 at 15:35
  • why on the feature activated event and not in a feature upgrade event?if you use correct feature versioning that is better than creating another feature for that Commented Dec 30, 2013 at 22:55
  • There is a dispose error in this code. The SPSite currentSite object from properties.Feature.Parent should not be disposed as it uses an already existing SPSite object.
    – tarjeieo
    Commented Mar 17, 2014 at 14:59
12

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...

8

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
6

This happens because the library may have the Check-out and Check-in option, IgnoreIfAlreadyExists will not work on those times. We have to add the program on feature receiver activation event, which overwrites the page layout, try the following post, http://vivek-soni.blogspot.com/2009/07/overwriting-files-using-module-element.html

5

Hi Take a look at this tool, http://spcrew.com/blogs/lists/posts/post.aspx?id=18 it makes use of swapping page layouts. has been very helpful

5

I was facing the exact same issue and resolved it by setting ReplaceContent="true" in the element.xml file of the module as below

<File Path="MasterPageModule\MyMasterPage.master" Url="MyMasterPage.master" Type="GhostableInLibrary" ReplaceContent="TRUE" Level="Published"/>

Also you can get it here https://blog.lekman.com/2013/06/page-layouts-and-master-pages-fail-to.html

1
  • 1
    This is available in 2013 and above, not for 2010 (just to clarify) Commented Apr 23, 2016 at 10:56
-3

There is no need for feature for updating, just do the following in your elemnets file ;)

Url="YourPage.aspx" ReplaceContent="True" IgnoreIfAlreadyExists="true" Type="GhostableInLibrary"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.