Merging SharePoint List Data into Word Documents

In this article we will see how to merge SharePoint list data into a Word 2007 document. The Office Open XML format supported by Microsoft Word 2007 lets us leverage the document templates and can allow us to modify the document programmatically.

Many organizations always work with their predefined set of document templates throughout their enterprise. It is often a challenge to make sure that information workers are uniformly using the latest template and capturing the appropriate metadata everywhere the document templates are used. It is not unusual for some templates to share common data elements such as custom information or product details.  Always the authors, who are responsible for working with these templates, are retyping, cutting and pasting, or otherwise repetitively importing data elements into the appropriate places in the document. Also, they have a totally separate application to locate and maintain the data.

Also, users have been looking for different and easy ways to automate data merges since the beginning of word processing. It is, in essence, a different take on a mail merge. In this case the data source will be a SharePoint list. Now, many and more companies’ workers are relying on SharePoint lists where they previously may have kept local spreadsheets or Access databases. The fact is that SharePoint lists are very simple to use and very easy to manage and customize without help of developer. Also this is enhanced once you add the benefits of the list being web-based and supporting other users’ ability to edit individual items. This situation is so generic that it could apply to almost any organization in any industry. If the list where custom contact information, the documents templates could be contracts, statements of work and letters. If the list were product information, the document templates would be order forms.
So, in this article we will see and discuss that how to develop a solution on SharePoint site to enable users to merge list data into Microsoft Word documents. We will discuss this as a real world case study.

Case Study – Solution or application to merge SharePoint List data into Microsoft Word 2007 documents.

We as an enterprise organization have predefined document templates which are used to communicate with customers for Customer Contracts. We are using SharePoint site to maintain our customer related information. We want to have a facility in the SharePoint site that our employee can work easily and make a customer document (Microsoft Word 2007) for a particular customer contract. When the employee select to generate a document, he will be given various options like which specific type of customer document he would like to be constructed with the contact’s data. We use Business Fax template, a statement-of-work template and a template to thank new customers. Once the employee select a document type, the contact’s data will be merged with document and the resulting file will be sent to the user.

Solution Overview

For this case study, we will develop a feature that, when enabled in a site, provided the functionality to merge list-item data into specific organizational document templates. So for our solution we will focus our efforts on building a solution that deals with an organization’s customers. Once the feature has been enabled in a SharePoint site, the solution will create a Customer Contacts list along with a Customer Documents document library. The document library will reference the organization’s document templates, which will be configured as SharePoint content types. The Customer Contacts list will be enhanced with an additional action so that for a specific contact, the employee may select to build a customer document. After the selection, the user will be directed to a custom application page that enables the employee to select which specific type of customer document he would like to create with the contact’s data. As said in requirements, we will build a business fax template, a statement-of-work (or work-for-hire) template and a template to thank new customers. Once the employee completes with his selection of document type, the application page will merge the contact’s data with the document using the Open XML file format and send the resulting file to the user.

Also, our developed solution will be flexible enough to allow any site administrator to simply turn on this functionality for the site. For this reason, we will develop the solution as a SharePoint feature. Features are a way of packaging a set of customizations as a single unit that can be activated or deactivated. Though features take a bit of effort to set up, they not only increase the reusability of the solution, but also make deployment complexities easy. For more information on feature please read my article “Feature – the most used customization component in WSS and MOSS 2007”.

Case study development Walkthrough

In this section we will see the major elements of the case study solution development. As a walkthrough, we will see how to create content types for the organization’s document templates. We will go into detail about how to package the solution as a feature that can be enabled by any site administrator that desires the functionality. In walkthrough we will also see how to setup a feature project in Visual Studio, the XML files used to define the customizations of the feature and how to deploy it. We also see the construction of a custom SharePoint application page. Finally, the contact data and the document come together through the use of custom XML parts and Microsoft Word 2007 content controls.

Creating Content Types

In this section of the walkthrough, an organization has a set of document templates that are often used to communicate with customers. These document templates and their required metadata should be used uniformly throughout the enterprise. We want to define the settings for each document type once and have any site or even any library in the site collection is able to reference them. For this, we will see how to define each as a SharePoint content type.

SharePoint content types are new to Windows SharePoint Services 3.0; they allow organizations to reuse settings that apply to content of a specific category. Once the content type is defined for the site collection, any library can be configured to support the content type. Enabling a content type for a library results in an additional selection being added to the library’s “New” menu.

One interesting thing is that, the metadata columns associated with the library and the content types do not have to match. This allows for the users to upload different file of different content types with different metadata to the same document library. This flexibility breaks down barriers that required administrators to create multiple libraries like in WSS v2. It allows you to store different types of documents that should be grouped based on their user or security requirements. It also provides a single point of administration as changes to the content type need to be made in only one place. Content types also support inheritance, where a derived type can reuse and extend its parent’s settings.

To see the content type of a particular SharePoint site collection, navigate to the Site Settings administration area from the top site in the site collection. There you’ll find the Site Content Types gallery.

By looking through details of some of the default content types, you can see that they include settings for columns, workflow, information panels and information management policies. Each content type also has an associated template that can be set from its Advanced Settings page. The documents used in our example will be a business fax, a statement of work and a thank-you letter for new customers. Though these document templates are different, they are all related by a common/overlapping core of metadata requirements for the organization. So in creating them, we will first create a content type called CustomerDocument that will contains all of the common settings and serve as a parent for the other documents. Perform the below steps to create the CustomerDocument content type:

1. Click the Create toolbar button on the Site Content Type Gallery page.
2. Enter CustomerDocument for the name of content type. Also add some related description if you need.
3. As all of our types are documents, select Document Content Types for “Select parent content type from:” and then Document for “Parent Content Type:” to identify the parent content type.
4. Now to differentiate our customer documents, in the Group section click “New group” radio button and type “Customer Document Metadata Model”.
5. Click Ok to create new content type for our solution.



If you have noticed, this new content type automatically inherits the Name and Title columns due to its child relationship with the Document content type. These both columns are SharePoint site columns. Like content types, site columns provide an element of reuse as they uniformly define a column for a site collection. For this reason, content types must use site columns. Perform below steps to extend the CustomerDocument content type to include a Company metadata column.

1. Since Company is a site column that already exists, select “Add from Existing Site columns”.
2. Select “Core Contact and Calendar Columns” from “Select columns from” dropdown list.
3. This will populate Available columns.
4. From the Available columns, select “Company” and click Add button.



5. Click OK button.
6. Click on the Company column in the list and make it a required field.
7. Click OK button.

Now repeat the content-type-creating steps (the first numbered list) for the BusinessFax, WorkForHire, and ThankYouNewCustomer types, except select the CustomerDocument content type as their parent. Note that each gets the Company site column in its settings. Also add some different columns for each so you can see how the library changes to accommodate the various schemas. Also, upload a Word 2007 document to server as a template from the content type’s Advanced Settings page. We will modify the templates later, but for now use the Business Fax Cover Sheet, Standard Work for Hire Contract and Thank You to New Customer templates from Microsoft Office Online (available in the left-hand navigation of the New Document dialog in Microsoft Word 2007). Make sure that when you save them, select the new Office 2007 file format and a .docx file extension.

Now to test our created new content types we will associate them with a document library. This library must be in the same site collection, but not necessarily the same site. Pick any test document library and follow these steps to enable the content types.

1. From the library’s Settings menu, select Document Library Settings.
2. Select Advanced Settings from the General Settings group.
3. Use the radio buttons to enable the management of content types.
4. Click OK.
5. The library’s Settings page should have a new section called Content Types. Use the Add from Existing Content Types link to associate our content types: Business Fax, WorkForHire and Thank You New Customer. See the below image.



Now create at least one instance of each document type and save them to the document library. Do not enter any new content into the files, but complete the metadata entry form. Later we will use these documents to explore their XML structures, modify them to hold custom data, and upload them as new templates for the content types.

Creating the CustomerDocuments Feature Project

Our solution is going to provide the organization with a set of elements that together enable any site in the site collection to maintain a list of customer contacts, a customer documents library and the ability to merge contact data into specific document templates. The important fact here is that we want to provide multiple instances of this capability throughout the site collection. Some sites may want to use it while others may not. So, we will package our solution as a SharePoint feature.

A SharePoint feature is a deployable unit that packages site customizations and custom code into a single component that an administrator can activate or deactivate. Features have a defined scope that dictates the breadth of their influence. Read more about features at Feature – the most used customization component in WSS and MOSS 2007. Below image shows the Site Features administration page from the Site Settings menu of a site.



To see the impact of a feature, explore what happens when you deactivate the Slide Library feature. You will receive a warning; deactivate it. Turning off this feature removes your ability to create instances of the slide library template in your site. Even, this option will not be seen in any creation dialog. If you reactivate the feature, the options return. In case you are thinking, deactivating the Slide Library feature does not delete any slide libraries that have already been created. But that was a choice by the developers who created this feature. You will have to decide what you want to happen when your feature is activated and deactivated on a site.

To create the Feature, we will use a Visual Studio C# class library project. Name the project and solution CustomDocumentsFeature. Delete the default class file and add one named CustomDocumentsReceiver.cs. This class can be used as receiver containing custom code that runs in response to the feature being activated or deactivated. Be sure to add a reference to the Windows SharePoint Services assembly. This assembly should be listed in Visual Studio’s Add Reference dialog; if it’s not, you can find it in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI\Microsoft.SharePoint.dll. The project needs to be signed since the receiver will be deployed to the Global Assembly Cache. Follow these steps to sign the assembly:

1. Right-click on the project in Solution Explorer and select Properties.
2. Select Signing in the left-hand navigation bar.
3. Click the check box to sign the assembly.
4. Select New.
5. For the name of the file, type in key.
6. Deselect the check box for protecting the file with a password.
7. Click OK.
8. Click Visual Studio’s Save toolbar button to commit this setting.

We will also add some files to this project except the feature receiver. These additional files will need to be deployed to the server’s TEMPLATE directory at C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE by default. To make deployment easier we will mirror the directory structure of the server so that our new files are placed in the appropriate location. Below figure illustrates the structure, beginning from the TEMPLATE directory and including directories named Features, CustomerDocuments and Layouts. The files in these directories are ones that we will we proceed.



Defining the Feature

The CustomerDocumentsFeature folder defines the feature. It contains three XML files, including feature.xml. That file is the primary manifest file containing information about the feature, references to element files that detail the customizations the feature contains, and activation dependencies for other features that this one relies on. Below listing contains the feature information section of this file.

<?xml version="1.0" encoding="utf-8" ?>
<Feature Id="354ACC08-5FC2-4c57-9A19-B57DC463148D"
         xmlns="http://schemas.microsoft.com/sharepoint/"
         Title="Customer Documents Feature"
         Description="This feature inserts customer contact data into provided document templates."
         Version="1.0.0.0"
         Scope="Web"
         ReceiverAssembly="CustomerDocumentsFeature, Version=1.0.0.0, Cultuer=neutral, PublicKeyToken=62148cf357e70681"
         ReceiverClass="CustomerDocumentsFeature.CustomerDocumentsReceiver"
         Hidden="FALSe">


In this section we will be creating several XML files like this one. This definition of the feature includes a new GUID as its Id. You can use the Create Guid option from the Tools menu of Visual Studio to create a unique GUID. The ReceiverAssembly attribute is the strong name of the assembly. The public key token used here depends on the key you used to sign the assembly. To retrieve this you must first compile your application to create the DLL and then use Visual Studio’s command-line SN.exe tool.

The next portion of feature.xml defines the element manifests. An element manifest contains the settings of the customizations that the feature will contain. You can have any number of manifests, but it makes sense to group them based on the type of customization. For our solution, we will include one manifest for customizations that act on lists and one manifest for custom actions that our feature places in the site. Below listing shows this section of XML document, including references to an element manifest of custom actions and an element manifest of list customizations.

<ElementManifests>
    <ElementManifest Location="elements.xml" />
    <ElementManifest Location="listElements.xml" />
  </ElementManifests>


The last section of feature.xml file is the feature’s activation dependencies. An activation dependency is a feature’s way of enforcing that other features that include functionality it relies on are also activated. A feature’s activation dependencies can’t reference a feature of a more restrictive scope. Since the CustomerDocuments feature is going to create a contacts list and a document library, it makes sense for each of these to be listed as a dependency. You can find these dependencies’ feature IDs by browsing the Template\Features directory on the server. Both the DocumentLibrary and ContactsList features have their own folders and a feature.xml file that contains their GUIDs. Since these features are part of the core product, they will be the same on your system. Below listing shows how to construct the activation dependencies.

<ActivationDependencies>
    <ActivationDependency FeatureId="00BFEA71-7E6D-4186-9BA8-C047AC750105"/>
    <!-- ContactsList Feature -->
    <ActivationDependency FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101"/>
    <!-- DocumentLibrary Feature -->
  </ActivationDependencies>
</Feature>


The listElements.xml file contains information about the customizations related to creating and configuring the necessary lists and libraries for the solution. In this case, the feature will create a contacts list named Customer Contacts and a document library named Customer Documents. Below listing details the portion of this file responsible for creating the Customer Contacts list.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ListInstance
   Id="30001"
   FeatureId="00BFEA71-7E6D-4186-9BA8-C047AC750105"
   Description="Customer Contacts"
   TemplateType="105"
   Title="Customer Contacts"
   OnQuickLaunch="TRUE"
   QuickLaunchUrl="Customer Contacts/AllItems.aspx"
   Url="Customer Contacts">
    <Data/>
  </ListInstance>

The ListInstance element instructs the site to create a Customer Contacts list upon activation. The Id attribute is a unique integer within this feature definition. The FeatureId attribute used here is the GUID of the feature containing the template for the list. Since Customer Contacts is to be a normal instance of the out-of-the-box contacts list, the identifier of the ContactsList feature is used in our ListInstance declaration. Also note the attributes related to the Quick Launch bar, which make sure this new list is included in the navigation of the site. The empty Data element of the instance is the area that could be used to preload data into the list; however, we will not use it here.
Below lising shows the details about other ListInstance element responsible for creating the Customer Documents document library. In this case, the FeatureId is the GUID of the DocumentLibrary feature.

<ListInstance
   Id="30002"
   FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101"
   Description="Customer Documents"
   TemplateType="101"
   Title="Customer Documents"
   OnQuickLaunch="TRUE"
   QuickLaunchUrl="Customer Documents/Forms/AllItems.aspx"
   Url="Customer Documents">
    <Data/>
  </ListInstance>

The last portion of this file contains three ContentTypeBinding elements that are responsible for linking the content types we created earlier with the Customer Documents document library. Now the ContentTypeIds will be specific to your environment since you created them in the earlier steps. In the next section, we will show you how to find the IDs that the system generated for each of your content types.

<ContentTypeBinding
    ContentTypeId="0x0101006BAE020A103E7849A65D9169F921F97401"
    ListUrl="Customer Documents"
  />
  <ContentTypeBinding
    ContentTypeId="0x0101006BAE020A103E7849A65D9169F921F97402"
    ListUrl="Customer Documents"
  />
  <ContentTypeBinding
    ContentTypeId="0x0101006BAE020A103E7849A65D9169F921F97403"
    ListUrl="Customer Documents"
  />
</Elements>

Each of these bindings maps to one of the CustomerDocument content types we created earlier: BusinessFax, WorkForHire and ThankYouNewCustomer. Looking at the ContentTypeIds in above listing, it is easily to tell that they all share a commong parent. The telling sign is that the ID is exactly the same up to the last digit. In SharePoint, when a content type is derived from another type, it simply extends the ID by a few characters. In addition, the fact that each of these types begins with 0x0101 tells us that an ancestor of this type is the base Document content type. As showin in below image, you can obtain the content type IDs in your environment by copying the ctype query string parameter on the Site Content Type administration page. Use this technique to retrieve each of your ContentTypeIds and update the code in above listing to match your environment.



The next element manifest file you need to create is elements.xml. This file contains information about the customizations related to adding links to menus for the solution. In this case, the feature will create a new link in the menu that displays when hovering over any contact item in the site. This menu is called the Edit Control Block (ECB) menu. The custom action will be a link that directs the user to a custom application page that we will develop in the section “Building a Custom Application Page” later in this article. This page will allow the user to select which document template to merge with the chosen contact. Below lising shows the XML needed to add this custom action.

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!-- Per Item Dropdown (ECB)-->
  <CustomAction Id="ECBItemToolbar"
RegistrationType="List"
RegistrationId="105"
ImageUrl="/_layouts/images/ICDOC.GIF"
Location="EditControlBlock"
Sequence="225"
Title="Build Customer Document">
    <UrlAction Url="~site/_layouts/BuildCustomerDoc.aspx?ItemId={ItemId}&ListId={ListId}"/>
  </CustomAction>
</Elements>

The most important attributes of the CustomAction element are RegistrationType, RegistrationId, and Location. The RegistrationType attribute details an attachment type. In this case, the action is associated with a list. Other possible attachment types include ContentType, FileType or ProgId. The RegistrationId attribute further clarifies the attachment by specifying the identifier of the list, item or content type that the attachment is associated with. In this case, the value of 105 represents the list template ID matching this action with contact lists. The Location attribute dictates where this action should be displayed for the list. In this case, we want the action to display when the user hovers over any specific content item, so we want it in the Edit Control Block (ECB).

When the user clicks on our action, we want to direct them to the custom application page (BuildCustomerDoc.aspx). Notice that how the URL specified in above listing includes tokens that represent the current site (~site), the item selected ({ItemId}) and the list that contains it ({ListId}). Passing these values will allow the custom page to retrieve the selected contact item so it can merge it with a document template. As a developer, you can use custom actions within features to add links to just about every where within SharePoint.

Deploying the Feature

At this point, there is enough functionality in this feature that it makes sense to deploy it to SharePoint to test that it successfully activates and deactivates. Deploying the feature involves first compiling the solution. Once it has been compiled, we will install the assembly that contains the feature receiver into the Global Assembly Cache (GAC), copy the project’s XML files to their correct location in the server’s TEMPLATE directory, and use STSADM.exe to install and activate the feature. To orchestrate these steps, we will reply on Visual Studio’s support of post-build events and a custom installation batch file. Use the following steps to access the Build Events dialog for your project.

1. Right-click on your project name and select Properties.
2. Select Compile in the left-hand navigation bar.
3. Click the Build Events button.
Below image shows the Build Events dialog, where we will enter commands to add the assembly to the GAC and start the batch file.



Three command make up the post-build command script. The syntax of these commands is details in the below listing. The first install the built assembly into the GAC. The $(TargetPath) wildcard used here will be replated with the path and name of the constructed assembly. The second command changes the current directory to the root level of the project using the $(ProjectDir) wildcard. The third command executes the install batch file.

“%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe” –if $(TargetPath)
Cd $(ProjectDir)
Install.bat

Add a batch file named install.bat to the root of your project directory. This installation batch file will do all the necessary steps to properly deploy the feature. The batch file will first attempt to deactivate and uninstall the feature in the development site. This is to enable multiple iterations of a developer building, deploying and testing the same feature in the same site. The batch file then copies the contents of the project’s Template directory to the server’s TEMPLATE directory. This is why we took so much care earlier to organize the project’s directory structure to mirror that of the server. With the files deployed, the only remaining steps are to install the feature and activate it on our development site. Notice the batch file does include the IISRESET since the server could cache the definition of the feature in memory. Below listing includes the contents of the installation batch file. Be sure to customize the URLs here for the development site you are working with.

@SET TEMPLATEDIR="c:\program files\common files\microsoft shared\web server extensions\12\Template"
@SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm"

Echo Deactivating feature
%STSADM% -o deactivatefeature -filename CustomerDocuments\feature.xml -url http://localhost:20918

Echo Uninstalling feature
%STSADM% -o uninstallfeature -filename CustomerDocuments\feature.xml

Echo Copying files
rd /s /q %TEMPLATEDIR%\Features\CustomerDocuments
xcopy /e /y TEMPLATE\* %TEMPLATEDIR%

Echo Installing feature
%STSADM% -o installfeature -filename  CustomerDocuments\feature.xml -force

IISRESET

Echo Activating features
%STSADM% -o activatefeature -filename CustomerDocuments\feature.xml -url http://localhost:20918 –force

Building a Custom Application Page

When a user selects the Build Customer Document custom action from a contact item in the site, the solution will redirect the user to an application page that will show the document templates that are available from the Customer Documents document library. After the user makes a selection, the page will serialize the contact list item and place it into the Word 2007 document template as a custom XML part. The next section of this article goes into detail about Microsoft Word 2007’s support for custom XML parts. We will focus on creating the page, laying out its controls and initial processing. Below image depicts the end goal of BuildCustomerDoc.aspx page.



The Build Customer Documents page is termed an application page because it is not part of a site definition, but rather lives in SharePoint’s Layouts directory. This means that the ASPX page can be accessed off of the path of any site through the _layouts virtual folder. Application pages are ASP.NET pages that derive from a SharePoint class LayoutsPageBase and rely on the application.master master page for their layout, look and feel. These pages do not use the code-behind model and instead include their code within script blocks of the ASPX page. Below listing details the initial page directives, including referenced assemblies, imported namespaces and page directives.

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<%@ Assembly Name="WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"%>
<%@ Page Language="VB" MasterPageFile="~/_layouts/application.master" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" EnableViewState="true" EnableViewStateMac="false"    %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Import Namespace="Microsoft.SharePoint.Administration" %>
<%@ Import Namespace="System.IO.Packaging" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

As seen in the above listing, the Assembly directive is responsible for linking an assembly to the page. Notice that there is an Assembly directive for the WindowsBase assembly. This assembly contains the System.IO.Packaging namespace, which contains classes for manipulating files based on the Office Open XML file format. In addition, you will need to add a reference to your project for the WindowsBase assembly. The Import directives associate particular namespaces so that referring to classes or controls within them does not requir a fully qualified name. The Register directives identify tag prefixes that we can use to refer to server controls within the page’s HTML. Finally, the Page directive details the page attributes, including the language we will use for coding and the fact that we want this page to maintain ViewState through post-backs. By maintaining ViewState, the controls on the page will maintain their values through round-trips to the server that result from the user clicking one of the buttons.

The application master page contains three placeholders where the page can plac content. The most important is PlaceHolderMain, which holds the server controls and layout for the body of the page. On the Build Customer Documents page, the main content area is an HTML table containing a drop-down list and two button controls. The other two placeholders just contain titles for the application page. Below listing details the content of the Build Customer Documents application page’s PlaceHolderMain placeholder. For full working code please download the sample code.

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <table cellpadding="0" cellspacing="0" style="width: 100%; font-size: 9pt">
        <tr>
            <td class="ms-formlabel">                
            </td>
            <td class="ms-formbody">
                <asp:Label ID="lblError" runat="server" ForeColor="Red"></asp:Label>
            </td>
        </tr>
        <tr>
            <td class="ms-formlabel">
                Document Type:
            </td>
            <td class="ms-formbody">
                <asp:DropDownList ID="lstContentTypes" runat="server" EnableViewState="true" />
            </td>
        </tr>
        <tr>
            <td class="ms-formlabel">
            </td>
            <td class="ms-formbody">
                <asp:Button ID="btnCancel" Text="Return" runat="server" /> <asp:Button
                    ID="btnOK" Text="Generate" runat="server" OnClick="btnOK_Click" />
            </td>
        </tr>
    </table>
</asp:Content>

This application page will be displayed in response to a user clicking on the Build Customer Document action from any site that has the feature activated. For this reason, the page must first establish its context and get references to the site collectio and the site from which it is being invoked. With these pieces of information, the application page can use the query string parameters that were passed by the custom action to identify the contacts list the user was in when he selected the action, as well as the individual item he selected. Please download the sample code for refernce.

Using a Custom XML Part in a Document Template

The custom applicatio page will merge the selected contact item with the selected document template. To facilitate this operation, we will leverage the fact that the document templates are Microsoft Word 2007 files saved using the new Open XML file format. So, a Word 2007 document is really a package of parts and items. Parts are pieces of content for the file, whereas items are metadata describing how the parts should be assembled and rendered. Most of these pieces are XML files, making it possible for them to be manipulated through code. You can gain insight into the strucure of an Open XML-based file by replacing its file extension with .zip since the file is really an ordinary Zip archive. We will use an instance of the business-fax template we created earlier. Make sure that you are modifying a BusinessFax document that has been saved to the site’s Customer Documents document library once before. This will help you understand how SharePoint uses the Open XML file formats, as well. Below image shows the root of the archive for the Business Fax document.



The XML file in the root is named [Content_Types].xml and it stores content-type directives for all the parts that appear in the archive. A content type contains metadata about a particular part or groups of parts and, more importantly, contains a directive as to how the application should render that part.

<Override PartName="/customXml/itemProps1.xml" ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml"/>
<Default Extension="wmf" ContentType="image/x-wmf"/>
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>

The above listing shows few lines from the XML file. It clearly shows that how this file tells the rendering applicatio which parts are images, style, relationships, custom properties and even the main document. Pay particular attention to the first line. The Override element is describing the itemProps1.xml file as being something more than just any XML file inside the document package. In fact, ItemProps1.xml is a file that contains the properties of a custom XML part. It describes item1.xml, which stores the actual custom XML data. All of the Office document types support having a custom XML folder that is capable of storing information. In fact, SharePoint itself creates three custom XML parts in Office 2007 files when they are uploaded into site libraries. These XML parts are for storing metadata values, the schema of the document information panel and workflow information. Though all of the Office 2007 files can store custom XML, Microsoft Word 2007 offers the unique added benefit of having a set of content controls that can bind to the custom XML parts, displaying the data inline with the document. So, open the BusinessFax document you created earlier and follow these steps to add content controls that we will bind to contact data:

1. Delete the text following the Fax Number: prompt.
2. Click on the Developer tab in the Microsoft Word ribbon. This tab may be hidden on your machine, but it can be enabled through the Popular settings of the Word Options dialog. The Word Optins dialog is accessible by clicking on the new pearl (Office logo) button in the top-left corner of Microsoft Word 2007.
3. Place your cursor after the color (:) and click the plain-text button Aa in the Controls group.
4. Click the Properties button in the ribbon on the right-hand side of the Controls group.
5. Set the Title and Tag values to FaxNumber.
6. Click the check box to keep the content control from being deleted accidentlly.
7. Click the check box to disable editing of the control’s content.

Repeat the same steps to add a plain-text content control for the To field. Name the content control FullName. The document could have many more content controls, but these two will be enough to illustrate the solution. In thie example, we are relying on just the plain-text content control, but Microsoft Word does support others, such as picture, rich text, drop-down list and a calendar control for dates. Save the file, change it to a Zip archive, and extract its contents to a folder. With all of the package’s parts available to use as individual files, we can take the necessary steps to create a new custom XML part named item4.xml and bind these controls to its data. We will be making many of our edits directly to the XML parts.

Start by creating the custom XML part item4.xml. This file should be inside the customXml folder. Eventually the application page will replace this file with data of its own, so we need to put it only enough data to confirm the bindings are set up appropriately. Below listing contains sample XML for this part. Notice that we use our own XML namespace to differentiate the data here from the rest of the parts, and that the element names are encoded. This encoding is done deliberately since the names of the list columns will need this same treatment when the custom application page serializes the contact data.

<?xml version="1.0"?>
<sc:Contact xmlns:sc="http://www.sample.com/2006/schemas/contact/">
  <sc:ID>1</sc:ID>
  <sc:Last_x0020_Name>hild</sc:Last_x0020_Name>
  <sc:First_x0020_Name>ed</sc:First_x0020_Name>
  <sc:Full_x0020_Name>ed hild</sc:Full_x0020_Name>
  <sc:E-mail_x0020_Address/>
  <sc:Company>sample inc</sc:Company>
  <sc:Fax_x0020_Number>333-333-333</sc:Fax_x0020_Number>
  <sc:Address/>
  <sc:City/>
  <sc:State_x002F_Province/>
  <sc:ZIP_x002F_Postal_x0020_Code/>
</sc:Contact>

Each custom XML part has a paired properties file. So in the same customXML folder create an itemProps4.xml. This properties file contains a key piece of information: the dataStoreItem identifier. This identifier is a unique GUID. Be sure to keep this ID handy; we will have to reference it in the bindings as well as the merging code in the custom application page. Below listing shows the itemProps4.xml.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ds:datastoreItem ds:itemID="{C7189B18-753E-4599-964B-526E4AF29DED}"
xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml">
  <ds:schemaRefs>
    <ds:schemaRef ds:uri="http://www.sample.com/2006/schemas/contact/"/>
  </ds:schemaRefs>
</ds:datastoreItem>

The package’s customXml folder contains a subfolder named _rels. This folder contains relationship files that pair the custom XML part with its properties. Create a new file named item4.xml.rels with the following content.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1"
  
Type="http://schemas.openxmlformats.org/officeDocument/~
2006/relationships/customXmlProps"
  
Target="itemProps4.xml"/>
</Relationships>

The next step is to make sure the package understands that we have added a custom XML part. To information Microsoft Word, the part needs to be registered in the [Content_Types].xml file. Add another Override element to this file which is identical to the other itemProps#.xml files. There is one other relationship file that must be modified. The file is located in the Word folder of the package and is named document.xml.rels. This file contains information about the relationships between the package and actual document. Add another relationship to this file with a unique relationship number. The added line should be similar to the one like below.

<Relationship Id="rId11"
Type="http://schemas.openxmlformats.org/officeDocument/2006/~
relationships/customXml"
Target="../customXml/item4.xml"/>

The last set of modifications binds the content controls to specific data contained in the custom XML part. To create these bindings, we must edit the document.xml file contained in the package’s Word folder. This file contains the BusinessFax document itself. Locate the FaxNumber content control; it’s displayed in below listing along with a new dataBinding element and default text.

<w:sdt>
  <w:sdtPr>
    <w:alias w:val="FaxNumber"/>
    <w:tag w:val="FaxNumber"/>
    <w:id w:val="204026034"/>
    <w:lock w:val="sdtContentLocked"/>
    <w:placeholder>
      <w:docPart w:val="DefaultPlaceholder_22675703"/>
    </w:placeholder>
    <w:showingPlcHdr/>
    <w:dataBinding
    
w:prefixMappings="xmlns:sc='http://www.sample.com/2006~
/schemas/contact/'"
    
w:xpath="/sc:Contact/sc:Fax_x0020_Number"
    
w:storeItemID="{C7189B18-753E-4599-964B-526E4AF29DED}"/>
    <w:text/>
  </w:sdtPr>
  <w:sdtContent>
    <w:r w:rsidR="00C947A8" w:rsidRPr="001362A5">
      <w:rPr>
        <w:rStyle w:val="PlaceholderText"/>
      </w:rPr>
      <w:t>Insert fax number</w:t>
    </w:r>
  </w:sdtContent>
</w:sdt>

The dataBinding element in above lines uses the same namespace as the custom XML part and includes an attribute to the data-store identifier, which was the GUID used in item4Props.xml. The XPath query must resolve to a single node, as Microsoft Word’s content controls will not automatically repeat for node collections. Notice the string Insert fax number. This is default text that will be used if the data binding is unable to locate a value in the custom XML part. Make the same type of edit for the FullName content control.

Now that the modifications are complete, zip the package contents such that [Content_Types].xml is at the root of the archive. Rename the Zip file to have a .docx file extension and open it in Microsoft Word 2007. If the modifications were made correctly, the data placed in item4.xml should display in the document. If they do, use this document to replace the template that we uploaded for the content type. You can also use these steps to make similar edits to the other content-type templates.

Performing the Merge

The BuildCustomerDoc.aspx application page has the responsibility of serializing the contact list item and placing it in the custom XML part we created in the preceding section. All of this is done when the user clicks the Generate button on the custom application page to generate the document. The code in the button’s OnClick event handler locates the document template for the selected content type and copies it to a memory stream. This way the copy can be modified without changing the content type’s template.

Dim documentStream As Stream = New MemoryStream
            Dim contentType As SPContentType = customerDocumentLibrary.ContentTypeOrder.Item(lstContentTypes.SelectedValue)
            
            Dim templateFile As SPFile = contentType.ResourceFolder.Files(contentType.DocumentTemplate)
            Dim templateStream As Stream = templateFile.OpenBinaryStream()
            Dim reader As BinaryReader = New BinaryReader(templateStream)
            Dim writer As BinaryWriter = New BinaryWriter(documentStream)
             writer.Write(reader.ReadBytes(CInt(templateStream.Length)))
             writer.Flush()
             reader.Close()
            templateStream.Dispose()

The next step is to locate the custom XML part where the application page needs to place the content data. Unfortunately, this is not as easy as it looks. We can’t be guaranteed that the customXML part we created earlier is still named item4.xml – SharePoint may have reordered the parts when we uploaded the document to the system. Therefore, the code in the below listing opens the stream as a package and iterates through the custom XML parts, looking for the namespace and used to represent the contact data.

documentStream.Position = 0
            Dim packageFile As Package = Package.Open(documentStream, FileMode.Open, FileAccess.ReadWrite)
             'retrieve package part with XML data
            Dim partNameTemplate As String = "/customXml/item{0}.xml"
            Dim partName As String = Nothing
            Dim packagePartData As PackagePart = Nothing
            Dim xDoc As XmlDocument = Nothing
            Dim i As Integer = 1
             While True
                partName = String.Format(partNameTemplate, i)
                 If (packageFile.PartExists(New Uri(partName, UriKind.Relative))) Then
                    packagePartData = packageFile.GetPart(New Uri(partName, UriKind.Relative))
                    xDoc = New XmlDocument()
                     xDoc.Load(packagePartData.GetStream())
                     If (xDoc.DocumentElement.NamespaceURI = "http://www.sample.com/2006/schemas/contact/") Then
                        Exit While
                     End If
                 End If
                 'Pick an arbitrary number of part names to try
                 If i = 1000 Then
                    Throw New InvalidOperationException("Unable to find XML part.")
                 End If
                i += 1
             End While

While the custom XML part located, the code continues. The next step is to serialize the selected contact into an XML representation and store the result in the customXML part. Below code lines include the code that performs this action. Notice the use of the XmlConvert class to make sure that the column names pare propertly escaped for XML.

Dim rootNode As XmlNode = xDoc.DocumentElement
            rootNode.RemoveAll()
            Dim field As SPField
             For Each field In contactItem.Fields
                Dim fieldNode As XmlNode = xDoc.CreateElement("sc", XmlConvert.EncodeName(field.Title), "http://www.sample.com/2006/schemas/contact/")
                 If (contactItem(field.Id) IsNot Nothing) Then
                    Dim fieldValue As XmlNode = xDoc.CreateElement(contactItem(field.Id).ToString())
                      fieldNode.AppendChild(fieldValue)
                  End If
                 rootNode.AppendChild(fieldNode)
             Next
            xDoc.Save(packagePartData.GetStream(FileMode.Create, FileAccess.Write))

In modifying the contents of the custom XML part, we need to make sure that the data store identifier remained unchanged. This is because the GUID is used in the data bindings of the content controls in document.xml. The below code finds the file that corresponds with the found custom XML part and makes sure the GUID is the one defiend earlier.

Dim dataStoreID As String = "{C7189B18-753E-4599-964B-526E4AF29DED}"
            partNameTemplate = "/customXml/itemProps{0}.xml"
            partName = String.Format(partNameTemplate, i)
            packagePartData = packageFile.GetPart(New Uri(partName, UriKind.Relative))
            xDoc = New XmlDocument
            xDoc.Load(packagePartData.GetStream())
            xDoc.DocumentElement.Attributes(0).InnerText = dataStoreID
             xDoc.Save(packagePartData.GetStream(FileMode.Create, FileAccess.Write))

At this point, the memory stream contains the properly constructed document with the selected content data injected  into the custom XML part. The only remaining task is to send this constructed document to the user via the code in below listing, which copies the contents of the memory stream into that of the HTTP response.

Response.ClearContent()
             Response.ClearHeaders()
            Response.AddHeader("content-disposition", "attachment; filename=" & templateFile.Name)
             Me.Response.ContentType = "application/vnd.ms-word.document.12"
            Response.ContentEncoding = System.Text.Encoding.UTF8
            documentStream.Position = 0
            Const size As Integer = 4096
            Dim bytes(4096) As Byte
            Dim numBytes As Integer
            numBytes = documentStream.Read(bytes, 0, size)
             While numBytes > 0
                 Response.OutputStream.Write(bytes, 0, numBytes)
                numBytes = documentStream.Read(bytes, 0, size)
             End While
            'clean up
            Response.Flush()
            documentStream.Close()
            documentStream.Dispose()
            Response.Close()

With the custom application page complete and the document templates updated with their content controls and custom XML parts, redeploy the feature. Place a contact in the Custom Controls list and run the merge by selecting the Build Customer Document action. Choose the BusinessFax content type and generate the merged document.

Hope this article helped you lot to learn about Office Open XML and how to work with it as well as how to create and deploy a custom feature to the SharePoint site.

Download the Source code from here.

By Jatin Prajapati   Popularity  (7916 Views)
Picture
Biography - Jatin Prajapati
I think, most of the people are interested only in answers so no Biography provided... Want know more just write me at jatin.prajapati.er@gmail.com