Chapter 6. Working with Templates and Other XML Files

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.

Adding a PDF Image to Docicon.xml

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.

Recipe Type: CAML

Ingredients

  • Adobe Acrobat PDF file icon

  • Docicon.xml

Special Considerations

  • 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.

Preparation

  • Download a copy of the Adobe PDF file icon from www.adobe.com/misc/linking.html#pdficon.

Process Flow

Process Flow
  1. 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.

    Note

    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.

  2. Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateXMLDocicon.xml and add an entry for PDF files within the <ByExtension> element.

  3. Open a command window and execute the IISRESET command to force SharePoint to reload all document image icons.

Warning

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.

Recipe—PDF Entry in Docicon.xml

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>

To Run

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.

PDF icon associated with uploaded Adobe Acrobat file

Figure 6.1. PDF icon associated with uploaded Adobe Acrobat file

Adding Custom Menus by Using a Feature

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.

Recipe Type: Feature + ASP.NET Web Application

Ingredients

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:

Assembly References

  • Windows SharePoint Services .NET assembly

Class Library References

  • Microsoft.SharePoint class library

Classes Used

  • SPSite class

  • SPWeb class

  • SPList class

  • SPListItem

Special Considerations

  • 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.

Preparation

  1. Create a new folder under C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateFeatures named DocumentComments. This folder will hold our feature definition XML.

  2. Create a new ASP.NET web application called AddComment to provide a web form through which users will add their comments.

  3. Add a reference to the Windows SharePoint Services library to the project.

  4. 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.

  5. 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.

Process Flow

Process Flow
  1. 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.

  2. 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.

  3. The extracted parameters are written to the web-part page.

  4. 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.

  5. 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.

  6. 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).

  7. 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.

Note

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.

Recipe—Feature

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

Recipe—Feature

Warning

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.

Recipe—Feature Element

Each feature references one or more ElementManifests. In our example, we have just one, DocumentComments.xml.

Note

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>/ 
Recipe—Feature Element
Comments.aspx?itemId={ItemId}&amp;itemUrl={ItemUrl}
Recipe—Feature Element
&amp;siteUrl={SiteUrl}&amp;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>

Note

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.

Note

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.

Recipe—Installer Script

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.

Note

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 
Recipe—Installer Script
-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 
Recipe—Installer Script
-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

Recipe—Installer Script

Recipe—VB: Default.aspx (See Project AddComment-VB, Class Default.aspx.vb)

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

Recipe—C#: Default.aspx (See Project AddComment, Class Default.aspx.cs)

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("?", "") 
Recipe—C#: Default.aspx (See Project AddComment, Class Default.aspx.cs)
.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>"
        );
    }
}

Web.config Changes

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.

IIS Configuration

  1. 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

    IIS Configuration
    Adding a new virtual directory under _layouts

    Figure 6.2. Adding a new virtual directory under _layouts

  2. Click Next, and name the new virtual directory AddComment ( Figure 6-3).

  3. Enter the path to the physical application and click Next ( Figure 6-4).

    Naming the virtual directory

    Figure 6.3. Naming the virtual directory

    Specifying the path to the physical application

    Figure 6.4. Specifying the path to the physical application

  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.

Setting the directory options

Figure 6.5. Setting the directory options

To Run

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:

Note

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.

  1. Navigate to the http://<yourserver>/<yoursite> (replace <yourserver> and <yoursite> with values you specified in the preceding feature definition), and select Site Actions

    To Run
    Creating a new web-part page

    Figure 6.6. Creating a new web-part page

  2. Click the Web Part Page option ( Figure 6-7).

    Selecting the Web Part Page option

    Figure 6.7. Selecting the Web Part Page option

  3. Create a new page name, Comments.aspx, placing it in the document library named Pages ( Figure 6-8).

    Creating the Comments.aspx page

    Figure 6.8. Creating the Comments.aspx page

  4. 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.

    Configuring the Page Viewer web part to display the Comments page

    Figure 6.9. Configuring the Page Viewer web part to display the Comments page

  5. Exit edit mode. Your page should look similar to Figure 6-10.

    The completed comment-editing page

    Figure 6.10. The completed comment-editing page

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.

Structure of the DocComments list

Figure 6.11. Structure of the DocComments list

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.

Chosing the Add Comment option for a selected document

Figure 6.12. Chosing the Add Comment option for a selected document

The AddComment application page

Figure 6.13. The AddComment application page

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 the View Comments site action

Figure 6.14. Selecting the View Comments site action

Selecting this option will redirect the browser to the default view of the DocComments list on the Docs web site ( Figure 6-15).

Warning

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.

The DocComments list

Figure 6.15. The DocComments list

Adding Web Parts Through Onet.xml

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.

Recipe Type: CAML

Ingredients

  • 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.

Special Considerations

  • 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.

    Warning

    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.

Preparation

  1. 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.

  2. 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.

  3. 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.

  4. Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-Mgerow2XMLOnet.xml in Visual Studio, Notepad, or whatever text editor you prefer.

  5. 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">
  6. Within the <File> element, add the following two child elements:

    <AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1">
    <![CDATA[
    ]]>
    </AllUsersWebPart>
    <AllUsersWebPart WebPartZoneID="Right" WebPartOrder="1">
    <![CDATA[
    ]]>
    </AllUsersWebPart>
  7. 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").

  8. 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").

  9. 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.

  10. Open the new Webtemp file you created in the preceding step, and delete all the <Template> elements within the <Templates> parent element.

  11. 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.  
    Preparation
    The only difference is the addition of web-part definitions
    Preparation
    directly into ONET.XML by using the AllUsersWebPart element." DisplayCategory="Chapter 6" > </Configuration> </Template>

    Warning

    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.

  12. 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.

Webtemp <Configuration> attributes as displayed in the New SharePoint Site page

Figure 6.16. Webtemp <Configuration> attributes as displayed in the New SharePoint Site page

Recipe—Page Viewer Web Part

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" 
Recipe—Page Viewer Web Part
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
Recipe—Page Viewer Web Part
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.
Recipe—Page Viewer Web Part
The linked content is isolated from other content on the
Recipe—Page Viewer Web Part
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,  
Recipe—Page Viewer Web Part
PublicKeyToken=71e9bce111e9429c</Assembly> <TypeName>Microsoft.SharePoint.WebPartPages.PageViewerWebPart</TypeName> <ContentLink xmlns="http://schemas.microsoft.com/WebPart/v2/PageViewer">
Recipe—Page Viewer Web Part
http://www.apress.com/book/view/1430209615</ContentLink> <SourceType xmlns="http://schemas.microsoft.com/WebPart/v2/PageViewer">URL </SourceType> </WebPart>

Recipe—Content Editor Web Part

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" 
Recipe—Content Editor Web Part
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
Recipe—Content Editor Web Part
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, 
Recipe—Content Editor Web Part
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>

Warning

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.

To Run

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:

  1. Select the Site Actions

    To Run
  2. Enter a title and URL for the new site.

  3. Click the Chapter 6 tab in the template section and highlight the Adding Web Parts Directly to Onet.xml template.

  4. Click the Create button. After the new site has been created, you should see a page similar to that shown in Figure 6-17.

New site based on the modified Onet.xml

Figure 6.17. New site based on the modified Onet.xml

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.

Variations

  • 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.

Adding an ExecuteUrl Directive to Onet.xml

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

Recipe Type: CAML + ASP.NET

Ingredients

Assembly References

  • Windows SharePoint Services .NET assembly

Class Library References

  • Microsoft.SharePoint class library

  • Microsoft.SharePoint.WebControls class library

  • Microsoft.SharePoint.WebParts class library

Other Ingredients

  • 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)

Note

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.

Special Considerations

  • 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.

    Warning

    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.

Preparation

  1. 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.

  2. Open the file C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12TemplateSiteTemplatessts-MGEROWxmlOnet.xml in Visual Studio, Notepad, or whatever text editor you prefer.

  3. 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.

  4. Save your changes to Onet.xml.

  5. 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.

  6. 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>
  7. 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.

    Webtemp <Configuration> attributes as displayed in the New SharePoint Site page

    Figure 6.18. Webtemp <Configuration> attributes as displayed in the New SharePoint Site page

    Warning

    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.

  8. Create a new ASP.NET application on your SharePoint. I named my C# application SelectWebParts-CS, but you may name it as you wish.

  9. Rename Default.aspx to SelectWebParts.aspx in your new ASP.NET application.

  10. Add a reference to the Windows SharePoint Services .NET assembly.

  11. Add a using (C#) or Imports (VB.NET) reference to the Microsoft.SharePoint class library.

  12. 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).

    Note

    If you are not using the default SharePoint—80 web application, please replace references to that web application as appropriate.

    The IIS Manager application showing the SelectWebParts virtual directory

    Figure 6.19. The IIS Manager application showing the SelectWebParts virtual directory

  13. Right-click on the new virtual directory and select the Properties context menu item to open its properties dialog box.

  14. 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.

  15. 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).

Note

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.

Configuring the SelectWebParts virtual directory properties

Figure 6.20. Configuring the SelectWebParts virtual directory properties

We're now ready to code our SelectWebParts application.

Process Flow: Finish_Click()

Process Flow: Finish_Click()
  1. Use the SPControl.GetContextWeb() method to get a handle to the new web site just provisioned.

  2. 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.

    Note

    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.

  3. 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.

  4. 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.

  5. When all postprovisioning modifications are complete, redirect the user's browser to the home page (typically Default.aspx) of the new web site.

Page Layout: SelectWebParts.aspx

Figure 6-21 shows the page layout for SelectWebParts.aspx.

SelectWebParts.aspx page layout

Figure 6.21. SelectWebParts.aspx page layout

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

Recipe—VB (See Project SelectWebParts-VB, Class SelectWebParts.aspx.vb)

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

Recipe—C# (See Project SelectWebParts-CS, Class SelectWebParts.aspx.cs)

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 Run

  1. To test our new recipe, select the Site Actions

    To Run
  2. Enter a title and URL for the new site.

  3. Click the Chapter 6 tab in the template section and highlight Execute URL Sample.

  4. 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.

  5. 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.

    Completed SelectWebParts.aspx form

    Figure 6.22. Completed SelectWebParts.aspx form

  6. Click the Finish button. The resulting home page of the new SharePoint web site should appear as shown in Figure 6-23.

Home page of provisioned SharePoint web site

Figure 6.23. Home page of provisioned SharePoint web site

Variations

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.159.86