Although this book is focused primarily on how you can customize SharePoint through programming, you can do a great deal simply by modifying the XML files that control SharePoint's behavior and that control site provisioning. In this chapter, you'll look at several modifications that are of particular interest to developers:
Associating new icon images with document library file types
Adding custom menu items to the standard Site Settings and document context menus
Creating a custom site definition to include specific web parts on various web-part pages when a new site is provisioned
Adding a custom web form and code that SharePoint will run as part of the provisioning process
Not only can these recipes be used as is, but they are suggestive of a range of opportunities for customizing or automating SharePoint with XML alone or in conjunction with custom .NET programming.
Let's jump right in.
Because Adobe Acrobat PDF files are so common, this is something that I've had to do for every SharePoint installation I've been involved with. I'm sure there's some good reason why Microsoft and Adobe haven't worked out a way to include the PDF icon in the out-of-the-box SharePoint installation; I just can't think what it would be! The process of adding an icon is quick and simple, and it provides a good introduction to the many XML files that underpin your SharePoint installation.
Because SharePoint caches image files at startup, you'll need to execute the IISRESET
command after you've added the icon file and updated the Docicon.xml
file.
According to the Adobe web site, you may use the PDF icon under the following conditions:
You may display the Adobe PDF file icon only on your website, and not in any other manner.
The Adobe PDF file icon must appear by itself, with minimum spacing (the height of the icon) between each side of the icon and any other graphic or textual elements on your web page.
You may not alter the Adobe PDF file icon in any manner (including size, proportions, colors, elements, and so forth) or animate, morph, or otherwise distort its perspective or appearance.
Your use may not be obscene or pornographic, and may not be disparaging, defamatory, or libelous to Adobe, any of its products, or any other person or entity.
Your use may not directly or indirectly imply Adobe's sponsorship, affiliation, or endorsement of your product or service.
Your use may not infringe on any Adobe intellectual property or other rights, may not violate any state or federal laws, and must comply with international IP laws.
You may not create a frame or border environment around Adobe content.
You may not present false or misleading information about Adobe products or services.
Your reference to Adobe, its products, and its website must comply with the general trademark guidelines.
These guidelines do not give you permission to use any other Adobe logos, icons, or trademarks. Adobe reserves the right in its sole discretion to terminate or modify your permission to display the Adobe PDF file icon at any time.
Obtain a copy of the Pdficon_small.gif
file from the site www.adobe.com/misc/linking.html#pdficon
and copy it to the C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateImages
folder.
The preceding location is standard for language pack 1033 (English). If you are using another language version of SharePoint, the target folder may be different.
Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateXMLDocicon.xml
and add an entry for PDF files within the <ByExtension>
element.
Open a command window and execute the IISRESET
command to force SharePoint to reload all document image icons.
When you execute the IISRESET
command, SharePoint will become temporarily unavailable. If you are installing the PDF icon on a production server, you should perform this operation outside of normal business hours.
The following is a fragment of the Docicon.xml
file showing a new entry for files with a .pdf
extension, and the associated Pdficon_small.gif
file.
<?xml version="1.0" encoding="utf-8"?> <DocIcons>... <ByExtension> <Mapping Key="pdf" Value="pdficon_small.gif" /> <Mapping Key="accdt" Value="icaccdb.gif"/> <Mapping Key="accdc" Value="icaccdb.gif"/> <Mapping Key="asax" Value="icasax.gif" OpenControl=""/> ... </ByExtension> ... </DocIcons>
After you've run IISRESET
, you can test that the new icon is correctly associated with PDF files by simply uploading a file with that extension to a document library anywhere on your site. Figure 6-1 shows the new icon associated with an uploaded PDF file.
One of the really cool new capabilities of SharePoint 2007 is that you can add your own options to virtually all of the menus, making it much easier to extend the native SharePoint interface to integrate your custom add-ons. SharePoint exposes this capability through the new Feature interface, which provides a standard (and supported) mechanism to add in all sorts of customizations, including menu items, event handlers, workflows, list definitions, and custom ASP.NET applications. If you ever made modifications to Onet.xml
in SharePoint 2003, you know that after a new web site was provisioned in that version, changes to the Onet.xml
file had no impact. The site-definition template was applied at provisioning time, and that was it!
SharePoint 2007 takes many of the template components that were static after provisioning in 2003, and allows you to "staple" them onto preexisting sites—providing significantly enhanced flexibility of design.
So, although this recipe is about adding custom menu items (or custom actions, as Microsoft calls them), the Feature framework allows you similar flexibility in managing many other components of your site design.
In this recipe, you'll add the ability for end users to provide comments on any document in any library on a site. The recipe will have several components:
A custom action on the drop-down menu associated with document library list items, allowing users to add a comment about the document
An ASP.NET web form for entering and saving those comments
A custom list where the comments will be stored
A custom action on the Site Actions menu that will show users all comments entered by document
A Feature definition for the two custom actions
A batch file containing STSADM
commands to deploy our feature
Of course, we'll need the standard SharePoint components:
Features can be deployed at the Web
, Site
, WebApplication
, or Farm
level. The level determines the scope of activation and deactivation. For example, a feature with a scope of Web
must be activated or deactivated for each individual web site, whereas a feature with a scope of Farm
will be activated or deactivated across all web sites, in all site collections, on all web applications at once. You should therefore select the scope based on how granular you need control to be.
Site, web application, or farm administrators (depending on scope) will have the ability to deactivate or activate any features installed at their level of authority. In fact, most of the base functionality that you can access through the SharePoint Central Administration website (SCAW) and Site Settings pages in SharePoint 2007 is defined through features.
Create a new folder under C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateFeatures
named DocumentComments.
This folder will hold our feature definition XML.
Create a new ASP.NET web application called AddComment
to provide a web form through which users will add their comments.
Add a reference to the Windows SharePoint Services library to the project.
Add a custom list named DocComments
to hold user comments. This list should be accessible to all users who will be adding or viewing comments.
Add a using
(C#) or Imports
(VB.NET) statement to the top of the Default.aspx
page of the AddComment
ASP.NET web application referencing the Microsoft.SharePoint
class library.
The user clicks the Add Comment custom action installed through the feature. This custom action redirects the browser to the web-part page with the Page Viewer web part referring to our AddComment
application.
The page load event of the AddComment
application retrieves the querystring of the web-part page, and parses out the parameters sent to it from the Add Comment custom action.
The extracted parameters are written to the web-part page.
One of the parameters passed in to the AddComment
application was the GUID of the source list. We want to get a handle to the list based on that GUID to find its name and the URL to its root folder.
The last bit of information we want to display on the page is a list of already existing comments for the source document. We'll do that by getting a handle to the DocComments
list, which contains all user comments, converting the list items collection to a DataTable
, filtering the data by using a DataView
, and then displaying that data by using a GridView
web control.
If the user clicks the Save button, we'll write the text from the Comment
control to a new item in the DocComments
list (writing to lists was covered in Chapter 3).
All that's left is to return the user to the list page from which they came. Because the AddComment
application is running in a Page Viewer web part, a Response.Redirect()
would display the list page in that same page viewer, which is not what we want. To force the list page to display at the top level, we'll write a window.open()
JavaScript command to the page.
An alternative way of deploying these types of add-ons is to deploy them to a subfolder of the _layouts
directory (C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateLayouts
) and then redirect the browser to that page. This removes the need to create virtual directories in IIS. This also allows you to use the application.master
page in the layouts directory so that the custom page maintains the SharePoint look and feel. However, I generally prefer to deploy custom applications outside of the ...12TemplateLayouts
folder so as to avoid modifying the out-of-the-box SharePoint structure to the extent possible. For more information, see http://msdn2.microsoft.com/en-us/library/cc297200.aspx
.
Each feature in SharePoint is stored in its own folder under the .../12/Template/Features
folder. In this case, we'll name our feature DocumentComments
. Place the following XML in a file named Feature.xml
in the .../12/Template/Features/DocumentComments
folder.
<?xml version="1.0" encoding="utf-8" ?> <Feature xmlns="http://schemas.microsoft.com/sharepoint/" Id="FD20EE04-D0C4-4bad-B909-D453D89CF7F5" Scope="Web" Title="DocumentComments" Version="1.0.0.0" Description="Add Document Comments feature."> <ElementManifests> <ElementManifest Location="DocumentComments.xml" /> </ElementManifests> </Feature>
Key components of each feature are as follows:
Id
:A unique GUID identifying this feature to SharePoint. You can use the Visual Studio Tools
The Create GUID utility will generate a GUID enclosed in {} brackets. You will need to remove these after you paste the GUID into your feature definition, or SharePoint will throw an error when you attempt to install your feature.
Scope
:Can be one of Web
, Site
, WebApplication
, or Farm
. For our purposes, we'll set the Scope
to Web
.
Title
:The title displayed in the Site Features page under Site Settings.
Description
:The description displayed in the Site Features page under Site Settings.
ElementManifest
:This points to the external file containing the custom action definitions.
Each feature references one or more ElementManifest
s. In our example, we have just one, DocumentComments.xml
.
Be sure to change the URL references to http://<yourserver>/<yoursiteurl>/Pages/Comments.aspx
in the following <UrlAction>
element to point to the server and site, and replace <yourdoclib>
with the document library where you will be adding your Comments.aspx
page.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <!-- Action to add action to drop-down menu associated with --> <!-- document library list items. --> <CustomAction Id="DC5D5B12-9ACA-4d95-AC54-0E42D4BB4051" RegistrationType="List" RegistrationId="101" Location="EditControlBlock" Sequence="999" ImageUrl="/_layouts/images/NMW16.GIF" Title="Add Comment..."> <UrlAction Url="http://<yourserver>/<yoursiteurl>/<yourdoclib>/ Comments.aspx?itemId={ItemId}&itemUrl={ItemUrl} &siteUrl={SiteUrl}&listId={ListId}"/> </CustomAction> <!-- Action to add action to Site Actions menu --> <!-- The "UrlAction" element should point to the list where your --> <!-- AddComments application is storing the user comments --> <CustomAction Id="E1E69684-CDFC-4981-B7E0-57DE6EB3C3C8" GroupId="SiteActions" Location="Microsoft.SharePoint.StandardMenu" Sequence="999" ImageUrl="/_layouts/images/NMW16.GIF" Title="View Comments..."> <UrlAction Url="http://<yourserver>/<yoursiteurl>/lists/DocComments"/> </CustomAction> </Elements>
The image file Doclibrary.gif
ships with MOSS but is not installed in WSS-only installations. If you are running a WSS-only installation, you may substitute any other available image file found in the ...12TemplateImages
folder, such as Editicon.gif
.
Key components of each element manifest are as follows:
Type
:In our case, this element is of type CustomAction
. Note that different feature element types will have differing sets of required and optional properties.
Id
:As with the parent feature definition, this must be a unique GUID with the leading and trailing braces removed.
Location
:This tells SharePoint which menu to attach this custom action to.
Sequence
:The ordinal position of this menu item on the target menu.
ImageUrl
:An optional icon image file to associate with this menu item.
Title
:The text to display on the menu.
UrlAction
:The page to redirect the user's browser to when this menu item is selected.
The UrlAction
for the EditControlBlock
action refers to the web-part page on which we will place a Page Viewer web part to contain our AddComment
application. Be sure to pass all the querystring parameters to the web-part page, so that the AddComment
application can extract them at runtime.
The following commands, entered into a Windows .cmd
file, will save you time when installing and reinstalling your feature. They also provide good examples of the STSADM
commands you'll use when working with features.
To execute the STSADM
command, you will need to open a command window and navigate to C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12in
, or add that path to your environment. Also, be sure to replace <yourserver>
and <yoursite>
in the following example with the appropriate values for your SharePoint environment.
stsadm -o deactivatefeature -filename DocumentCommentsfeature.xml -url http://<yourserver>/<yoursite>
This command deactivates the feature if it is activated. When you first run the script, you'll receive a warning that the feature had not already been activated. Note that the –url
parameter references a web site URL because the feature Scope
is Web
.
stsadm -o uninstallfeature -filename DocumentCommentsfeature.xml –force
After a feature is deactivated, it can be uninstalled from the web site. As with the preceding command, the first time you run the script, you will receive a warning that the specified feature is not installed yet.
stsadm -o installfeature -filename DocumentCommentsfeature.xml –force
Here we're installing the feature. Note that SharePoint assumes that the path to the specified feature folder will be under ...12TemplateFeatures
.
stsadm -o activatefeature -filename DocumentCommentsfeature.xml -url http://<yourserver>/<yoursite>
Finally, the activatefeature STSADM
command turns the feature on. This is equivalent to a site administrator navigating to the Site Settings
Imports System Imports System.Data Imports System.Configuration Imports System.Web Imports System.Web.Security Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Web.UI.WebControls.WebParts Imports System.Web.UI.HtmlControls Imports Microsoft.SharePoint Imports Microsoft.SharePoint.WebControls Partial Public Class _Default Inherits System.Web.UI.Page Const COMMENTS_SITE_URL As String = "http://<yourserver>/<siteurl>" Const COMMENTS_WEB_NAME As String = "<webname>" Const COMMENTS_LIST As String = "DocComments" Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Try ' Step 2: Get parse querystring for parameters. ' We are assuming that this page is hosted in ' a page viewer web part on a web-part page. ' So get the parameters passed to the web-part ' page contining this page in a PageViewer Dim querystring As String() = _ Server.UrlDecode( _ Request.UrlReferrer.Query).ToLower().Replace("?", "").Split("&"c) For i As Integer = 0 To querystring.Length - 1 ' Step 3: Display querystring parameters If querystring(i).IndexOf("itemid=") <> −1 Then lblItemId.Text = querystring(i).Split("="c)(1) End If If querystring(i).IndexOf("itemurl=") <> −1 Then hlItemUrl.NavigateUrl = querystring(i).Split("="c)(1) hlItemUrl.Text = querystring(i).Split("="c)(1) End If If querystring(i).IndexOf("listid=") <> −1 Then lblListId.Text = querystring(i).Split("="c)(1) End If If querystring(i).IndexOf("siteurl=") <> −1 Then hlSiteUrl.NavigateUrl = querystring(i).Split("="c)(1) hlSiteUrl.Text = querystring(i).Split("="c)(1) End If Next
' Step 4: Get the list name and URL from its GUID Dim site As New SPSite(hlSiteUrl.NavigateUrl) 'Extract the web name Dim webUrl As String webUrl = hlSiteUrl.NavigateUrl.ToLower().Replace(site.Url, "") If webUrl.IndexOf("/") = 0 Then webUrl = webUrl.Substring(1) EndIf Dim web As SPWeb = site.OpenWeb(webUrl); Dim guid As New Guid(lblListId.Text) Dim origList As SPList = web.Lists(guid) lblListName.Text = origList.Title hlListUrl.NavigateUrl = web.Url + "/" + origList.RootFolder.Url hlListUrl.Text = web.Url + "/" + origList.RootFolder.Url ' Step 5: Display existing comments for this document Dim siteComments As New SPSite(COMMENTS_SITE_URL) Dim webComments As SPWeb = _ siteComments.OpenWeb(COMMENTS_WEB_NAME) Dim docComments As SPList = webComments.Lists(COMMENTS_LIST) Dim dtComments As DataTable = docComments.Items.GetDataTable() dtComments.TableName = "Comments" Dim dvComments As New DataView( _ dtComments, "ItemUrl='" + hlItemUrl.NavigateUrl + "'", _ "Created DESC", DataViewRowState.CurrentRows) GridView1.DataSource = dvComments GridView1.DataBind() webComments.Dispose() siteComments.Dispose() Catch ex As Exception End Try End Sub Protected Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles cmdSave.Click ' Get handle to web Dim siteComments As New SPSite(COMMENTS_SITE_URL) Dim webComments As SPWeb = _ siteComments.OpenWeb(COMMENTS_WEB_NAME) webComments.AllowUnsafeUpdates = True ' Step 6: Write new comment to DOCCOMMENTS list Dim docComments As SPList = webComments.Lists(COMMENTS_LIST) Dim item As SPListItem = docComments.Items.Add() item("ItemId") = lblItemId.Text item("ItemUrl") = hlItemUrl.NavigateUrl item("ListId") = lblListId.Text item("SiteUrl") = hlSiteUrl.NavigateUrl item("Comment") = txtComments.Text item.Update()
webComments.Dispose() siteComments.Dispose() ' Step 7: Return user to list returnToList() End Sub Protected Sub cmdCancel_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles cmdCancel.Click returnToList() End Sub Private Sub returnToList() ' Because this page is running in a page viewer ' (i.e. in an <IFRAME>), a redirect statement would ' display the target URL in the frame, whereas we ' want the target page displayed at the top level. ' To accomplish this, we'll insert a bit of JavaScript ' to perform a window.open(). Response.Write("<script>window.open('" + hlListUrl.NavigateUrl + _ "','_top'),</script>") End Sub End Class
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using Microsoft.SharePoint; using Microsoft.SharePoint.WebControls; public partial class _Default : System.Web.UI.Page { const string COMMENTS_SITE_URL = "http://<yourserver>/<siteurl>"; const string COMMENTS_WEB_NAME = "<webname>"; const string COMMENTS_LIST = "DocComments"; protected void Page_Load(object sender, EventArgs e) { try { // Step 2: Get parse querystring for parameters. // We are assuming that this page is hosted in // a page viewer web part on a web-part page. // So get the parameters passed to the web-part // page contining this page in a PageViewer
string[] querystring = Server.UrlDecode( Request.UrlReferrer.Query).ToLower().Replace("?", "") .Split('&'), // Step 3: Display querystring parameters for (int i = 0; i < querystring.Length; i++) { if (querystring[i].IndexOf("itemid=") != −1) lblItemId.Text = querystring[i].Split('=')[1]; if (querystring[i].IndexOf("itemurl=") != −1) { hlItemUrl.NavigateUrl = querystring[i].Split('=')[1]; hlItemUrl.Text = querystring[i].Split('=')[1]; } if (querystring[i].IndexOf("listid=") != −1) lblListId.Text = querystring[i].Split('=')[1]; if (querystring[i].IndexOf("siteurl=") != −1) { hlSiteUrl.NavigateUrl = querystring[i].Split('=')[1]; hlSiteUrl.Text = querystring[i].Split('=')[1]; } } // Step 4: Get the list name and URL from its GUID SPSite site = new SPSite(hlSiteUrl.NavigateUrl); // Extract the web URL string webUrl = hlSiteUrl.NavigateUrl.ToLower().Replace(site.Url, ""); if (webUrl.IndexOf("/") == 0) webUrl = webUrl.Substring(1); SPWeb web = site.OpenWeb(webUrl); Guid guid = new Guid(lblListId.Text); SPList origList = web.Lists[guid]; lblListName.Text = origList.Title; hlListUrl.NavigateUrl = web.Url + "/" + origList.RootFolder.Url; hlListUrl.Text = web.Url + "/" + origList.RootFolder.Url; // Step 5: Display existing comments for this document SPSite siteComments = new SPSite(COMMENTS_SITE_URL); SPWeb webComments = siteComments.OpenWeb(COMMENTS_WEB_NAME); SPList docComments = webComments.Lists[COMMENTS_LIST]; DataTable dtComments = docComments.Items.GetDataTable(); dtComments.TableName = "Comments"; DataView dvComments = new DataView( dtComments, "ItemUrl='" + hlItemUrl.NavigateUrl + "'", "Created DESC", DataViewRowState.CurrentRows); GridView1.DataSource = dvComments; GridView1.DataBind(); web.Dispose(); site.Dispose();
webComments.Dispose(); siteComments.Dispose(); } catch (Exception ex) { } } protected void cmdSave_Click(object sender, EventArgs e) { // Get handle to web SPSite siteComments = new SPSite(COMMENTS_SITE_URL); SPWeb webComments = siteComments.OpenWeb(COMMENTS_WEB_NAME); webComments.AllowUnsafeUpdates = true; // Step 6: Write new comment to DOCCOMMENTS list SPList docComments = webComments.Lists[COMMENTS_LIST]; SPListItem item = docComments.Items.Add(); item["ItemId"] = lblItemId.Text; item["ItemUrl"] = hlItemUrl.NavigateUrl; item["ListId"] = lblListId.Text; item["SiteUrl"] = hlSiteUrl.NavigateUrl; item["Comment"] = txtComments.Text; item.Update(); webComments.Dispose(); siteComments.Dispose(); // Step 7: Return user to list returnToList(); } protected void cmdCancel_Click(object sender, EventArgs e) { returnToList(); } private void returnToList() { // Because this page is running in a page viewer // (i.e. in an <IFRAME>), a redirect statement would // display the target URL in the frame, whereas we // want the target page displayed at the top level. // To accomplish this, we'll insert a bit of JavaScript // to perform a window.open(). Response.Write( "<script>window.open('" + hlListUrl.NavigateUrl + "','_top'),</script>" ); } }
You will also need to tell ASP.NET to pass the user's credentials through to the AddComment
application. You do this by editing the Web.config
file in the AddComment
application, adding the <identity impersonate="true"/>
element just after the <authentication mode="Windows"/>
tag.
Whether you use the VB or C# version of the AddComment
application, the application will be accessed from http://<yourserver>/_layouts/ AddComment/Default.aspx
(or some similar virtual path under /_layouts
). To do this, open the IIS management console, navigate to the web site SharePoint – 80/_layouts
, right-click on the _layouts
node, and choose the New
Click Next, and name the new virtual directory AddComment
( Figure 6-3).
Enter the path to the physical application and click Next ( Figure 6-4).
Tell IIS to allow scripts to run from this virtual directory, and click Next and then Finish ( Figure 6-5). The application will now be accessible from any SharePoint web site as .../_layouts/AddComment/Default.aspx
.
Before you can use your new custom action, you'll need to create a web-part page to display the AddComment/Default.aspx
form that you have created:
The following instructions assume you have a document library named Pages
in the site http://<yourserver>/<yoursite>
. If this is not the case, you should create the Pages
document library before proceeding.
Navigate to the http://<yourserver>/<yoursite>
(replace <yourserver>
and <yoursite>
with values you specified in the preceding feature definition), and select Site Actions
Click the Web Part Page option ( Figure 6-7).
Create a new page name, Comments.aspx
, placing it in the document library named Pages
( Figure 6-8).
In design mode on the page Comments.aspx
, add a Page Viewer web part by clicking on the Add a Web Part banner. Set the Page Viewer's link property to http://<yourserver>/_layouts/AddComment/Default.aspx
, as shown in Figure 6-9. In addition, set the height of the page to 10 inches.
Exit edit mode. Your page should look similar to Figure 6-10.
You're not quite there yet. You'll need a custom list named DocComments
in the same site you placed your web-part page in the preceding steps. This list will hold all user comments. The finished list should have the fields shown in Figure 6-11.
Now that all the pieces are in place and installed, let's take our comments feature for a spin. First, be sure to run the command script to install the two custom actions. Then navigate to a document library on the web site in which you installed the feature (http://<yourserver>/<yoursite>
), mouse over a document, and display the context menu. You should see a new option titled Add Comment, as shown in Figure 6-12. Click the Add Comment link to display the web-part page containing a reference to the AddComment
application Default.aspx
page.
The AddComment
application will be displayed in the Page Viewer web part, as shown in Figure 6-13.
As shown in Figure 6-13, the AddComment
application extracts the querystring parameters sent to its containing web-part page and displays them on the web form, along with any preexisting comments for the source document.
You will also find a new option on the Site Actions menu titled View Comments, as shown in Figure 6-14.
Selecting this option will redirect the browser to the default view of the DocComments
list on the Docs
web site ( Figure 6-15).
Be sure that the DocComments
list permissions (and those of the web site containing it) are such that all users who can access the Add Comments or View Comments custom actions can also insert new items and navigate to the view.
Onet.xml
plays a central role in the process of provisioning new SharePoint sites. When a new site is requested, Onet.xml
provides the "punch list" of components to be added. These components include characteristics such as features, lists, and web parts, to name a few. After a site is provisioned, the Onet.xml
file has essentially done its job, and will not be read again—which is not the case with other site-definition elements of which Onet.xml
is a part.
In this recipe, you'll look at how to edit the Onet.xml
file to instruct SharePoint to add specific web parts to specific pages at provision time. You'll add a Page Viewer and a Content Editor web part to the Default.aspx
page of a new site, but you can follow this procedure to add any web part that is registered as safe in SharePoint's Web.config
file.
A copy of the C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts
folder. In this recipe, I copied it to C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-Mgerow2
.
Web-part definition (.dwp
or .webpart
) files for the web parts you want to add to the page.
Never edit the out-of-the-box site-definition templates that ship with SharePoint. If you introduce errors to some components of those definitions, SharePoint may become inoperable, and Microsoft may not support your installation. You should therefore always make a copy of the out-of-the-box site definition, or another working custom site definition, that most closely matches the definition you are trying to create to serve as the basis for your new definition.
Microsoft may refuse to support your SharePoint installation if you've made edits to any of the out-of-the-box site-definition templates. For that reason, you should always make your customizations to copies of these templates.
SharePoint provides scant information about any errors it may find in an Onet.xml
file during provisioning, so it's best to make your customizations a little bit at a time, testing those changes at each step by provisioning a new site. That way you can back out an erroneous change to the Onet.xml
file without losing too much work.
Remember that CAML (the flavor of XML used by SharePoint) is case sensitive—as is all XML—and all element tags must be properly closed.
Add a Page Viewer and Content Editor web part to a web-part page on your site. Set their properties as desired. For example, specify a URL to display in the Page Viewer and add some text to display in the Content Editor web parts.
Export each of the web parts by using the Export option on the web part's menu. You will need the resulting .dwp
or .webpart
files later.
Create a copy of the folder C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts
. I named my copy sts-MGE ROW2
, but you may give your copy any name you wish as long as it's unique.
Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-Mgerow2XMLOnet.xml
in Visual Studio, Notepad, or whatever text editor you prefer.
Find the two following sequences of elements just after the </Configurations>
closing tag:
<Modules> <Module Name="Default" Url="" Path=""> <File Url="default.aspx" NavBarHome="True">
Within the <File>
element, add the following two child elements:
<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1"> <![CDATA[ ]]> </AllUsersWebPart> <AllUsersWebPart WebPartZoneID="Right" WebPartOrder="1"> <![CDATA[ ]]> </AllUsersWebPart>
Open the .dwp
or .webpart
file created when you exported the Page Viewer web part, and copy the <WebPart></WebPart>
element and all of its contents in between the first set of <![CDATA[
and ]]>
tags (see the following section, "Recipe—Page Viewer Web Part").
Open the .dwp
or .webpart
file created when you exported the Content Editor web part, and copy the <WebPart></WebPart>
element and all of its contents in between the second set of <![CDATA[
and ]]>
tags (see the upcoming section "Recipe—Content Editor Web Part").
Create a copy of C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12Template1033XMLWebtemp.xml
. I named my copy Webtempsts-Mgerow.xml
, but you may choose any name as long as it begins with Wemtemp
, has an .xml
extension, and is unique. At startup, SharePoint concatenates the contents of all XML files beginning with Webtemp
in this folder to build its list of available site definitions.
Open the new Webtemp
file you created in the preceding step, and delete all the <Template>
elements within the <Templates>
parent element.
Within the <Templates>
element, create the following child element:
<Template Name="STS-MGEROW2" ID="1002"> <Configuration ID="0" Title="Adding web parts directly to ONET.XML" Hidden="FALSE" ImageUrl="/_layouts/images/stsprev.png" Description="A custom site definition based on STS. The only difference is the addition of web-part definitions directly into ONET.XML by using the AllUsersWebPart element." DisplayCategory="Chapter 6" > </Configuration> </Template>
The template ID value must be unique within your SharePoint installation. To avoid collisions with out-of-the-box templates, I typically begin my numbering at 1001. Failure to provide a unique ID will result in an error when you attempt to provision a site with the new template.
Issue an IISRESET
console command to force SharePoint to reload its list of site definitions.
Figure 6-16 shows how the <Configuration>
attributes map to template-selection page elements in SharePoint.
The following is the XML that SharePoint creates when I export my Page Viewer web part, without the opening <?xml...>
tag. This is what you will need to pass between the <![CDATA[
and ]]>
tags of the first <AllUsersWebPart>
element. Essentially you are telling SharePoint to automate the process of adding a Page Viewer web part and setting its properties when it provisions a new web site.
<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>And here's my page viewer web part!</Title> <FrameType>Default</FrameType> <Description>Use to display linked content, such as files, folders, or Web pages. The linked content is isolated from other content on the Web Part Page.</Description>
<IsIncluded>true</IsIncluded> <ZoneID>Left</ZoneID> <PartOrder>0</PartOrder> <FrameState>Normal</FrameState> <Height>1000px</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 /> <IsIncludedFilter /> <Assembly>Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly> <TypeName>Microsoft.SharePoint.WebPartPages.PageViewerWebPart</TypeName> <ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/PageViewer"> http://www.apress.com/book/view/1430209615</ContentLink> <SourceType xmlns="http://schemas.microsoft.com/WebPart/v2/PageViewer">URL </SourceType> </WebPart>
As with the Page Viewer web-part definition in the preceding section, you will paste the following portion of the Content Editor web-part definition file between the <![CDATA[
and ]]>
tags of the second <AllUsersWebPart>
element.
<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>Hello World web part</Title> <FrameType>Default</FrameType> <Description>Use for formatted text, tables, and images.</Description> <IsIncluded>true</IsIncluded> <ZoneID>Right</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 /> <IsIncludedFilter /> <Assembly>Microsoft.SharePoint, Version=12.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>
When SharePoint exports a Content Editor web part, it will place the text contained in the <Content>
element in a <![CDATA[ ]]>
element. Because CAML doesn't support nested <![CDATA[ ]]>
elements, you will need to remove that element before pasting your <WebPart>
element into Onet.xml
. For example, the original contents of the <Content>
element as exported by SharePoint was <![CDATA[Hello World!]]>
, but was altered to Hello World
when added to Onet.xml
.
At this point, you've created your new site definition, edited the Onet.xml
file to contain the two web-part definitions, added a new Webtemp*.xml
file to tell SharePoint about the new site definition, and you've run the IISRESET
command to force SharePoint to refresh its list of site definitions in memory. You're now ready to provision a site based on the new template.
To test your new site-definition template, follow these steps:
Select the Site Actions
Enter a title and URL for the new site.
Click the Chapter 6 tab in the template section and highlight the Adding Web Parts Directly to Onet.xml
template.
Click the Create button. After the new site has been created, you should see a page similar to that shown in Figure 6-17.
Note that in addition to the standard image and links web parts that are part of the base sts
template, our two additional web parts are displayed.
In this recipe, we left the preexisting web-part definitions. However, we could have easily deleted the preexisting <AllUsersWebPart>
elements to start with a clean slate.
The out-of-the-box sts Onet.xml
file references only one page, Default.aspx
. However, you can easily add more pages at provision time, and assign web parts to each of them in this manner. To do so, simply make a copy of the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-MGEROW2Default.aspx
under a different name (for example, Default2.aspx
), and add a new <File>
element under the same <Module>
element that contains our reference to Default.aspx
, but with its Url
attribute changed to reference Default2.aspx
. Edit or add <AllUsersWebPart>
elements to this second <File>
element as needed.
SharePoint's site-definition template framework is incredibly flexible, providing you a declarative language in the form of CAML (a special variety of XML). Through the use of CAML in Onet.xml
combined with the features framework, you can do a great deal. But, as a programmer I really appreciate that Microsoft included a way to plug in whatever code I want to execute at the end of the site-provisioning process. This is done by inserting an <ExecuteUrl>
element into the Onet.xml
file for a given site-definition template. The <ExecuteUrl>
element tells SharePoint what ASP.NET web page to redirect to after the site has been provisioned. That page can gather additional data from the site requestor, perform additional site definition under your control, and optionally redirect the user to any page on the new site.
In this recipe, you'll see how easy it is to incorporate your postprovisioning code into a site-definition template to modify the web parts on the Default.aspx
page of a new site before it's presented to the requestor. In this instance, you'll display a page giving the requestor the following three options:
Delete any preexisting web parts that were part of the site-definition template
Add a Hello World Content Editor web part
Add a Page Viewer web part displaying a URL of the requestor's choice
Microsoft.SharePoint
class library
Microsoft.SharePoint.WebControls
class library
Microsoft.SharePoint.WebParts
class library
The AddContentEditorWebPart()
and AddWebPart()
methods developed in Recipe 1-6 in Chapter 1
A copy of the ..12TemplateSiteTemplatessts
folder (for example, ..12TemplateSiteTemplatesSts-new
)
A copy of the ..12Template1033XMLWebtemp.xml
file (for example, ..12Template1033XMLWebtempsts-new.xml
)
The reference to 1033
in the preceding ingredient is specific to the English language version of SharePoint. If you are using a different language version, substitute the appropriate numeric designation.
You will want to configure the ASP.NET application hosting the ExecuteUrl
to be accessible under the virtual _layouts
path. Rather than physically placing the application files under that path, you can create a virtual directory in IIS under _layouts
pointing to the physical location of the application on your SharePoint server.
Create a new site-definition template by copying one of the existing definitions under ..12TemplateSiteTemplates
. In this recipe, I copied the sts
folder, which contains the standard team site definition.
You should never edit the out-of-the-box site-definition templates that ship with MOSS or WSS. Instead, always copy one of those templates or a custom definition derived from one of those templates to form the basis for a new custom definition.
SharePoint doesn't provide much of a safety net when editing Onet.xml
or Webtemp*.xml
files, which is one reason never to edit the stock versions. In fact, it's not a bad idea to use configuration-management software such as Microsoft Visual SourceSafe to keep track of prior versions in case you need to roll back edits that render either file unusable.
Be sure to give your new definition template a unique ID, or SharePoint will balk when you attempt to use it. You do this by assigning a unique value to the ID attribute of the <Template>
element in your Webtemp*.xml
file.
Create a copy of the folder C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts
. I named my copy sts-Mgerow
, but you may give your copy any name you wish as long as it's unique.
Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-MGEROWxmlOnet.xml
in Visual Studio, Notepad, or whatever text editor you prefer.
Find the <Configuration ID="0" Name="Default">
element, and within this add the following child element:
<ExecuteUrl Url="_layouts/SelectWebParts/SelectWebParts.aspx"></ExecuteUrl>
Note the use of the _layouts/ virtual path. This is important because we want our SelectWebParts application to be able to detect the context of the SharePoint web site being created.
Save your changes to Onet.xml
.
Create a copy of C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12Template1033XMLWebtemp.xml
. I named my copy Webtempsts-Mgerow.xml
, but you may choose any name as long as it begins with Webtemp
, has an .xml
extension, and is unique. At startup, SharePoint concatenates the contents of all XML files beginning with Webtemp
in this folder to build its list of available site definitions.
Within the <Templates>
element, remove the existing contents of the file's <Templates>
element, and create the following child element:
<Template Name="STS-MGEROW" ID="1001"> <Configuration ID="0" Title="Execute Url Sample" Hidden="FALSE" ImageUrl="/_layouts/images/stsprev.png" Description="A custom site definition based on STS" DisplayCategory="Chapter 6" > </Configuration> </Template>
Issue an IISRESET
console command to force SharePoint to reload its list of site definitions.
Figure 6-18 shows how the <Configuration>
attributes map to template-selection page elements in SharePoint.
The Template ID
attribute must be a unique value within the collection of site-definition templates on your server, or SharePoint will not recognize your template. I typically begin numbering my templates at 1001 to achieve this end.
Create a new ASP.NET application on your SharePoint. I named my C# application SelectWebParts-CS
, but you may name it as you wish.
Rename Default.aspx
to SelectWebParts.aspx
in your new ASP.NET application.
Add a reference to the Windows SharePoint Services .NET assembly.
Add a using
(C#) or Imports
(VB.NET) reference to the Microsoft.SharePoint
class library.
Open up the IIS Manager application (found under Administrative Tools) and create a new virtual directory named SelectWebParts
under Web Sites/SharePoint – 80/_layouts
that points to the root folder of your SelectWebParts-CS
ASP.NET application ( Figure 6-19).
If you are not using the default SharePoint—80
web application, please replace references to that web application as appropriate.
Right-click on the new virtual directory and select the Properties context menu item to open its properties dialog box.
Click the Create button to the right of the Application Name field if it's grayed out, to designate this virtual directory as a self-contained ASP.NET application.
Select SharePoint—80 from the Application Pool drop-down list to ensure that your application runs in the same security context as SharePoint ( Figure 6-20).
The SharePoint—80 application pool will exist in a default SharePoint installation. If your installation differs, you should select the same application pool as that being used the SharePoint web application you will be using with this recipe.
We're now ready to code our SelectWebParts
application.
Use the SPControl.GetContextWeb()
method to get a handle to the new web site just provisioned.
If the user has selected the checkbox to remove preexisting web parts, iterate through the collection of web parts on the new web site's home page, removing each.
As each web part is deleted from the web-part collection, the collection Count
is decremented, so we must iterate backward through the collection to avoid a runtime error.
If the user has selected the checkbox to add a new Content Editor web part, call the AddContentEditorWebPart()
custom method we created in Chapter 1 to add the Hello World web part.
If the user has selected the checkbox to add a new Page Viewer web part, call the AddWebPart()
method we created in Chapter 1 to add a page view of the URL that the user entered into the SelectWebParts.aspx
web form.
When all postprovisioning modifications are complete, redirect the user's browser to the home page (typically Default.aspx
) of the new web site.
Figure 6-21 shows the page layout for SelectWebParts.aspx
.
The following fields are defined on the form:
cbRemoveExisting
:A checkbox indicating whether existing web parts should be removed from the new web site's Default.aspx
page
cbCEWP
:A checkbox indicating whether a new Hello World Content Editor web part should be added to Default.aspx
cbPVWP
:A checkbox indicating whether a new Page Viewer web part should be added to Default.aspx
txtPVUrl
:The source URL to assign to the new Page Viewer web part if added
cmdFinish
:A Submit button that calls the Finish_Click()
form event handler
Imports System Imports System.Data Imports System.Configuration Imports System.Web Imports System.Web.Security Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Web.UI.WebControls.WebParts Imports System.Web.UI.HtmlControls Imports Microsoft.SharePoint Imports Microsoft.SharePoint.WebControls Imports Microsoft.SharePoint.WebPartPages Imports System.Xml Partial Public Class _Default Inherits System.Web.UI.Page Private Shared Function AddContentEditorWebPart(ByVal strContent As String, _ ByVal strTitle As String, _ ByVal strSiteUrl As String, _ ByVal strWebName As String, _ ByVal strDocLibName As String, _ ByVal strPage As String, _ ByVal strZone As String, _ ByVal numOrder As Integer, _ ByVal pScope As System.Web.UI.WebControls.WebParts.PersonalizationScope) _ As Microsoft.SharePoint.WebPartPages.ContentEditorWebPart Try ' Create an empty Content Editor web part. Dim cewp As New Microsoft.SharePoint.WebPartPages.ContentEditorWebPart() ' Create an xml element object and transfer the content 'into the web part. Dim xmlDoc As New XmlDocument() Dim xmlElem As System.Xml.XmlElement = xmlDoc.CreateElement("xmlElem") xmlElem.InnerText = strContent cewp.Content = xmlElem ' Call generic method to add the web part cewp = AddWebPart(cewp, _ strTitle, _ strSiteUrl, _ strWebName, _
strDocLibName, _ strPage, _ strZone, _ numOrder, _ System.Web.UI.WebControls.WebParts.PersonalizationScope.[Shared]) Return cewp Catch ex As Exception Throw New Exception("AddContentEditorWebPart() error: " + ex.Message) End Try End Function Private Shared Function AddWebPart(_ ByVal oWebPart As System.Web.UI.WebControls.WebParts.WebPart, _ ByVal strTitle As String, _ ByVal strSiteUrl As String, _ ByVal strWebName As String, _ ByVal strDocLibName As String, _ ByVal strPage As String, _ ByVal strZone As String, _ ByVal numOrder As Integer, _ ByVal pScope As System.Web.UI.WebControls.WebParts.PersonalizationScope) _ As System.Web.UI.WebControls.WebParts.WebPart Try ' Get handles to site, web, and page to which ' web part will be added. Dim site As New SPSite(strSiteUrl) Dim web As SPWeb = site.OpenWeb(strWebName) ' Enable update of page web.AllowUnsafeUpdates = True Dim webParts As SPLimitedWebPartManager If (strDocLibName <> "") Then webParts = web.GetLimitedWebPartManager(strDocLibName + "/" _ + strPage, pScope) Else webParts = web.GetLimitedWebPartManager(strPage, pScope) End If ' If web-part page is in a document library, ' disable checkout requirement ' for duration of update Dim list As SPList = Nothing Dim origForceCheckoutValue As Boolean = False If (strDocLibName <> "") Then list = web.Lists(strDocLibName) origForceCheckoutValue = list.ForceCheckout list.ForceCheckout = False list.Update() End If
' Add the web part oWebPart.Title = strTitle webParts.AddWebPart(oWebPart, strZone, numOrder) ' Save changes back to the SharePoint database webParts.SaveChanges(oWebPart) web.Update() ' If necessary, restore ForceCheckout setting If (strDocLibName <> "") Then list.ForceCheckout = origForceCheckoutValue list.Update() End If web.Dispose() site.Dispose() Return oWebPart Catch ex As Exception Throw New Exception(("AddWebPart() error: " + ex.Message)) End Try End Function Protected Sub Finish_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles cmdFinish.Click ' Step 1: Get handle to web site being created Dim web As SPWeb = SPControl.GetContextWeb(Context) web.AllowUnsafeUpdates = True ' Step 2: If requested, clear out any existing ' web parts If cbRemoveExisting.Checked Then Dim webparts As SPLimitedWebPartManager _ = web.GetLimitedWebPartManager( _ "default.aspx", PersonalizationScope.[Shared]) For i As Integer = webparts.WebParts.Count - 1 To −1 + 1 Step −1 webparts.DeleteWebPart(webparts.WebParts(i)) Next End If ' Step 3: If requested, add an instance of a SharePoint ' "ContentEditorWebPart", which is a descendent of ' the generic .NET 2.0 WebPart class If cbCEWP.Checked Then Dim oCEwp As New _ Microsoft.SharePoint.WebPartPages.ContentEditorWebPart() oCEwp = AddContentEditorWebPart( _ "Hello World!", _ "Hello World web part", _ web.Site.Url.ToString(), web.ServerRelativeUrl.Substring(1), "", _ "Default.aspx", _ "Right", 0, _
System.Web.UI.WebControls.WebParts.PersonalizationScope.[Shared]) End If ' Step 4: If requested, add a PageViewer web part If cbPVWP.Checked Then Dim oPVwp As New Microsoft.SharePoint.WebPartPages.PageViewerWebPart() oPVwp.SourceType = PathPattern.URL oPVwp.ContentLink = txtPVUrl.Text oPVwp.Height = "1000px" oPVwp = AddWebPart(oPVwp, _ "And here's my page viewer web part!", _ web.Site.Url.ToString(), _ web.ServerRelativeUrl.Substring(1), "", _ "Default.aspx", _ "Left", 0, _ System.Web.UI.WebControls.WebParts.PersonalizationScope.[Shared]) End If ' Step 5: Now take the user to the home page of the new ' web site Response.Redirect(web.ServerRelativeUrl) End Sub ' Determine whether Url text box should ' be enabled based on whether ' option to add a Page Viewer web part ' is checked Protected Sub cbPVWP_CheckedChanged( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles cbPVWP.CheckedChanged txtPVUrl.Enabled = cbPVWP.Checked End Sub End Class
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using Microsoft.SharePoint; using Microsoft.SharePoint.WebControls; using Microsoft.SharePoint.WebPartPages; using System.Xml;
public partial class _Default : System.Web.UI.Page { private static Microsoft.SharePoint.WebPartPages.ContentEditorWebPart AddContentEditorWebPart( string strContent, string strTitle, string strSiteUrl, string strWebName, string strDocLibName, string strPage, string strZone, int numOrder, System.Web.UI.WebControls.WebParts.PersonalizationScope pScope) { try { // Create an empty content editor web part. Microsoft.SharePoint.WebPartPages.ContentEditorWebPart cewp = new Microsoft.SharePoint.WebPartPages.ContentEditorWebPart(); // Create an xml element object and transfer the content //into the web part. XmlDocument xmlDoc = new XmlDocument(); System.Xml.XmlElement xmlElem = xmlDoc.CreateElement("xmlElem"); xmlElem.InnerText = strContent; cewp.Content = xmlElem; // Call generic method to add the web part cewp = (Microsoft.SharePoint.WebPartPages.ContentEditorWebPart) AddWebPart( cewp, strTitle, strSiteUrl, strWebName, strDocLibName, strPage, strZone, numOrder, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared); return cewp; } catch (Exception ex) { throw new Exception( "AddContentEditorWebPart() error: " + ex.Message); } }
private static System.Web.UI.WebControls.WebParts.WebPart AddWebPart( System.Web.UI.WebControls.WebParts.WebPart oWebPart, string strTitle, string strSiteUrl, string strWebName, string strDocLibName, string strPage, string strZone, int numOrder, System.Web.UI.WebControls.WebParts.PersonalizationScope pScope) { try { // Get handles to site, web, and page to which // web part will be added. SPSite site = new SPSite(strSiteUrl); SPWeb web = site.OpenWeb(strWebName); // Enable update of page web.AllowUnsafeUpdates = true; SPLimitedWebPartManager webParts; if ((strDocLibName != "")) { webParts = web.GetLimitedWebPartManager( strDocLibName + "/" + strPage, pScope); } else { webParts = web.GetLimitedWebPartManager(strPage, pScope); } // If web-part page is in a document library, // disable checkout requirement // for duration of update SPList list = null; bool origForceCheckoutValue = false; if ((strDocLibName != "")) { list = web.Lists[strDocLibName]; origForceCheckoutValue = list.ForceCheckout; list.ForceCheckout = false; list.Update(); } // Add the web part oWebPart.Title = strTitle; webParts.AddWebPart(oWebPart, strZone, numOrder); // Save changes back to the SharePoint database webParts.SaveChanges(oWebPart); web.Update();
// If necessary, restore ForceCheckout setting if ((strDocLibName != "")) { list.ForceCheckout = origForceCheckoutValue; list.Update(); } web.Dispose(); site.Dispose(); return oWebPart; } catch (Exception ex) { throw new Exception(("AddWebPart() error: " + ex.Message)); } } protected void Finish_Click(object sender, EventArgs e) { // Step 1: Get handle to web site being created SPWeb web = SPControl.GetContextWeb(Context); web.AllowUnsafeUpdates = true; // Step 2: If requested, clear out any existing // web parts if (cbRemoveExisting.Checked) { SPLimitedWebPartManager webparts = web.GetLimitedWebPartManager("default.aspx", _ PersonalizationScope.Shared); for (int i = webparts.WebParts.Count - 1; i > −1; i--) { webparts.DeleteWebPart(webparts.WebParts[i]); } } // Step 3: If requested, add an instance of a SharePoint // "ContentEditorWebPart", which is a descendent of // the generic .NET 2.0 WebPart class if (cbCEWP.Checked) { Microsoft.SharePoint.WebPartPages.ContentEditorWebPart oCEwp = new Microsoft.SharePoint.WebPartPages.ContentEditorWebPart(); oCEwp = AddContentEditorWebPart( "Hello World!", "Hello World web part", web.Site.Url.ToString(), web.ServerRelativeUrl.Substring(1), "", "Default.aspx", "Right",
0, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared); } // Step 4: If requested, add a PageViewer web part if (cbPVWP.Checked) { Microsoft.SharePoint.WebPartPages.PageViewerWebPart oPVwp = new Microsoft.SharePoint.WebPartPages.PageViewerWebPart(); oPVwp.SourceType = PathPattern.URL; oPVwp.ContentLink = txtPVUrl.Text; oPVwp.Height = "1000px"; oPVwp = (Microsoft.SharePoint.WebPartPages.PageViewerWebPart) AddWebPart( oPVwp, "And here's my page viewer web part!", web.Site.Url.ToString(), web.ServerRelativeUrl.Substring(1), "", "Default.aspx", "Left", 0, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared); } // Step 5: Now take the user to the home page of the new // web site Response.Redirect(web.ServerRelativeUrl); } // Determine whether Url text box should // be enabled based on whether // option to add a Page Viewer web part // is checked protected void cbPVWP_CheckedChanged(object sender, EventArgs e) { txtPVUrl.Enabled = cbPVWP.Checked; } }
To test our new recipe, select the Site Actions
Enter a title and URL for the new site.
Click the Chapter 6 tab in the template section and highlight Execute URL Sample.
Click the Create button. After the new site has been created and the site-definition template has been applied, your SelectWebParts.aspx
page will be displayed.
Select all checkboxes and enter http://www.apress.com/book/view/1430209615
in the Page Viewer URL field. The form should appear as shown in Figure 6-22.
Click the Finish button. The resulting home page of the new SharePoint web site should appear as shown in Figure 6-23.
There are so many possible variations when it comes to adding custom postprovisioning code. Here are a few possibilities:
Log the creation of the new site, along with the requestor, to a SQL database for reporting.
Send an email to the SharePoint administrator notifying them that a new site was created.
Provide multiple groups of web parts that will be added together depending on the purpose of the site.
Give the requestor the option of viewing a tutorial on the purpose and use of the selected template.
3.21.159.86