Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

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?

share|improve this question
    
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 Sep 29 '11 at 9:23
add comment

6 Answers 6

up vote 29 down vote accepted

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

share|improve this answer
    
Thanks FreeStyler, your code works fine! –  Michal Sep 26 '11 at 11:42
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 Mar 16 '12 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 –  L.V. Sharepoint Architect Dec 30 '13 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 Mar 17 at 14:59
add comment

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

share|improve this answer
add comment

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
share|improve this answer
add comment

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

share|improve this answer
add comment

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

share|improve this answer
add comment

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"

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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