Thursday, June 24, 2010

WikiEditPage.InsertWebPartIntoWikiPage - does it have a bug?


SharePoint 2010 has new interesting feature "Team Site Wiki". It's enabled by default for each team site and replace original site home page (default.aspx) with sitepages/home.aspx. This new page is Wiki and can be edited very simply: you can just type any text there, insert image (without Image WebPart) and etc. Web Parts can be used also.

How to edit this wiki page programmatically?

First of all you need to open this aspx file:
SPFile wikiFile = web.GetFile(wikiUrl);

After that you can get access to its Item and LimitedWebPartManager.

Wiki page body is stored in wikiFile.Item["WikiField"] field.
Empty wiki page contains something like this
<div class="ExternalClassB93FFFFBB50E42E5B5D1BE5F906438A1">
<table id="layoutsTable" style="width:100%">
<tbody>
 <tr style="vertical-align:top">
  <td style="width:100%">
  <div class="ms-rte-layoutszone-outer" style="width:100%">
  <div class="ms-rte-layoutszone-inner"></div>
  </div>
  </td>
 </tr>
</tbody>
</table>
<span id="layoutsData" style="display:none">false,false,1</span>
</div>


So to add some text message to wiki page you need just to insert your text in HTML format:
<p> Welcome to our WIKI</p>

in div with ms-rte-layoutszone-inner class:
<div class="ms-rte-layoutszone-inner"><<p> Welcome to our WIKI</p>/div>

Ok. But how to add new Web Part to this page. SharePoint 2010 Object Model suggests to use WikiEditPage.InsertWebPartIntoWikiPage method for this purpose.
But looks like it works incorrect: Position parameter is processed by the wrong way and you get non-working page after update.

Reflected source code:

public static void InsertWebPartIntoWikiPage(SPFile wikiFile, WebPart webpart, int position)
{
if (wikiFile == null)
{
throw new ArgumentNullException("wikiFile");
}
if (webpart == null)
{
throw new ArgumentNullException("webpart");
}
string str = (string) wikiFile.Item["WikiField"];
if (position < 0)
{
throw new ArgumentOutOfRangeException("position");
}
if ((str != null) && (position > str.Length))
{
throw new ArgumentOutOfRangeException("position");
}
SPLimitedWebPartManager limitedWebPartManager = wikiFile.GetLimitedWebPartManager(PersonalizationScope.Shared);
Guid storageKey = Guid.NewGuid();
string str2 = Utility.StorageKeyToID(storageKey);
webpart.ID = str2;
limitedWebPartManager.AddWebPart(webpart, "wpz", 0);
string str3 = string.Format(CultureInfo.InvariantCulture, "<div class=\"ms-rtestate-read ms-rte-wpbox\" contentEditable=\"false\"><div class=\"ms-rtestate-read {0}\" id=\"div_{0}\"></div><div style='display:none' id=\"vid_{0}\"></div>
</div>", new object[] { storageKey.ToString("D") });
if (str == null)
{
str = str3;
}
else
{
str = str.Insert(position, str3);
}
wikiFile.Item["WikiField"] = str;
wikiFile.Item.Update();
}
Insert call corrupts original WikiField value because, for instance, if your position =2, you get failed HTML. Even your position=0 and Wiki page looks nice - you cannot edit it.
So I don't recommend to use InsertWebPartIntoWikiPage in your work.

You can write own implementation which insert new str3 (check source code above) in the correct place: in div with ms-rte-layoutszone-inner class.

Note: Microsoft.SharePoint.WebPartPages.Utility.StorageKeyToID is internal, but you can replace it with own implementation also:
string StorageKeyToID(Guid storageKey)
{
if (!(Guid.Empty == storageKey))
{
return ("g_" + storageKey.ToString().Replace('-', '_'));
}
return string.Empty;
}

Addition: to have ability web part drag&drop you need to insert <p> </p> before and after your web part HTML node

1 comment: