Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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

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

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

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

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"

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top