Avoid duplicated webparts on Page Layout feature reactivation

We’ve all been there:
You have created some nice Page Layouts, added some webparts to the WebPartZones via the Elements.xml file, deployed the solution and activated the feature.

Now it is just a matter of time before the client asks for a new Page Layout, or for a change in one of the existing one.

“Fine”, you think – makes the changes, builds, uploads the .wsp to the server, runs Update-SPSolution and reactivates the feature.

Disaster! All your webparts now are added two times to all new pages created! The client gets furious and you can’t stop hitting yourself.

Don’t worry! There is a quite easy solution to this problem:

Add a FeatureReceiver to the feature containing your Page Layouts by “Right Click on the Feature in Visual Studio -> Add Event Receiver”

In the FeatureActivatedEvent write the following:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var site = properties.Feature.Parent as SPSite;
    FixDuplicateWebParts(site, "YOURPAGELAYOUTPREFIX");
}

Replace YOURPAGELAYOUTPREFIX with the prefix you hopefully have used in the filename of your webparts.

Now all you have to do is implementing the method

        private void FixDuplicateWebParts(SPSite site, string prefix)
        {
            try
            {
                SPList list = site.GetCatalog(SPListTemplateType.MasterPageCatalog);
                SPListItemCollection items = list.Items;
                List<string> webParts = new List<string>();

                // find the right Page Layout
                for (var i = items.Count - 1; i >= 0; i--)
                {
                    var item = items[i];
                    if (item.Name.IndexOf(prefix,
                        StringComparison.CurrentCultureIgnoreCase) > -1 && item.Name.IndexOf(".aspx",
                        StringComparison.CurrentCultureIgnoreCase) > -1)
                    {
                        SPFile file = item.File;
                        file.CheckOut();
                        // get the Web Part Manager for the Page Layout
                        using (SPLimitedWebPartManager wpm =
                            file.GetLimitedWebPartManager(PersonalizationScope.Shared))
                        {
                            // iterate through all Web Parts and remove duplicates
                            int nbrOfWebParts = wpm.WebParts.Count - 1;
                            for (var j = nbrOfWebParts; j >= 0; j--)
                            {
                                using (var stringWriter = new StringWriter())
                                {
                                    using (var xw = new XmlTextWriter(stringWriter))
                                    {
                                        System.Web.UI.WebControls.WebParts.WebPart wp =
                                        wpm.WebParts[j];

                                        wpm.ExportWebPart(wp, xw);
                                        xw.Flush();

                                        string md5Hash = GetMD5(stringWriter.ToString());
                                        if (webParts.Contains(md5Hash))
                                        {
                                            wpm.DeleteWebPart(wp);
                                        }
                                        else
                                        {
                                            webParts.Add(md5Hash);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                file.CheckIn(string.Empty);
                if ((file.Level == SPFileLevel.Draft) && (file.CheckOutType == SPFile.SPCheckOutType.None))
                {
                    file.Publish(string.Empty);

                    try
                    {
                        if (file.Item.ModerationInformation.Status == SPModerationStatusType.Pending)
                        {
                            file.Approve(string.Empty);
                        }
                    }
                    catch (System.Exception)
                    { //Suppress 
                    }
                }
            }
            catch (Exception e)
            {
                Logger.WriteLog(Logger.Category.Unexpected, this.ToString(), e.Message);
            }
        }

This code in turn requires this method:

        public static string GetMD5(string Value)
        {
            if (Value == "") return "";
            MD5CryptoServiceProvider x = new MD5CryptoServiceProvider();
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(Value);
            bs = x.ComputeHash(bs);
            x.Clear();  // dispose
            System.Text.StringBuilder s = new System.Text.StringBuilder();

            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToLower());
            }

            return s.ToString();
        }

That’s it! When you reactivate the feature it makes sure that not two identical WebParts will be present in a Page Layout.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: