How To Change Sharepoint PageLayout Icon To Custom Image

Posted by Ahmed Tarek Hasan on 12/31/2012 08:59:00 PM with 2 comments
In Sharepoint, when you create a new article page, you have to choose a page layout for this article to control the way the article page will appear. So, to differentiate between different page layouts from UI, each page layout has its own title and an image. Thus, once you click on any page layout, its corresponding image appears.

This image should somehow reflect the basic structure of the layout so that you can differentiate between all of the layouts based on what you see in the image. So, if the page layout consists of three columns, the image should reflect this. Also, if the page layout consists of two columns, the image should reflect this and so on.

There are two places in system UI where you can see the page layout image.

When creating a new article page and selecting its page layout

When changing the page layout of an existing article page

So, when you work on your custom page layouts, the OOTB page layout images may not be suitable for your layouts. So, you may need to assign your custom descriptive images to these page layouts to maintain the same concept.

When I first decided to do this while working on my project, I thought that I will just need to set the "PublishingPreviewImage" property of my page layouts in the "Elements.xml" file. But, when I tried to do it, I found that the images of my custom layouts are still the default ones.

After some searching, I found out that some of the page layouts properties are not updated once they are set and deployed by upgrading the solution. The only way to update these images is to retract then deploy your solution or to do it using some code executed at the feature activation.

Sure I decided to take the second approach. So, now I need to write some code to be executed when the feature -provisioning my custom page layouts- is activated to set the image of my page layouts explicitly by code.

The "Elements.xml" file looks as follows;
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

So, now in the feature activation, I can set the "PublishingPreviewImage" property of each of these two page layouts to the path of my custom images. This is easy, but, I thought I can achieve the same result without depending on some hard-coded values in my code, instead, I can depend on the values already set in the "Elements.xml" file.

So, to do this, I used the approach I already explained before in a previous post. It is the post called "How Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener". If you didn't read this post, please try to read it before you move to the next step.

So, now the code will be as follows;
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using DevelopmentSimplyPut.CommonUtilities.Helpers;

namespace DevelopmentSimplyPut.News.Features.pagelayouts
{
    [Guid("e882ad84-444a-421a-961c-bb945556c3f8")]
    public class pagelayoutsEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            PageLayoutsHelper.UpdatePageLayoutsPublishingPreviewImage(properties);
        }
    }
}

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Globalization;
using System.Xml;
using Microsoft.SharePoint.Publishing;
using System.Collections.ObjectModel;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class PageLayoutsHelper
    {
  public static void UpdatePageLayoutsPublishingPreviewImage(SPFeatureReceiverProperties properties)
  {
   SPSite site = null;

   if (properties.Feature.Parent is SPSite)
   {
    site = properties.Feature.Parent as SPSite;
   }
   else
   {
    site = (properties.Feature.Parent as SPWeb).Site;
   }

   if(null != site)
   {
    PublishingSite publishingSite = new PublishingSite(site);
    PageLayoutCollection layouts = publishingSite.PageLayouts;
    List<ModuleInfo> moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
    foreach (PageLayout layout in layouts)
    {
     string layoutIdentifier = layout.ServerRelativeUrl;
     ModuleInfo moduleInfo = moduleInfoCollection.DefaultIfEmpty(null).FirstOrDefault(module => module.Url == "_catalogs/masterpage");
     if (null != moduleInfo)
     {
      ModuleFile fileInfo = moduleInfo.Files.DefaultIfEmpty(null).FirstOrDefault(file => layoutIdentifier.Contains(file.Url));
      if (null != fileInfo)
      {
       string imagePath = string.Empty;

       if (fileInfo.Properties["PublishingPreviewImage"].Contains(","))
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Split(',')[0].Trim().ToUpperInvariant();  
       }
       else
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Trim().ToUpperInvariant();
       }

       if (imagePath.Contains("~SITECOLLECTION"))
       {
        string siteCollectionPath = site.ServerRelativeUrl.ToUpperInvariant();
        if (!siteCollectionPath.EndsWith("/"))
        {
         siteCollectionPath += "/";
        }
        imagePath = imagePath.Replace("~SITECOLLECTION/", siteCollectionPath).Replace("~SITECOLLECTION", siteCollectionPath);
       }

       if (!string.IsNullOrEmpty(imagePath))
       {
        SPFile layoutFile = layout.ListItem.File;
        SPFileHelper.PrepareFileForChanges(layoutFile);
        layout.PreviewImageUrl = imagePath;
        layout.Update();
        SPFileHelper.FinalizeFile(layoutFile);
       }
      }
     }
    }
   }
  }
 }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class SPFileHelper
    {
        public static void PrepareFileForChanges(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.ForceCheckout
                        : file.Item.ParentList.ForceCheckout;

                if (checkOutEnabled)
                {
                    if (file.CheckOutType != SPFile.SPCheckOutType.None)
                    {
                        file.UndoCheckOut();
                    }
                    file.CheckOut();
                }
            }
        }
        public static void FinalizeFile(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                        file.Item == null
                            ? file.ParentFolder.Item.ParentList.ForceCheckout
                            : file.Item.ParentList.ForceCheckout;

                bool needsApproval =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.EnableModeration
                        : file.Item.ParentList.EnableModeration;

                if (checkOutEnabled)
                {
                    file.CheckIn(string.Empty, SPCheckinType.MajorCheckIn);
                }

                if (needsApproval)
                {
                    file.Approve(string.Empty);
                }

                file.Update();
            }
        }
    }
}

So, now every time the feature is activated, the page layouts images will be updated with the ones set in the "Elements.xml".


That's it. Hope this will help you someday.
Good Luck.


 

Categories: ,