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.