Sunday, February 7, 2010

SharePoint 2010 – how to add web parts to rich html fields with code

It’s actually pretty simple – check out the small code snippet below that adds an xslt list view web part and a content editor web part to the PublishingPageContent field of a publishing page:

[Update 2010-12-24: the XLV web part provisioned with the code below will have an issue when you use folders in the target list – check the first two comments after the posting for a quick fix]

        private void TestAddRichContentWebParts(SPFile file)

        {

            // NOTE: we assume that the SPFile was checked out

            // get the limited wp manager for that SPFile

            SPLimitedWebPartManager mngr = file.GetLimitedWebPartManager(PersonalizationScope.Shared);

 

            // create a sample XLV

            XsltListViewWebPart xlv = new XsltListViewWebPart();

            // get a target list

            SPList list = file.ParentFolder.ParentWeb.Lists["Pages"];

            // set the ListName property with the capitalized list ID w/o the curly braces

            xlv.ListName = list.ID.ToString("B").ToUpper();

            // use the schema of the default view

            xlv.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();

            // add the web part to the special wpz zone

            mngr.AddWebPart(xlv, "wpz", 0);

            // store the web part's internal ID

            Guid xlvID = mngr.GetStorageKey(xlv);

 

            // create a sample content editor wp

            ContentEditorWebPart cwp = new ContentEditorWebPart();

            cwp.Title = "My editor";

            // add the web part to the special wpz zone

            mngr.AddWebPart(cwp, "wpz", 0);

            // store the web part's internal ID

            Guid cwpID = mngr.GetStorageKey(cwp);

 

            // get the list item for that SPFile

            SPListItem item = file.Item;

            // update the PublishingPageContent field value

            item["PublishingPageContent"] = "some content<br/>" + this.GetEmbeddedWPString(xlvID) + "<br/>some other content" + this.GetEmbeddedWPString(cwpID);

            // update the item

            item.SystemUpdate(false);

            // the SPFile instance can be checked in, published, approved afterwards

        }

 

        private string GetEmbeddedWPString(Guid wpID)

        {

            const string form = @"<div class=""ms-rtestate-read ms-rte-wpbox"">

  <div class=""ms-rtestate-notify ms-rtegenerate-notify ms-rtestate-read {0}"" id=""div_{0}""></div>

  <div id=""vid_{0}"" style=""display:none""></div>

</div>";

            // set the web part's ID as part of the ID-s of tho div elements

            return string.Format(form, wpID);

        }

The adding of the web parts involves two steps:

  • adding the web parts to the page with the limited web part manager – this is what you do with web parts in web part zones as well
  • the second and extra step is to set the HTML content of the rich HTML field that will contain the web parts – you need to add HTML “anchors” in it for the web parts – these will be the places in the content where the web parts will get rendered. The “anchors” contain several HTML div elements with ID attributes containing the internal guid IDs of the web parts.

And several notes on the sample code:

  • the sample TestAddRichContentWebParts method is provided with a SPFile instance for a publishing or wiki page file. You can get the page file with SPWeb.GetFile and should check it out if it is a publishing page (the default settings of a Pages library require check out) before calling the TestAddRichContentWebParts method.
  • notice that in the call of GetLimitedWebPartManager.AddWebPart you should pass “wpz” for the zoneId parameter. This special zone id name (an obvious acronym of “web part zone”) is required otherwise the web parts will end up in one of the real web part zones on the page.
  • in the sample the “PublishingPageContent” field of the page is updated – you can update any HTML field (and I suppose any HTML containing/rendering field) on a publishing or wiki page in the same manner. The GetEmbeddedWPString method generates the HTML “anchor” for the web part that you should embed in the HTML contents of the rich HTML field – this will be the place in the content where the web part will appear.

4 comments:

  1. When using your code I create XLV for documents library, then create subfolder in the library and upload a file into it, then attempt to click ECB on that file to view its properties, it produces the alert "This item is no longer available. It may have been deleted by another user".

    Editing/saving XLV properties helps.

    Dont you know a remedy by a chance?

    ReplyDelete
  2. Hi Ilya,
    I was able to reproduce your issue. It is actually a problem with the initializing of the XLV web part rather than the logic for adding web parts to rich html fields. The solution is also a tricky one but very simple:
    just replace this line:
    xlv.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();
    with this line of code:
    xlv.XmlDefinition = "<View BaseViewID='1'/>";
    (I found this after reflecting the code of the Microsoft.SharePoint.SPWikiPageHomePageFeatureReceiver class - this is the feature receiver that provisions the "Shared Documents" web parts in the home page of the standard "Team Site" site definition).

    ReplyDelete
  3. Or, instead of trying XmlDefinition, add xlv.ListId = list.DefaultView.ID.

    ReplyDelete
  4. Great article, it really helped me. I was trying to add a WebPart using CSOM to a SharePoint site. However, for rich html fields [wpz], WebParts were not getting displayed. The issue was I was not adding that EmbeddedWPString to the PublishingPageContent field. Thanks.

    ReplyDelete