Wednesday, October 6, 2010

SharePoint 2010 Client Object Model: "(400) Bad Request" error

If you use Microsoft.SharePoint.Client.FileCollection.Add or Microsoft.SharePoint.Client.File.SaveBinary
method to create (or change) file in SharePoint 2010, you can get "The remote server returned an error: (400) Bad Request" error.

To resolve this issue you need to change default Maximum Message Size for WCF calls:

open SharePoint 2010 Management Shell
type:
$ws = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$ws.ClientRequestServiceSettings.MaxReceivedMessageSize = your_value
$ws.Update()

I recommend to set MaxReceivedMessageSize to Int32.MaxValue - 1 (2147483646).

Sometimes you may need to run "iisreset /noforce" to enforce changes applying.

Also you can use Microsoft.SharePoint.Client.File.SaveBinaryDirect approach that does not have this limitation.

Tuesday, September 7, 2010

How to add new aspx pages to SharePoint programmatically

There are two different solutions to create new aspx pages in SharePoint.

First one is using of SharePoint Services RPC Methods:
1. Create query in XML format to call NewWebPage method:
<?xml version="1.0" encoding="UTF-8"?>
<Batch>
<Method>
<SetList Scope="Request">DocLib_ID</SetList>
<SetVar Name="ID">New</SetVar>
<SetVar Name="Cmd">NewWebPage</SetVar>
<SetVar Name="Type">BasicPage|WebPartPage</SetVar>
<SetVar Name="WebPartPageTemplate">LayoutID</SetVar>
<SetVar Name="Title">AspxTitle</SetVar>
<SetVar Name="Overwrite">true</SetVar>
</Method>
</Batch>

Where:
  • DocLib_ID is ID (GUID) of document library where do you need to create new aspx file.
  • BasicPage|WebPartPage type of aspx. Use BasicPage for page without layout (LayoutID =0) and WebPartPage for all other terms
  • LayoutID specifies page layout [1...8]
2. Call SPWeb.ProcessBatchData method with this query.
3. If you need to create aspx file in document library subfolder, create it in doclib and move to the necessary subfolder with SPFile.MoveTo method.

But this approach doesn't work for meeting workspace where you can create several pages with access via convenient multi-page web part.
To create new pages there you need to use SPMeeting.AddPage (AspxTitle, InstanceID, out resNewPageUrl) method. Set InstanceID = 0 to create page directly in the Workspace Pages library.

Friday, August 20, 2010

Django cache (storage)

If you need to store something useful between client requests, you can use Django cache framework.

It allows you to use memory, file, database or custom storage type.

Its using is very simple:

Add CACHE_BACKEND = 'locmem://' in settings to use memory cache

Import cache: "from django.core.cache import cache" in .py file.
Get value: cache.get('stored_data'). It returns None if there is no 'stored_data'.
Set value: cache.set('stored_data', my_data, 60). '60' is optional - timeout of cache expiration.
Delete value: cache.delete('stored_data')

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