Inconvenient provisioning Web Parts to Wiki Pages in Sandboxed Solutions
Sandboxed Solutions are a powerful way to create SharePoint 2010 Solutions in a secure manner. The price of security however is limited access to the SharePoint API which makes it challenging to accomplish certain tasks like provisioning Web Parts to Wiki Pages. Find out how to provision Web Parts to Wiki Pages in Sandboxed Solutions.
Playing in the Sandbox
The introduction of Sandbox in SharePoint 2010 empowers both IT-Pros and Developers. Using Sandboxed Solutions developers can deliver rich solutions that leverage the SharePoint 2010 capabilities. Because those solutions are executed in an isolated fashion and have limited access to the SharePoint API and the .NET Framework, there are less risks involved with deploying and using them in a SharePoint Farm. One more important benefit of using Sandboxed Solutions is, that they require less involvement from IT departments as they can be deployed and managed by Site Collection Administrators.
In SharePoint 2007, where no Sandbox was available, designing SharePoint Solutions was a lot like all or nothing: either developers were allowed to create solutions with custom code and had virtually access to the full SharePoint API and .NET Framework or SharePoint Farms were sealed, no custom code would be allowed and developers were tasked with the challenge to get things to work using nothing more than the standard functionality of the SharePoint platform. As you can imagine neither of those two approaches is perfect. Allowing developers to use the full API means you have to spend more time on security-testing their work and narrowing the options to standard functionality only means that not the solution but the technical capabilities are central and there is a chance that business might get not the solution that they need, not to mention the extra effort required to “just get things done”.
Sandboxed Solutions offer added value to SharePoint developers allowing them to use their knowledge to build secure solutions. And although the API available for Sandboxed Solutions gives developers the ability to interact with data stored in SharePoint, it might be too limited to complete all tasks and provide end users with fully working solutions instead of a collection of building blocks and a manual.
Inconvenient provisioning Web Parts instances from Sandboxed Solutions
One of the challenges when working with Sandboxed Solutions is how to provision Web Parts instances to pages. If you have done this before in a Farm Solution you know, that in order to add a Web Part to a page programmatically in SharePoint you have to retrieve the instance of the SPLimitedWebPartManager class and use it to add the instance of a Web Part to a page. Unfortunately, as the SDK mentions, the SPLimitedWebPartManager class is not available in Sandboxed Solutions which means that you cannot programmatically add a Web Part to a page from within a Sandboxed Solution.
Luckily the programmatic approach isn’t the only option that SharePoint offers for adding Web Parts to pages. It turns out that there is a declarative approach as well, which is just perfect for when you need to provision a Web Part instance from within a Sandboxed Solution.
Declaratively provisioning Web Part instances to pages
Declaratively provisioning Web Part instances to pages is easy and can be done using a Module. The following code snippet shows how you can add a Content Editor Web Part with the Hello world text to a Web Part Page:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="HomePage">
<File Path="HomePage\default.aspx" Url="default.aspx">
<AllUsersWebPart WebPartOrder="0" WebPartZoneID="Left">
<![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">
<Title>Content Editor</Title>
<FrameType>Default</FrameType>
<Description>Allows authors to enter rich text content.</Description>
<IsIncluded>true</IsIncluded>
<ZoneID>Left</ZoneID>
<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 />
<Assembly>Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>
<ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
<Content xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor">Hello world</Content>
<PartStorage xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
</WebPart>]]></AllUsersWebPart>
</File>
</Module>
</Elements>
Important: to provision a Web Part to a Web Part Page you will need a Web Part Page file. You can copy the default Web Part Page file from 14\TEMPLATE\SiteTemplates\sts\default.aspx.
As you can see adding Web Parts to Web Part Pages is fairly easy. The challenge begins however as soon as you start working with Wiki Pages.
Declaratively provisioning Web Part instances to Wiki Pages
One of the new features introduced with SharePoint 2010 was the concept of adding Web Parts in content. Before that we had to use Content Editor Web Parts to store content if we wanted to use Web Parts in between. With SharePoint 2010 we could just type rich content and insert Web Parts in between just as if we were adding images or tables.
The Web Parts in content capability has been made available in Publishing Sites as well as Collaboration Sites. The only requirement with regards to Collaboration Sites is, that you have to be using Wiki Pages. A while ago I wrote an article about how you can add Web Part instances to content. The approach I presented involved using custom code which unfortunately cannot be used from within Sandboxed Solutions as it relies on the SPLimitedWebPartManager class. However using the same information about how Web Parts in content work behind the scenes, we can alter the declarative approach used for Web Part Pages to add a Web Part to a Wiki Page. The following code snippet presents the Module modified to add the Content Editor Web Part to a Wiki Page:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="HomePage">
<File Path="HomePage\wkpstd.aspx" Url="SitePages/Home.aspx">
<Property Name="WikiField" Type="string" Value="
<div class="ms-rtestate-read ms-rte-wpbox" contentEditable="false"><div class="ms-rtestate-read 46113482-da36-45a5-bbac-e1e1771c19fa" id="div_46113482-da36-45a5-bbac-e1e1771c19fa"></div><div style='display:none' id="vid_46113482-da36-45a5-bbac-e1e1771c19fa"></div></div>
"/>
<AllUsersWebPart WebPartOrder="0" WebPartZoneID="wpz" ID="g_46113482_da36_45a5_bbac_e1e1771c19fa">
<![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">
<Title>Content Editor</Title>
<FrameType>Default</FrameType>
<Description>Allows authors to enter rich text content.</Description>
<IsIncluded>true</IsIncluded>
<ZoneID>Left</ZoneID>
<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 />
<Assembly>Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>
<ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
<Content xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor">Hello world</Content>
<PartStorage xmlns="http://schemas.microsoft.com/WebPart/v2/ContentEditor" />
</WebPart>]]></AllUsersWebPart>
</File>
</Module>
</Elements>
Important: Note that the Wiki Page uses a different page file that can be copied from 14\TEMPLATE\DocumentTemplates\wkpstd.aspx
If you took a careful look at the code snippet you might have noticed that a few things have changed. First of all we changed the page’s URL to SitePages/Home.aspx (line 4) so that it points to a Wiki Page instead of a Web Part Page. Next we added the WikiField property (lines 5-7) which contains the content of the Wiki Page. Although the content is a little difficult to read it contains the HTML marker of Web Part added to content, which after decoding looks like this:
<div class="ms-rtestate-read ms-rte-wpbox" contenteditable="false"><div class="ms-rtestate-notify ms-rtestate-read 46113482-da36-45a5-bbac-e1e1771c19fa" id="div_46113482-da36-45a5-bbac-e1e1771c19fa"></div><div id="vid_46113482-da36-45a5-bbac-e1e1771c19fa" style="display: none"></div></div>
Next, to the AllUsersWebPart tag we added the ID attribute (line 8). Although the SDK doesn’t explain what this attribute is for, it can be used to provision a Web Part with a predefined ID. The only requirement is, that all – (dash) are replaced with _ (underscore) and the ID is prefixed with g_. As you can see the Web Part’s ID corresponds to the ID used in the marker placed in the Wiki Page’s content.
Bonus: Declaratively Provisioning List View Web Parts to Wiki Pages
There are scenarios when you might need to provision a List View Web Part to a Wiki Page. The good news is that the required approach is not much different from provisioning a Content Editor Web Part or any other Web Part for that matter.
If you’ve worked with declaratively provisioning List View Web Parts you know that in order to add a List View Web Part to a page you have to use the View element. For example to add a List View Web Part for the Announements list available in a standard Team Site to a Web Part Page, you would use the following code:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="HomePage">
<File Path="HomePage\default.aspx" Url="default.aspx">
<View List="$Resources:core,lists_Folder;/$Resources:core,announce_Folder;" BaseViewID="1" WebPartZoneID="Left" WebPartOrder="0" />
</File>
</Module>
</Elements>
Adding List View Web Parts to content is very similar to adding regular Web Parts. All you need to do is to change the value of the WebPartZoneID attribute to **wpz **** and add the ID attribute to the View element with a GUID specified exactly as if you would for any other Web Part that would be added in content, for example:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="HomePage">
<File Path="HomePage\wkpstd.aspx" Url="SitePages/Home.aspx">
<Property Name="WikiField" Type="string" Value="
<div class="ms-rtestate-read ms-rte-wpbox" contentEditable="false"><div class="ms-rtestate-read 46113482-da36-45a5-bbac-e1e1771c19fa" id="div_46113482-da36-45a5-bbac-e1e1771c19fa"></div><div style='display:none' id="vid_46113482-da36-45a5-bbac-e1e1771c19fa"></div></div>
"/>
<View List="$Resources:core,lists_Folder;/$Resources:core,announce_Folder;" BaseViewID="1" WebPartZoneID="wpz" WebPartOrder="4" ID="g_46113482_da36_45a5_bbac_e1e1771c19fa" />
</File>
</Module>
</Elements>
Summary
Sandboxed Solutions offer great power and flexibility for extending the SharePoint platform with custom functionality. The price of this empowerment is limited access to the SharePoint API and the .NET Framework. One of the common tasks which is affected is provisioning Web Part instances to pages. Although the programmatic approach is not available to SharePoint developers, the declarative way is a good alternative.