Friday, October 7, 2011

Programmatically Provisioning Wiki Pages with Content and Web Parts in SharePoint 2010

 

If you need to:

  1. Programmatically create a wiki page.
  2. Declaratively create a wiki page.
  3. Programmatically or Declaratively add or modify content on an existing wiki page.
  4. Programmatically or Declaratively deploy/add or remove/delete web parts from an existing (or new) wiki page

You should find this post useful…

I met these requirements in two simple steps:

Step 1 – Define a quick, re-usable schema that’ll allow someone to quickly declare what content and/or web parts are is to be added to which new or existing pages in what libraries

Step 2 – Develop a small provisioning class that i can throw the xml file at and have it provision as per the declarative instructions in a config file.

 

 

Config/Data File

My Config / data file looks something like:

image

 

So:

<Example> Root Document Node
    <Library> A Library node used to represent a wiki pages library.  The URL of the library is represented by the ‘URL’ attribute.
       <Page> A node to represent a single page in the library.  The page name is represented by the ‘Name’ attribute.
         <Content> The HTML content of the page encapsulated in <![CDATA[]]>
         <WebParts> A node to represent the webparts to be added to the page
          <WebPart> A node to represent a single web part.  The Web Part ID (As declared in the content of the page) is declared using the ‘ID’ Attribute.  The content of the web part is the exported web part xml

 

The page content is pretty self explanatory from the screen shot and description above (simply throw html content inside a cdata section).  Take note I’ve used the character pattern {<n>} to represent Web Part Place holders in the html.  n in this case is any unique string/number to identify the web parts declared in the appropriate section.

Each <WebPart> node simply contains the xml contents  of the exported web part again wrapped in a cdata section:

 

NB: Example below shows a content editor web part to be imported inline within a wiki page.  I know (and I hope you know) that in the real world this doesn’t really make sense but will suffice for the purpose of this demonstration. 

SNAGHTMLe931da9

 

My second web part is a little more interesting, an XSLT List View Web Part.  SharePoint does not allow you to export these web parts through the web UI.  You’ll need to crack open Designer to get this:

image

 

After clicking the magic button, you’ll be prompted to supply a location to save your exported file… Once saved, crack taht open in Visual Studio/Your favourite editor and copy paste directly into a CData Section within the <WebPart> tag

Provisioning Class

My Provisioning class constructor looks something like:

public class PageDeployer

{

    private XmlNode configNode;

    public PageDeployer(XmlNode ConfigNode)

    {

        this.configNode = ConfigNode;

    }

    ...

}

 

Taking note that upon instantiation, it’s expecting a <Library> Node…

A Deploy method within the class accepts a single SPWeb Object and is responsible for doing the work:

 

public void Deploy(SPWeb web)

{

    SPList list = this.GetListFromWebRelativeURL(web,this.configNode.Attributes["URL"].Value);

    foreach (XmlNode pageNode in this.configNode.SelectNodes("Page"))

    {

        //get the page name from the xml attribute:

        string pageFileName = pageNode.Attributes["Name"].Value;

               

        //build the file URL:

        string fileURL = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, pageFileName);

 

        bool fileExisted = true;

        SPFile file = web.GetFile(fileURL);

               

        if (!file.Exists)

        {

            //File does not exist, we need to create it:

            file = SPUtility.CreateNewWikiPage(list, fileURL);

            fileExisted = false;

        }

 

        //get the content as defined in the xml provisioning file

        string content = ((XmlCDataSection)pageNode.SelectSingleNode("Content").FirstChild).Value.Trim();

               

        //grab reference to the web part manager for the page:

        using (SPLimitedWebPartManager splwm =

            file.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))

        {

            if (fileExisted)

            {

                while (splwm.WebParts.Count > 0)

                {

                    splwm.DeleteWebPart(splwm.WebParts[0]);

                }

            }

            //iterate through each web part node:

            foreach (XmlNode webPartNode in pageNode.SelectNodes("WebParts/WebPart"))

            {

                //dynamically add the web part and modify the contents

                content = InsertWebPart(webPartNode, content, splwm);

            }

                   

        }

 

        //grab reference to the item:

        SPListItem item = file.GetListItem(new string[] { "WikiField" });

        //update the 'WikiField' field:

        item["WikiField"] = content;

 

        //commit the changes:

        item.Update();

    }

}

 

Nothing too special there – iterate through the page / web part nodes reading the properties of the config/xml file… The magic happens in the ‘InsertWebPart’ method which is responsible for:

  1. Creating WebPart Objects by loading the web part xml
  2. Adding web part objects to the web part manager
  3. Dynamically inserting a set of divs that the sharepoint rendering engine will later use to dynamically insert the web parts added to the SPLimitedWebPartManager

What is returned after doing all that is a single string representing the content of the wiki page.  To use this, simply set the field ‘WikiField’ to this value (demonstrated in last couple of lines in the ‘Deploy’ method outlined above).

private string InsertWebPart(XmlNode webPartNode, string wikiContent, SPLimitedWebPartManager WebPartManager)

{

    //get the provisioning file web part ID:

    string webPartID = webPartNode.Attributes["ID"].Value;

 

    //ensure that there's a place holder in the content for it.

    if (wikiContent.Contains(string.Format("{{<{0}>}}", webPartID)))

    {

        //get the web part xml:

        string webPartContent = ((XmlCDataSection)webPartNode.FirstChild).Value.Replace("\n",string.Empty).Replace("\r",string.Empty).Trim();

               

        //create the web part using the web part manager

        XmlReader xreader = XmlReader.Create(new System.IO.StringReader(webPartContent.Trim()));

        string WebPartImportErrorMsg = string.Empty;

        System.Web.UI.WebControls.WebParts.WebPart webpart = WebPartManager.ImportWebPart(xreader, out WebPartImportErrorMsg);

 

        //create a new web part IDs:

        Guid wpID = Guid.NewGuid();

        string containingDivID = wpID.ToString();

        string webPartObjID = this.StorageKeyToID(wpID);

               

        //set web part object with appropriate ID

        webpart.ID = webPartObjID;

 

        //add the web part

        WebPartManager.AddWebPart(webpart, "wpz", 0);

 

        //build the wiki content place holder

        string webPartPlaceHolder = this.GetWikiWebPartContainer(containingDivID);

 

        //replace the provisioning place holder with the one required by the wiki page:

        wikiContent = wikiContent.Replace(string.Format("{{<{0}>}}", webPartID), webPartPlaceHolder);

    }

 

    return wikiContent;

 

}

 

private string GetWikiWebPartContainer(string containingDivID)

{

    StringBuilder sb = new StringBuilder("<div class=\"ms-rtestate-read ms-rte-wpbox\"><div class=\"ms-rtestate-read ");

    sb.Append(containingDivID);

    sb.Append("\" id=\"div_");

    sb.Append(containingDivID);

    sb.Append("\"></div>\n");

    sb.Append("  <div class=\"ms-rtestate-read\" id=\"vid_");

    sb.Append(containingDivID);

    sb.Append("\" style=\"display:none\"></div>\n");

    sb.Append("</div>\n");

    string webPartPlaceHolder = sb.ToString();

    return webPartPlaceHolder;

}

 

private string StorageKeyToID(Guid storageKey)

{

    if (!(Guid.Empty == storageKey))

    {

        return ("g_" + storageKey.ToString().Replace('-', '_'));

    }

    return string.Empty;

}

 

 

So in summary,

I use a small console app like this:

static void Main(string[] args)

{

    using (SPSite site = new SPSite("http://iitb01/sites/test1"))

    {

        using (SPWeb web = site.OpenWeb())

        {

            string sampleDataFilePath = @"C:\SampleData\Pages.xml";

            XmlDocument configDoc = new XmlDocument();

            configDoc.Load(sampleDataFilePath);

            PageDeployer deployer =

                new PageDeployer(configDoc.SelectSingleNode("//Library"));

            deployer.Deploy(web);

        }

    }

    Console.WriteLine();

    Console.WriteLine();

    Console.WriteLine("All Done!");

    Console.ReadKey();

}

 

To throw a config file like this:

<?xml version="1.0" encoding="utf-8" ?>

<Example>

  <Library URL="SitePages">

    <Page Name="WP3.aspx">

      <Content>

        <![CDATA[

        <table>

          <tr>

            <td width="66%" valign="top">

              <div style="font-size:2em;">Hello World!!</div>

              <div style="font-style:italic; padding-left:8px; padding-top:4px">

                gone walkabout bloody as dry as a kindie. We're going cark it piece of piss you little ripper bludger. You little ripper banana bender shazza got us some bonza. She'll be right give it a burl with as busy as a budgie smugglers. It'll be bush oyster when shazza got us some bail up. As busy as a bonza mate trent from punchy larrikin. Shazza got us some battler piece of piss lets throw a butcher. Shazza got us some rage on how lets get some doovalacky. Grab us a slaps when stands out like a bogged. As stands out like dead horse piece of piss built like a digger.

              </div>

              <br/>

             

              {<0>}

             

              <div style="font-size:1.1em;padding-top:12px;padding-bottom:12px;">And Here is an example of a list view web part</div>

             

              {<1>}

             

            </td>

            <td>

              <img alt="People collaborating" src="/_layouts/images/homepageSamplePhoto.jpg" style="margin:5px" />

            </td>

          </tr>

        </table>

        ]]>

      </Content>

      <WebParts>

        <WebPart ID="0">

          <![CDATA[

<?xml version="1.0" encoding="utf-8" ?>

 

<WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">

       <Assembly>Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>

       <TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>

       <Title>Content Editor</Title>

       <FrameType>Default</FrameType>

       <Description>Allows authors to enter rich text content.</Description>

       <IsIncluded>true</IsIncluded>

       <PartOrder>0</PartOrder>

       <FrameState>Normal</FrameState>

       <Height />

       <Width />

       <AllowRemove>true</AllowRemove>

       <AllowZoneChange>true</AllowZoneChange>

       <AllowMinimize>true</AllowMinimize>

       <AllowConnect>true</AllowConnect>

       <AllowEdit>true</AllowEdit>

       <AllowHide>true</AllowHide>

       <IsVisible>true</IsVisible>

       <DetailLink />

       <HelpLink />

       <HelpMode>Modeless</HelpMode>

       <Dir>Default</Dir>

       <PartImageSmall />

       <MissingAssembly>Cannot import this Web Part.</MissingAssembly>

       <PartImageLarge>/_layouts/images/mscontl.gif</PartImageLarge>

       <IsIncludedFilter />

       <ExportControlledProperties>true</ExportControlledProperties>

       <ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />

       <Content xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor">&lt;span class=&quot;ms-rteThemeForeColor-8-3 ms-rteStyle-Comment&quot;&gt;&#8203;CONTENT EDITOR WEB

PART&lt;/span&gt;</Content>

 

          <PartStorage xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />

        </WebPart>

 

        ]]>

        </WebPart>

        <WebPart ID="1">

          <![CDATA[

          <?xml version="1.0" encoding="utf-8" ?>

 

<webParts>

       <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">

              <metaData>

                     <type name="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

                     <importErrorMessage>Cannot import this Web Part.</importErrorMessage>

              </metaData>

              <data>

                     <properties>

                           <property name="InitialAsyncDataFetch" type="bool">False</property>

                           <property name="ChromeType" type="chrometype">Default</property>

                           <property name="Title" type="string" />

                           <property name="Height" type="string" />

                           <property name="CacheXslStorage" type="bool">True</property>

                           <property name="ListDisplayName" type="string" />

                           <property name="AllowZoneChange" type="bool">True</property>

                           <property name="AllowEdit" type="bool">True</property>

                           <property name="XmlDefinitionLink" type="string" />

                           <property name="DataFields" type="string" />

                           <property name="Hidden" type="bool">False</property>

                           <property name="ListName" type="string" null="true" />

                            <property name="NoDefaultStyle" type="string" null="true" />

                           <property name="AutoRefresh" type="bool">False</property>

                           <property name="ViewFlag" type="string">8388621</property>

                           <property name="Direction" type="direction">NotSet</property>

                            <property name="AutoRefreshInterval" type="int">60</property>

                           <property name="AllowConnect" type="bool">True</property>

                           <property name="Description" type="string" />

                           <property name="AllowClose" type="bool">True</property>

                           <property name="ShowWithSampleData" type="bool">False</property>

                           <property name="ParameterBindings" type="string">

       &lt;ParameterBinding Name="dvt_sortdir" Location="Postback;Connection"/&gt;

       &lt;ParameterBinding Name="dvt_sortfield" Location="Postback;Connection"/&gt;

       &lt;ParameterBinding Name="dvt_startposition" Location="Postback" DefaultValue=""/&gt;

       &lt;ParameterBinding Name="dvt_firstrow" Location="Postback;Connection"/&gt;

       &lt;ParameterBinding Name="OpenMenuKeyAccessible" Location="Resource(wss,OpenMenuKeyAccessible)" /&gt;

       &lt;ParameterBinding Name="open_menu" Location="Resource(wss,open_menu)" /&gt;

       &lt;ParameterBinding Name="select_deselect_all" Location="Resource(wss,select_deselect_all)" /&gt;

       &lt;ParameterBinding Name="idPresEnabled" Location="Resource(wss,idPresEnabled)" /&gt;

       &lt;ParameterBinding Name="NoAnnouncements" Location="Resource(wss,noXinviewofY_LIST)" /&gt;

       &lt;ParameterBinding Name="NoAnnouncementsHowTo" Location="Resource(wss,noXinviewofY_DEFAULT)" /&gt;

</property>

                           <property name="Xsl" type="string" null="true" />

                           <property name="CacheXslTimeOut" type="int">86400</property>

                           <property name="WebId" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">00000000-0000-0000-0000-000000000000</property>

                           <property name="ListUrl" type="string">Lists/IITBComponents</property>

                           <property name="DataSourceID" type="string" />

                           <property name="FireInitialRow" type="bool">True</property>

                           <property name="ManualRefresh" type="bool">False</property>

                           <property name="ViewFlags" type="Microsoft.SharePoint.SPViewFlags, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">Html, TabularView, Hidden, Mobile</property>

                           <property name="ChromeState" type="chromestate">Normal</property>

                           <property name="AllowHide" type="bool">True</property>

                           <property name="PageSize" type="int">-1</property>

                           <property name="SampleData" type="string" null="true" />

                           <property name="BaseXsltHashKey" type="string" null="true" />

                            <property name="AsyncRefresh" type="bool">False</property>

                           <property name="HelpMode" type="helpmode">Modeless</property>

                           <property name="ListId" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">00000000-0000-0000-0000-000000000000</property>

                           <property name="DataSourceMode" type="Microsoft.SharePoint.WebControls.SPDataSourceMode, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">List</property>

                           <property name="AllowMinimize" type="bool">True</property>

                           <property name="TitleUrl" type="string">/sites/test1/Lists/IITBComponents</property>

                           <property name="CatalogIconImageUrl" type="string">/_layouts/images/itgen.png</property>

                           <property name="DataSourcesString" type="string" />

                           <property name="GhostedXslLink" type="string">main.xsl</property>

                           <property name="PageType" type="Microsoft.SharePoint.PAGETYPE, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">PAGE_NORMALVIEW</property>

                           <property name="DisplayName" type="string" />

                           <property name="UseSQLDataSourcePaging" type="bool">True</property>

                           <property name="Width" type="string" />

                           <property name="ExportMode" type="exportmode">All</property>

                           <property name="XslLink" type="string" null="true" />

                           <property name="ViewContentTypeId" type="string">0x</property>

                           <property name="HelpUrl" type="string" />

                           <property name="XmlDefinition" type="string">

       &lt;View Name="{108EA0FF-5634-4176-A1D7-DDF8A1A2421F}" MobileView="TRUE" Type="HTML" Hidden="TRUE" DisplayName="" Url="/sites/test1/SitePages/WP2.aspx" Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/images/generic.png"&gt;

              &lt;Query&gt;

                     &lt;OrderBy&gt;

                           &lt;FieldRef Name="ID"/&gt;

                     &lt;/OrderBy&gt;

              &lt;/Query&gt;

              &lt;ViewFields&gt;

                     &lt;FieldRef Name="Attachments"/&gt;

                     &lt;FieldRef Name="LinkTitle"/&gt;

              &lt;/ViewFields&gt;

              &lt;RowLimit Paged="TRUE"&gt;30&lt;/RowLimit&gt;

              &lt;Toolbar Type="Freeform"/&gt;

       &lt;/View&gt;

</property>

                           <property name="Default" type="string" />

                           <property name="TitleIconImageUrl" type="string" />

                           <property name="MissingAssembly" type="string">Cannot import this Web Part.</property>

                           <property name="SelectParameters" type="string" />

                     </properties>

              </data>

       </webPart>

</webParts>

          ]]>

        </WebPart>

      </WebParts>

    </Page>

   

  </Library>

</Example>

 

At a provisioning class that looks like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml;

using Microsoft.SharePoint;

using Microsoft.SharePoint.Utilities;

using Microsoft.SharePoint.WebPartPages;

 

namespace MyriadTech.SharePoint2010.WikiPageDeployer

{

    public class PageDeployer

    {

        private XmlNode configNode;

        public PageDeployer(XmlNode ConfigNode)

        {

            this.configNode = ConfigNode;

        }

 

        public void Deploy(SPWeb web)

        {

            SPList list = this.GetListFromWebRelativeURL(web,this.configNode.Attributes["URL"].Value);

            foreach (XmlNode pageNode in this.configNode.SelectNodes("Page"))

            {

                //get the page name from the xml attribute:

                string pageFileName = pageNode.Attributes["Name"].Value;

               

                //build the file URL:

                string fileURL = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, pageFileName);

 

                bool fileExisted = true;

                SPFile file = web.GetFile(fileURL);

               

                if (!file.Exists)

                {

                    //File does not exist, we need to create it:

                    file = SPUtility.CreateNewWikiPage(list, fileURL);

                    fileExisted = false;

                }

 

                //get the content as defined in the xml provisioning file

                string content = ((XmlCDataSection)pageNode.SelectSingleNode("Content").FirstChild).Value.Trim();

               

                //grab reference to the web part manager for the page:

                using (SPLimitedWebPartManager splwm = file.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))

                {

                    if (fileExisted)

                    {

                        while (splwm.WebParts.Count > 0)

                        {

                            splwm.DeleteWebPart(splwm.WebParts[0]);

                        }

                    }

                    //iterate through each web part node:

                    foreach (XmlNode webPartNode in pageNode.SelectNodes("WebParts/WebPart"))

                    {

                        //dynamically add the web part and modify the contents

                        content = InsertWebPart(webPartNode, content, splwm);

                    }

                   

                }

 

                //grab reference to the item:

                SPListItem item = file.GetListItem(new string[] { "WikiField" });

                //update the 'WikiField' field:

                item["WikiField"] = content;

 

                //commit the changes:

                item.Update();

            }

        }

 

        private string InsertWebPart(XmlNode webPartNode, string wikiContent, SPLimitedWebPartManager WebPartManager)

        {

            //get the provisioning file web part ID:

            string webPartID = webPartNode.Attributes["ID"].Value;

 

            //ensure that there's a place holder in the content for it.

            if (wikiContent.Contains(string.Format("{{<{0}>}}", webPartID)))

            {

                //get the web part xml:

                string webPartContent = ((XmlCDataSection)webPartNode.FirstChild).Value.Replace("\n",string.Empty).Replace("\r",string.Empty).Trim();

               

                //create the web part using the web part manager

                XmlReader xreader = XmlReader.Create(new System.IO.StringReader(webPartContent.Trim()));

                string WebPartImportErrorMsg = string.Empty;

                System.Web.UI.WebControls.WebParts.WebPart webpart = WebPartManager.ImportWebPart(xreader, out WebPartImportErrorMsg);

 

                //create a new web part IDs:

                Guid wpID = Guid.NewGuid();

                string containingDivID = wpID.ToString();

                string webPartObjID = this.StorageKeyToID(wpID);

               

                //set web part object with appropriate ID

                webpart.ID = webPartObjID;

 

                //add the web part

                WebPartManager.AddWebPart(webpart, "wpz", 0);

 

                //build the wiki content place holder

                string webPartPlaceHolder = this.GetWikiWebPartContainer(containingDivID);

 

                //replace the provisioning place holder with the one required by the wiki page:

                wikiContent = wikiContent.Replace(string.Format("{{<{0}>}}", webPartID), webPartPlaceHolder);

            }

 

            return wikiContent;

 

        }

 

        private string GetWikiWebPartContainer(string containingDivID)

        {

            StringBuilder sb = new StringBuilder("<div class=\"ms-rtestate-read ms-rte-wpbox\"><div class=\"ms-rtestate-read ");

            sb.Append(containingDivID);

            sb.Append("\" id=\"div_");

            sb.Append(containingDivID);

            sb.Append("\"></div>\n");

            sb.Append("  <div class=\"ms-rtestate-read\" id=\"vid_");

            sb.Append(containingDivID);

            sb.Append("\" style=\"display:none\"></div>\n");

            sb.Append("</div>\n");

            string webPartPlaceHolder = sb.ToString();

            return webPartPlaceHolder;

        }

 

        private string StorageKeyToID(Guid storageKey)

        {

            if (!(Guid.Empty == storageKey))

            {

                return ("g_" + storageKey.ToString().Replace('-', '_'));

            }

            return string.Empty;

        }

 

        private SPList GetListFromWebRelativeURL(SPWeb web, string WebRelativeListURL)

        {

            return web.GetList(string.Format("{0}/{1}", web.ServerRelativeUrl, WebRelativeListURL));

        }

    }

}

 

 

And i get:

image

9 comments:

  1. It works beautifully. Thanks for saving me a bunch of time.

    ReplyDelete
  2. excellent solution, very flexible, great work.
    Thanks a lot.

    ReplyDelete
  3. You have saved my day... or three, I've been struggling with this for 3 days already. Thank you very much!
    I would also like to add, that you can modify InsertWebPart method to allow it to insert custom webparts like this:

    private string InsertWebPart(System.Web.UI.WebControls.WebParts.WebPart webpart, string wikiContent, SPLimitedWebPartManager WebPartManager)
    {
    //get the provisioning file web part ID:
    string webPartID = "8"; //mu custom ID

    //ensure that there's a place holder in the content for it.
    if (wikiContent.Contains(string.Format("{{<{0}>}}", webPartID)))
    {
    //create a new web part IDs:
    Guid wpID = Guid.NewGuid();
    string containingDivID = wpID.ToString();
    string webPartObjID = this.StorageKeyToID(wpID);

    //set web part object with appropriate ID
    webpart.ID = webPartObjID;

    //add the web part
    WebPartManager.AddWebPart(webpart, "wpz", 0);

    //build the wiki content place holder
    string webPartPlaceHolder = this.GetWikiWebPartContainer(containingDivID);

    //replace the provisioning place holder with the one required by the wiki page:
    wikiContent = wikiContent.Replace(string.Format("{{<{0}>}}", webPartID), webPartPlaceHolder);
    }
    return wikiContent;
    }

    USAGE:
    content = InsertWebPart(new MyVeryCustomWebPart(), content, splwm);

    ReplyDelete
  4. It works!! Great, Thanks a lot!

    ReplyDelete
  5. Thanks a lot! You have saved my day, I was struggling 2 days with provisioning this until I've found this.
    Similiar way of doing the same http://dbremes.wordpress.com/2010/06/06/adding-web-parts-to-sharepoint-2010-wiki-pages-using-the-client-object-model/

    BR,
    Tomek

    ReplyDelete
  6. Nice, but I would like to see an example that does not use SPLimitedWebPartManager because you should ensure 365 support.

    ReplyDelete
  7. Did someone get 0x80070057 Error???

    ReplyDelete